Feat: DB.Discover()

This commit is contained in:
kayos@tcp.direct 2024-01-28 20:36:59 -08:00
parent 509f844f49
commit aed641de42
Signed by: kayos
GPG Key ID: 4B841471B4BEE979
3 changed files with 73 additions and 11 deletions

View File

@ -4,6 +4,8 @@ import (
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"
"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".
func (db *DB) Path() string {
return db.path

View File

@ -14,24 +14,26 @@ import (
"git.tcp.direct/tcp.direct/database"
)
func newTestDB(t *testing.T) database.Keeper {
func newTestDB(t *testing.T) (string, database.Keeper) {
t.Helper()
tpath := t.TempDir()
tdb := OpenDB(tpath)
if tdb == nil {
t.Fatalf("failed to open testdb at %s, got nil", tpath)
}
return tdb
return tpath, tdb
}
func seedRandKV(db database.Keeper, store string) error {
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()
names := make([]string, 0)
for n := 0; n != 5; n++ {
randstore := c.RandStr(5)
names = append(names, randstore)
err := db.Init(randstore)
if err != nil {
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())
return names
}
func TestDB_Init(t *testing.T) { //nolint:funlen,gocognit,cyclop
var db = newTestDB(t)
var _, db = newTestDB(t)
type args struct{ storeName string }
type test struct {
name string
@ -170,18 +173,36 @@ func TestDB_Init(t *testing.T) { //nolint:funlen,gocognit,cyclop
db = nil
})
t.Run("SyncAndCloseAll", func(t *testing.T) {
db = newTestDB(t)
seedRandStores(db, t)
var path string
path, db = newTestDB(t)
names := seedRandStores(db, t)
err := db.SyncAndCloseAll()
if err != nil {
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) {
// 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)
t.Run("Sync", func(t *testing.T) {
for d := range db.(*DB).store {
@ -196,7 +217,7 @@ func Test_Sync(t *testing.T) {
}
func Test_Close(t *testing.T) {
var db = newTestDB(t)
var _, db = newTestDB(t)
defer func() {
db = nil
}()
@ -231,7 +252,7 @@ func Test_Close(t *testing.T) {
}
func Test_withAll(t *testing.T) {
var db = newTestDB(t)
var _, db = newTestDB(t)
asdf1 := c.RandStr(10)
asdf2 := c.RandStr(10)
@ -247,7 +268,7 @@ func Test_withAll(t *testing.T) {
}
})
t.Run("withAllNilMap", func(t *testing.T) {
nilDb := newTestDB(t)
_, nilDb := newTestDB(t)
nilDb.(*DB).store = nil
err := nilDb.(*DB).withAll(dclose)
if err == nil {
@ -473,7 +494,7 @@ func Test_WithOptions(t *testing.T) { //nolint:funlen,gocognit,cyclop
}
})
t.Run("InitWithBogusOption", func(t *testing.T) {
db := newTestDB(t)
_, db := newTestDB(t)
err := db.Init("bogus", "yeet")
if err == nil {
t.Errorf("[FAIL] Init should have failed with bogus option")

View File

@ -13,6 +13,8 @@ type Keeper interface {
// WithNew should initialize a new Filer at the given path.
WithNew(name string, options ...any) Filer
Discover() ([]string, error)
AllStores() map[string]Filer
// TODO: Backups