Merge divergent versions of library

This commit is contained in:
kayos@tcp.direct 2022-05-21 16:17:21 -07:00
parent 6f20fa59ee
commit 0d670b3fc5
Signed by: kayos
GPG Key ID: 4B841471B4BEE979
5 changed files with 97 additions and 48 deletions

@ -36,10 +36,35 @@ func (db *DB) Path() string {
return db.path return db.path
} }
var DefaultBitcaskOptions []bitcask.Option
func SetDefaultBitcaskOptions(bitcaskopts ...bitcask.Option) {
for _, opt := range bitcaskopts {
DefaultBitcaskOptions = append(DefaultBitcaskOptions, opt)
}
}
func WithMaxDatafileSize(size int) bitcask.Option {
return bitcask.WithMaxDatafileSize(size)
}
func WithMaxKeySize(size uint32) bitcask.Option {
return bitcask.WithMaxKeySize(size)
}
func WithMaxValueSize(size uint64) bitcask.Option {
return bitcask.WithMaxValueSize(size)
}
// Init opens a bitcask store at the given path to be referenced by storeName. // Init opens a bitcask store at the given path to be referenced by storeName.
func (db *DB) Init(storeName string, bitcaskopts ...bitcask.Option) error { func (db *DB) Init(storeName string, bitcaskopts ...bitcask.Option) error {
db.mu.Lock() db.mu.Lock()
defer db.mu.Unlock() defer db.mu.Unlock()
if len(DefaultBitcaskOptions) > 0 {
bitcaskopts = append(bitcaskopts, DefaultBitcaskOptions...)
}
if _, ok := db.store[storeName]; ok { if _, ok := db.store[storeName]; ok {
return errStoreExists return errStoreExists
} }
@ -62,10 +87,27 @@ func (db *DB) With(storeName string) Store {
db.mu.RLock() db.mu.RLock()
defer db.mu.RUnlock() defer db.mu.RUnlock()
d, ok := db.store[storeName] d, ok := db.store[storeName]
if !ok { if ok {
return Store{Bitcask: nil} return d
} }
return d return Store{Bitcask: nil}
}
// WithNew calls the given underlying bitcask instance, if it doesn't exist, it creates it.
func (db *DB) WithNew(storeName string) Store {
db.mu.RLock()
defer db.mu.RUnlock()
d, ok := db.store[storeName]
if ok {
return d
}
db.mu.RUnlock()
err := db.Init(storeName)
db.mu.RLock()
if err == nil {
return db.store[storeName]
}
return Store{Bitcask: nil}
} }
// Close is a simple shim for bitcask's Close function. // Close is a simple shim for bitcask's Close function.

@ -7,7 +7,7 @@ import (
"strconv" "strconv"
"testing" "testing"
"git.tcp.direct/kayos/common/entropy" c "git.tcp.direct/kayos/common/entropy"
) )
var needle = "yeet" var needle = "yeet"
@ -40,20 +40,20 @@ func setupTest(storename string, t *testing.T) *DB {
} }
func genJunk(t *testing.T, correct bool) []byte { func genJunk(t *testing.T, correct bool) []byte {
item := entropy.RandStr(5) item := c.RandStr(5)
bar := entropy.RandStr(5) bar := c.RandStr(5)
if correct { if correct {
if entropy.RNG(100) > 50 { if c.RNG(100) > 50 {
item = needle + entropy.RandStr(entropy.RNG(5)) item = needle + c.RandStr(c.RNG(5))
} else { } else {
bar = entropy.RandStr(entropy.RNG(5)) + needle bar = c.RandStr(c.RNG(5)) + needle
} }
} }
f := Foo{ f := Foo{
Bar: bar, Bar: bar,
Yeet: entropy.RNG(50), Yeet: c.RNG(50),
What: map[string]int{entropy.RandStr(5): 2, item: 7}, What: map[string]int{c.RandStr(5): 2, item: 7},
} }
raw, err := json.Marshal(f) raw, err := json.Marshal(f)
@ -96,11 +96,11 @@ func Test_Search(t *testing.T) {
var storename = "test_search" var storename = "test_search"
var db = setupTest(storename, t) var db = setupTest(storename, t)
one := entropy.RNG(100) one := c.RNG(100)
two := entropy.RNG(100) two := c.RNG(100)
three := entropy.RNG(100) three := c.RNG(100)
four := entropy.RNG(100) four := c.RNG(100)
five := entropy.RNG(100) five := c.RNG(100)
addJunk(db, storename, one, two, three, four, five, t, true) addJunk(db, storename, one, two, three, four, five, t, true)
@ -136,7 +136,7 @@ func Test_Search(t *testing.T) {
}) })
t.Run("NoResultsSearch", func(t *testing.T) { t.Run("NoResultsSearch", func(t *testing.T) {
bogus := entropy.RandStr(55) bogus := c.RandStr(55)
t.Logf("executing search for %s", bogus) t.Logf("executing search for %s", bogus)
results, err := db.With(storename).Search(bogus) results, err := db.With(storename).Search(bogus)
@ -154,7 +154,7 @@ func Test_ValueExists(t *testing.T) {
var db = setupTest(storename, t) var db = setupTest(storename, t)
t.Run("ValueExists", func(t *testing.T) { t.Run("ValueExists", func(t *testing.T) {
needles := addJunk(db, storename, entropy.RNG(100), entropy.RNG(100), entropy.RNG(100), entropy.RNG(100), entropy.RNG(100), t, true) needles := addJunk(db, storename, c.RNG(100), c.RNG(100), c.RNG(100), c.RNG(100), c.RNG(100), t, true)
for _, needle := range needles { for _, needle := range needles {
if k, exists := db.With(storename).ValueExists(needle); !exists { if k, exists := db.With(storename).ValueExists(needle); !exists {
@ -167,7 +167,7 @@ func Test_ValueExists(t *testing.T) {
t.Run("ValueShouldNotExist", func(t *testing.T) { t.Run("ValueShouldNotExist", func(t *testing.T) {
for n := 0; n != 5; n++ { for n := 0; n != 5; n++ {
garbage := entropy.RandStr(55) garbage := c.RandStr(55)
if _, exists := db.With(storename).ValueExists([]byte(garbage)); exists { if _, exists := db.With(storename).ValueExists([]byte(garbage)); exists {
t.Errorf("[FAIL] store should have not contained value %v, but it did", []byte(garbage)) t.Errorf("[FAIL] store should have not contained value %v, but it did", []byte(garbage))
} else { } else {
@ -192,28 +192,28 @@ func Test_PrefixScan(t *testing.T) {
var storename = "test_prefix_scan" var storename = "test_prefix_scan"
var db = setupTest(storename, t) var db = setupTest(storename, t)
addJunk(db, storename, entropy.RNG(5), entropy.RNG(5), entropy.RNG(5), entropy.RNG(5), entropy.RNG(5), t, false) addJunk(db, storename, c.RNG(5), c.RNG(5), c.RNG(5), c.RNG(5), c.RNG(5), t, false)
var needles = []KeyValue{ var needles = []KeyValue{
{ {
Key: Key{b: []byte("user:Fuckhole")}, Key: Key{b: []byte("user:Fuckhole")},
Value: Value{b: []byte(entropy.RandStr(55))}, Value: Value{b: []byte(c.RandStr(55))},
}, },
{ {
Key: Key{b: []byte("user:Johnson")}, Key: Key{b: []byte("user:Johnson")},
Value: Value{b: []byte(entropy.RandStr(55))}, Value: Value{b: []byte(c.RandStr(55))},
}, },
{ {
Key: Key{b: []byte("user:Jackson")}, Key: Key{b: []byte("user:Jackson")},
Value: Value{b: []byte(entropy.RandStr(55))}, Value: Value{b: []byte(c.RandStr(55))},
}, },
{ {
Key: Key{b: []byte("user:Frackhole")}, Key: Key{b: []byte("user:Frackhole")},
Value: Value{b: []byte(entropy.RandStr(55))}, Value: Value{b: []byte(c.RandStr(55))},
}, },
{ {
Key: Key{b: []byte("user:Baboshka")}, Key: Key{b: []byte("user:Baboshka")},
Value: Value{b: []byte(entropy.RandStr(55))}, Value: Value{b: []byte(c.RandStr(55))},
}, },
} }

@ -1,10 +1,11 @@
package bitcask package bitcask
import ( import (
"bytes"
"os" "os"
"testing" "testing"
"git.tcp.direct/kayos/common/entropy" c "git.tcp.direct/kayos/common/entropy"
) )
func newTestDB(t *testing.T) *DB { func newTestDB(t *testing.T) *DB {
@ -17,12 +18,12 @@ func newTestDB(t *testing.T) *DB {
} }
func seedRandKV(db *DB, store string) error { func seedRandKV(db *DB, store string) error {
return db.With(store).Put([]byte(entropy.RandStr(55)), []byte(entropy.RandStr(55))) return db.With(store).Put([]byte(c.RandStr(55)), []byte(c.RandStr(55)))
} }
func seedRandStores(db *DB, t *testing.T) { func seedRandStores(db *DB, t *testing.T) {
for n := 0; n != 5; n++ { for n := 0; n != 5; n++ {
randstore := entropy.RandStr(5) randstore := c.RandStr(5)
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)
@ -91,16 +92,24 @@ func TestDB_Init(t *testing.T) {
if gerr != nil { if gerr != nil {
t.Fatalf("[FAIL] %e", gerr) t.Fatalf("[FAIL] %e", gerr)
} }
if string(gvalue) != string(value) { if !bytes.Equal(gvalue, value) {
t.Errorf("[FAIL] wanted %v, got %v", string(value), string(gvalue)) t.Errorf("[FAIL] wanted %v, got %v", string(value), string(gvalue))
} }
t.Logf("Got Value %v at Key %v", string(gvalue), key) t.Logf("Got Value %v at Key %v", string(gvalue), key)
}) })
t.Run("withStoreDoesntExist", func(t *testing.T) { t.Run("withNewStoreDoesntExist", func(t *testing.T) {
if nope := db.With("asdfqwerty"); nope.Bitcask != nil { if nope := db.WithNew("asdfqwerty"); nope.Bitcask == nil {
t.Errorf("[FAIL] got non nil result for nonexistent store: %T, %v", nope, nope) t.Fatalf("[FAIL] got nil result for nonexistent store when it should have made itself: %T, %v", nope, nope)
} else {
t.Logf("[SUCCESS] got nil Value for store that doesn't exist")
}
})
t.Run("withStoreDoesntExist", func(t *testing.T) {
if nope := db.With("afsafdassdfqwerty"); nope.Bitcask != nil {
t.Fatalf("[FAIL] got non nil result for nonexistent store: %T, %v", nope, nope)
} else {
t.Logf("[SUCCESS] got nil Value for store that doesn't exist")
} }
t.Logf("[SUCCESS] got nil Value for store that doesn't exist")
}) })
t.Run("syncAllShouldFail", func(t *testing.T) { t.Run("syncAllShouldFail", func(t *testing.T) {
db.store["wtf"] = Store{} db.store["wtf"] = Store{}
@ -168,6 +177,9 @@ 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() {
db = nil
}()
seedRandStores(db, t) seedRandStores(db, t)
var oldstores []string var oldstores []string
t.Run("Close", func(t *testing.T) { t.Run("Close", func(t *testing.T) {
@ -175,7 +187,7 @@ func Test_Close(t *testing.T) {
oldstores = append(oldstores, d) oldstores = append(oldstores, d)
err := db.Close(d) err := db.Close(d)
if err != nil { if err != nil {
t.Errorf("[FAIL] failed to close %s: %e", d, err) t.Fatalf("[FAIL] failed to close %s: %e", d, err)
} else { } else {
t.Logf("[+] Close() successful for %s", d) t.Logf("[+] Close() successful for %s", d)
} }
@ -184,14 +196,14 @@ func Test_Close(t *testing.T) {
t.Run("AssureClosed", func(t *testing.T) { t.Run("AssureClosed", func(t *testing.T) {
for _, d := range oldstores { for _, d := range oldstores {
if st := db.With(d); st.Bitcask != nil { if st := db.With(d); st.Bitcask != nil {
t.Errorf("[FAIL] store %s should have been deleted", d) t.Fatalf("[FAIL] store %s should have been deleted", d)
} }
} }
t.Logf("[SUCCESS] Confirmed that all stores have been closed") t.Logf("[SUCCESS] Confirmed that all stores have been closed")
}) })
t.Run("CantCloseBogusStore", func(t *testing.T) { t.Run("CantCloseBogusStore", func(t *testing.T) {
err := db.Close(entropy.RandStr(55)) err := db.Close(c.RandStr(55))
if err != errBogusStore { if err != errBogusStore {
t.Errorf("[FAIL] got err %e, wanted err %e", err, errBogusStore) t.Errorf("[FAIL] got err %e, wanted err %e", err, errBogusStore)
} }

@ -1,8 +1,7 @@
package bitcask package bitcask
import ( import (
"git.tcp.direct/kayos/common/hash" "bytes"
"git.tcp.direct/tcp.direct/database" "git.tcp.direct/tcp.direct/database"
) )
@ -30,7 +29,7 @@ func (k Key) String() string {
// Equal determines if two keys are equal. // Equal determines if two keys are equal.
func (k Key) Equal(k2 Key) bool { func (k Key) Equal(k2 Key) bool {
return hash.BlakeEqual(k.Bytes(), k2.Bytes()) return bytes.Equal(k.Bytes(), k2.Bytes())
} }
// Value represents a value in a key/value store. // Value represents a value in a key/value store.
@ -51,5 +50,5 @@ func (v Value) String() string {
// Equal determines if two values are equal. // Equal determines if two values are equal.
func (v Value) Equal(v2 Value) bool { func (v Value) Equal(v2 Value) bool {
return hash.BlakeEqual(v.Bytes(), v2.Bytes()) return bytes.Equal(v.Bytes(), v2.Bytes())
} }

@ -3,12 +3,12 @@ package bitcask
import ( import (
"testing" "testing"
"git.tcp.direct/kayos/common/entropy" c "git.tcp.direct/kayos/common/entropy"
) )
func Test_Equal(t *testing.T) { func Test_Equal(t *testing.T) {
t.Run("ShouldBeEqual", func(t *testing.T) { t.Run("ShouldBeEqual", func(t *testing.T) {
v := entropy.RandStr(55) v := c.RandStr(55)
kv1 := KeyValue{Key{b: []byte(v)}, Value{b: []byte(v)}} kv1 := KeyValue{Key{b: []byte(v)}, Value{b: []byte(v)}}
kv2 := KeyValue{Key{b: []byte(v)}, Value{b: []byte(v)}} kv2 := KeyValue{Key{b: []byte(v)}, Value{b: []byte(v)}}
if !kv1.Key.Equal(kv2.Key) { if !kv1.Key.Equal(kv2.Key) {
@ -35,13 +35,11 @@ func Test_Equal(t *testing.T) {
kv1.Value.String(), kv2.Value.String(), kv1.Value.String(), kv2.Value.String(),
) )
} }
} }
}) })
t.Run("ShouldNotBeEqual", func(t *testing.T) { t.Run("ShouldNotBeEqual", func(t *testing.T) {
v1 := entropy.RandStr(55) v1 := c.RandStr(55)
v2 := entropy.RandStr(55) v2 := c.RandStr(55)
kv1 := KeyValue{Key{b: []byte(v1)}, Value{b: []byte(v1)}} kv1 := KeyValue{Key{b: []byte(v1)}, Value{b: []byte(v1)}}
kv2 := KeyValue{Key{b: []byte(v2)}, Value{b: []byte(v2)}} kv2 := KeyValue{Key{b: []byte(v2)}, Value{b: []byte(v2)}}
if kv1.Key.Equal(kv2.Key) { if kv1.Key.Equal(kv2.Key) {
@ -68,8 +66,6 @@ func Test_Equal(t *testing.T) {
kv1.Value.String(), kv2.Value.String(), kv1.Value.String(), kv2.Value.String(),
) )
} }
} }
}) })
} }