Feat: DB.Discover()
This commit is contained in:
parent
509f844f49
commit
aed641de42
@ -4,6 +4,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@ -51,6 +53,43 @@ func OpenDB(path string) *DB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Discover will discover and initialize all existing bitcask stores at the path opened by [OpenDB].
|
||||||
|
func (db *DB) Discover() ([]string, error) {
|
||||||
|
db.mu.Lock()
|
||||||
|
defer db.mu.Unlock()
|
||||||
|
stores := make([]string, 0)
|
||||||
|
errs := make([]error, 0)
|
||||||
|
if db.store == nil {
|
||||||
|
db.store = make(map[string]Store)
|
||||||
|
}
|
||||||
|
entries, err := fs.ReadDir(os.DirFS(db.path), ".")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, entry := range entries {
|
||||||
|
if !entry.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name := entry.Name()
|
||||||
|
if _, ok := db.store[name]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c, e := bitcask.Open(filepath.Join(db.path, name), defaultBitcaskOptions...)
|
||||||
|
if e != nil {
|
||||||
|
errs = append(errs, e)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
db.store[name] = Store{Bitcask: c}
|
||||||
|
stores = append(stores, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range errs {
|
||||||
|
err = fmt.Errorf("%w: %w", err, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
return stores, err
|
||||||
|
}
|
||||||
|
|
||||||
// Path returns the base path where we store our bitcask "stores".
|
// Path returns the base path where we store our bitcask "stores".
|
||||||
func (db *DB) Path() string {
|
func (db *DB) Path() string {
|
||||||
return db.path
|
return db.path
|
||||||
|
@ -14,24 +14,26 @@ import (
|
|||||||
"git.tcp.direct/tcp.direct/database"
|
"git.tcp.direct/tcp.direct/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newTestDB(t *testing.T) database.Keeper {
|
func newTestDB(t *testing.T) (string, database.Keeper) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
tpath := t.TempDir()
|
tpath := t.TempDir()
|
||||||
tdb := OpenDB(tpath)
|
tdb := OpenDB(tpath)
|
||||||
if tdb == nil {
|
if tdb == nil {
|
||||||
t.Fatalf("failed to open testdb at %s, got nil", tpath)
|
t.Fatalf("failed to open testdb at %s, got nil", tpath)
|
||||||
}
|
}
|
||||||
return tdb
|
return tpath, tdb
|
||||||
}
|
}
|
||||||
|
|
||||||
func seedRandKV(db database.Keeper, store string) error {
|
func seedRandKV(db database.Keeper, store string) error {
|
||||||
return db.With(store).Put([]byte(c.RandStr(55)), []byte(c.RandStr(55)))
|
return db.With(store).Put([]byte(c.RandStr(55)), []byte(c.RandStr(55)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func seedRandStores(db database.Keeper, t *testing.T) {
|
func seedRandStores(db database.Keeper, t *testing.T) []string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
names := make([]string, 0)
|
||||||
for n := 0; n != 5; n++ {
|
for n := 0; n != 5; n++ {
|
||||||
randstore := c.RandStr(5)
|
randstore := c.RandStr(5)
|
||||||
|
names = append(names, randstore)
|
||||||
err := db.Init(randstore)
|
err := db.Init(randstore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to initialize store for test SyncAndCloseAll: %e", err)
|
t.Errorf("failed to initialize store for test SyncAndCloseAll: %e", err)
|
||||||
@ -42,10 +44,11 @@ func seedRandStores(db database.Keeper, t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.Logf("seeded random stores with random values for test %s", t.Name())
|
t.Logf("seeded random stores with random values for test %s", t.Name())
|
||||||
|
return names
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDB_Init(t *testing.T) { //nolint:funlen,gocognit,cyclop
|
func TestDB_Init(t *testing.T) { //nolint:funlen,gocognit,cyclop
|
||||||
var db = newTestDB(t)
|
var _, db = newTestDB(t)
|
||||||
type args struct{ storeName string }
|
type args struct{ storeName string }
|
||||||
type test struct {
|
type test struct {
|
||||||
name string
|
name string
|
||||||
@ -170,18 +173,36 @@ func TestDB_Init(t *testing.T) { //nolint:funlen,gocognit,cyclop
|
|||||||
db = nil
|
db = nil
|
||||||
})
|
})
|
||||||
t.Run("SyncAndCloseAll", func(t *testing.T) {
|
t.Run("SyncAndCloseAll", func(t *testing.T) {
|
||||||
db = newTestDB(t)
|
var path string
|
||||||
seedRandStores(db, t)
|
path, db = newTestDB(t)
|
||||||
|
names := seedRandStores(db, t)
|
||||||
err := db.SyncAndCloseAll()
|
err := db.SyncAndCloseAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("[FAIL] failed to SyncAndCloseAll: %e", err)
|
t.Errorf("[FAIL] failed to SyncAndCloseAll: %e", err)
|
||||||
}
|
}
|
||||||
|
db = OpenDB(path)
|
||||||
|
found, err := db.(*DB).Discover()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("[FAIL] failed to discover stores: %e", err)
|
||||||
|
}
|
||||||
|
for _, n := range names {
|
||||||
|
matched := false
|
||||||
|
for _, f := range found {
|
||||||
|
if f == n {
|
||||||
|
matched = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !matched {
|
||||||
|
t.Errorf("[FAIL] failed to find store %s", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_Sync(t *testing.T) {
|
func Test_Sync(t *testing.T) {
|
||||||
// TODO: make sure sync is ACTUALLY sycing instead of only checking for nil err...
|
// TODO: make sure sync is ACTUALLY sycing instead of only checking for nil err...
|
||||||
var db = newTestDB(t)
|
var _, db = newTestDB(t)
|
||||||
seedRandStores(db, t)
|
seedRandStores(db, t)
|
||||||
t.Run("Sync", func(t *testing.T) {
|
t.Run("Sync", func(t *testing.T) {
|
||||||
for d := range db.(*DB).store {
|
for d := range db.(*DB).store {
|
||||||
@ -196,7 +217,7 @@ func Test_Sync(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_Close(t *testing.T) {
|
func Test_Close(t *testing.T) {
|
||||||
var db = newTestDB(t)
|
var _, db = newTestDB(t)
|
||||||
defer func() {
|
defer func() {
|
||||||
db = nil
|
db = nil
|
||||||
}()
|
}()
|
||||||
@ -231,7 +252,7 @@ func Test_Close(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_withAll(t *testing.T) {
|
func Test_withAll(t *testing.T) {
|
||||||
var db = newTestDB(t)
|
var _, db = newTestDB(t)
|
||||||
asdf1 := c.RandStr(10)
|
asdf1 := c.RandStr(10)
|
||||||
asdf2 := c.RandStr(10)
|
asdf2 := c.RandStr(10)
|
||||||
|
|
||||||
@ -247,7 +268,7 @@ func Test_withAll(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
t.Run("withAllNilMap", func(t *testing.T) {
|
t.Run("withAllNilMap", func(t *testing.T) {
|
||||||
nilDb := newTestDB(t)
|
_, nilDb := newTestDB(t)
|
||||||
nilDb.(*DB).store = nil
|
nilDb.(*DB).store = nil
|
||||||
err := nilDb.(*DB).withAll(dclose)
|
err := nilDb.(*DB).withAll(dclose)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -473,7 +494,7 @@ func Test_WithOptions(t *testing.T) { //nolint:funlen,gocognit,cyclop
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
t.Run("InitWithBogusOption", func(t *testing.T) {
|
t.Run("InitWithBogusOption", func(t *testing.T) {
|
||||||
db := newTestDB(t)
|
_, db := newTestDB(t)
|
||||||
err := db.Init("bogus", "yeet")
|
err := db.Init("bogus", "yeet")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("[FAIL] Init should have failed with bogus option")
|
t.Errorf("[FAIL] Init should have failed with bogus option")
|
||||||
|
@ -13,6 +13,8 @@ type Keeper interface {
|
|||||||
// WithNew should initialize a new Filer at the given path.
|
// WithNew should initialize a new Filer at the given path.
|
||||||
WithNew(name string, options ...any) Filer
|
WithNew(name string, options ...any) Filer
|
||||||
|
|
||||||
|
Discover() ([]string, error)
|
||||||
|
|
||||||
AllStores() map[string]Filer
|
AllStores() map[string]Filer
|
||||||
// TODO: Backups
|
// TODO: Backups
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user