commit 30ddf1494884233ebe36d112a3ec9a2034d57db0
parent 326bdb5018ae81ee493371b56bd4564a8b35da59
Author: lash <dev@holbrook.no>
Date: Fri, 30 Aug 2024 19:02:42 +0100
Implement db in persister
Diffstat:
M | db/db.go | | | 8 | ++++++-- |
A | db/log.go | | | 9 | +++++++++ |
M | db/mem.go | | | 19 | ++++++++++++++++--- |
D | persist/fs.go | | | 91 | ------------------------------------------------------------------------------- |
M | persist/fs_test.go | | | 94 | ++++++++++++++++++++++++++++++++++++++++++++----------------------------------- |
M | persist/persist.go | | | 89 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- |
6 files changed, 163 insertions(+), 147 deletions(-)
diff --git a/db/db.go b/db/db.go
@@ -25,6 +25,10 @@ type Db interface {
Get(ctx context.Context, key []byte) ([]byte, error)
// Put stores a value under a key. Any existing value will be replaced. Errors if the value could not be stored.
Put(ctx context.Context, key []byte, val []byte) error
+ // SetPrefix sets the storage context prefix to use for consecutive Get and Put operations.
+ SetPrefix(pfx uint8)
+ // SetSession sets the session context to use for consecutive Get and Put operations.
+ SetSession(sessionId string)
}
// ToDbKey generates a key to use Db to store a value for a particular context.
@@ -47,12 +51,12 @@ type BaseDb struct {
sid []byte
}
-// SetPrefix sets the storage context prefix to use for consecutive Get and Put operations.
+// SetPrefix implements Db.
func(db *BaseDb) SetPrefix(pfx uint8) {
db.pfx = pfx
}
-// SetSession sets the session context to use for consecutive Get and Put operations.
+// SetSession implements Db.
func(db *BaseDb) SetSession(sessionId string) {
db.sid = append([]byte(sessionId), 0x2E)
}
diff --git a/db/log.go b/db/log.go
@@ -0,0 +1,9 @@
+package db
+
+import (
+ "git.defalsify.org/vise.git/logging"
+)
+
+var (
+ Logg logging.Logger = logging.NewVanilla().WithDomain("db")
+)
diff --git a/db/mem.go b/db/mem.go
@@ -2,7 +2,7 @@ package db
import (
"context"
- "fmt"
+ "encoding/hex"
)
// MemDb is a memory backend implementation of the Db interface.
@@ -11,8 +11,18 @@ type MemDb struct {
store map[string][]byte
}
+// NewMemDb returns an already allocated
+func NewMemDb(ctx context.Context) *MemDb {
+ db := &MemDb{}
+ _ = db.Connect(ctx, "")
+ return db
+}
+
// Connect implements Db
func(mdb *MemDb) Connect(ctx context.Context, connStr string) error {
+ if mdb.store != nil {
+ return nil
+ }
mdb.store = make(map[string][]byte)
return nil
}
@@ -20,7 +30,7 @@ func(mdb *MemDb) Connect(ctx context.Context, connStr string) error {
// convert to a supported map key type
func(mdb *MemDb) toHexKey(key []byte) (string, error) {
k, err := mdb.ToKey(key)
- return fmt.Sprintf("%x", k), err
+ return hex.EncodeToString(k), err
}
// Get implements Db
@@ -29,9 +39,11 @@ func(mdb *MemDb) Get(ctx context.Context, key []byte) ([]byte, error) {
if err != nil {
return nil, err
}
+ Logg.TraceCtxf(ctx, "mem get", "k", k)
v, ok := mdb.store[k]
if !ok {
- return nil, NewErrNotFound([]byte(k))
+ b, _ := hex.DecodeString(k)
+ return nil, NewErrNotFound(b)
}
return v, nil
}
@@ -43,6 +55,7 @@ func(mdb *MemDb) Put(ctx context.Context, key []byte, val []byte) error {
return err
}
mdb.store[k] = val
+ Logg.TraceCtxf(ctx, "mem put", "k", k, "v", val)
return nil
}
diff --git a/persist/fs.go b/persist/fs.go
@@ -1,91 +0,0 @@
-package persist
-
-import (
- "io/ioutil"
- "path"
- "path/filepath"
- "github.com/fxamacker/cbor/v2"
-
- "git.defalsify.org/vise.git/cache"
- "git.defalsify.org/vise.git/state"
-)
-
-// FsPersister is an implementation of Persister that saves state to the file system.
-type FsPersister struct {
- State *state.State
- Memory *cache.Cache
- dir string
-}
-
-// NewFsPersister creates a new FsPersister.
-//
-// The filesystem store will be at the given directory. The directory must exist.
-func NewFsPersister(dir string) *FsPersister {
- fp, err := filepath.Abs(dir)
- if err != nil {
- panic(err)
- }
- return &FsPersister{
- dir: fp,
- }
-}
-
-// WithContent sets a current State and Cache object.
-//
-// This method is normally called before Serialize / Save.
-func(p *FsPersister) WithContent(st *state.State, ca *cache.Cache) *FsPersister {
- p.State = st
- p.Memory = ca
- return p
-}
-
-// GetState implements the Persister interface.
-func(p *FsPersister) GetState() *state.State {
- return p.State
-}
-
-// GetMemory implements the Persister interface.
-func(p *FsPersister) GetMemory() cache.Memory {
- return p.Memory
-}
-
-// Serialize implements the Persister interface.
-func(p *FsPersister) Serialize() ([]byte, error) {
- return cbor.Marshal(p)
-}
-
-// Deserialize implements the Persister interface.
-func(p *FsPersister) Deserialize(b []byte) error {
- err := cbor.Unmarshal(b, p)
- return err
-}
-
-// Save implements the Persister interface.
-func(p *FsPersister) Save(key string) error {
- b, err := p.Serialize()
- if err != nil {
- return err
- }
- fp := path.Join(p.dir, key)
- err = ioutil.WriteFile(fp, b, 0600)
- if err != nil {
- return err
- }
- Logg.Debugf("saved state and cache", "key", key, "bytecode", p.State.Code, "flags", p.State.Flags)
- return nil
-}
-
-// Load implements the Persister interface.
-func(p *FsPersister) Load(key string) error {
- fp := path.Join(p.dir, key)
- b, err := ioutil.ReadFile(fp)
- if err != nil {
- return err
- }
- err = p.Deserialize(b)
- if err != nil {
- return err
- }
- Logg.Debugf("loaded state and cache", "key", key, "bytecode", p.State.Code)
- return nil
-}
diff --git a/persist/fs_test.go b/persist/fs_test.go
@@ -2,13 +2,14 @@ package persist
import (
"bytes"
- "io/ioutil"
+ "context"
"reflect"
"testing"
"git.defalsify.org/vise.git/cache"
"git.defalsify.org/vise.git/state"
"git.defalsify.org/vise.git/vm"
+ "git.defalsify.org/vise.git/db"
)
func TestSerializeState(t *testing.T) {
@@ -27,31 +28,38 @@ func TestSerializeState(t *testing.T) {
ca.Add("inky", "pinky", 13)
ca.Add("blinky", "clyde", 42)
- pr := NewFsPersister(".").WithContent(&st, ca)
+ ctx := context.Background()
+ store := db.NewMemDb(ctx)
+ pr := NewPersister(store).WithContext(context.Background()).WithSession("xyzzy").WithContent(&st, ca)
v, err := pr.Serialize()
if err != nil {
t.Error(err)
}
- prnew := NewFsPersister(".")
+ prnew := NewPersister(store).WithSession("xyzzy")
err = prnew.Deserialize(v)
if err != nil {
t.Fatal(err)
- }
- if !reflect.DeepEqual(prnew.State.ExecPath, pr.State.ExecPath) {
- t.Fatalf("expected %s, got %s", prnew.State.ExecPath, pr.State.ExecPath)
}
- if !bytes.Equal(prnew.State.Code, pr.State.Code) {
- t.Fatalf("expected %x, got %x", prnew.State.Code, pr.State.Code)
+ stNew := prnew.GetState()
+ stOld := pr.GetState()
+ caNew := prnew.GetMemory()
+ caOld := pr.GetMemory()
+
+ if !reflect.DeepEqual(stNew.ExecPath, stOld.ExecPath) {
+ t.Fatalf("expected %s, got %s", stNew.ExecPath, stOld.ExecPath)
+ }
+ if !bytes.Equal(stNew.Code, stOld.Code) {
+ t.Fatalf("expected %x, got %x", stNew.Code, stOld.Code)
}
- if prnew.State.BitSize != pr.State.BitSize {
- t.Fatalf("expected %v, got %v", prnew.State.BitSize, pr.State.BitSize)
+ if stNew.BitSize != stOld.BitSize {
+ t.Fatalf("expected %v, got %v", stNew.BitSize, stOld.BitSize)
}
- if prnew.State.SizeIdx != pr.State.SizeIdx {
- t.Fatalf("expected %v, got %v", prnew.State.SizeIdx, pr.State.SizeIdx)
+ if stNew.SizeIdx != stOld.SizeIdx {
+ t.Fatalf("expected %v, got %v", stNew.SizeIdx, stOld.SizeIdx)
}
- if !reflect.DeepEqual(prnew.Memory, pr.Memory) {
- t.Fatalf("expected %v, got %v", prnew.Memory, pr.Memory)
+ if !reflect.DeepEqual(caNew, caOld) {
+ t.Fatalf("expected %v, got %v", caNew, caOld)
}
}
@@ -71,35 +79,38 @@ func TestSaveLoad(t *testing.T) {
ca.Add("inky", "pinky", 13)
ca.Add("blinky", "clyde", 42)
- dir, err := ioutil.TempDir("", "vise_persist")
- if err != nil {
- t.Error(err)
- }
- pr := NewFsPersister(dir).WithContent(&st, ca)
- err = pr.Save("xyzzy")
+ ctx := context.Background()
+ store := db.NewMemDb(ctx)
+ pr := NewPersister(store).WithContent(&st, ca)
+ err := pr.Save("xyzzy")
if err != nil {
- t.Error(err)
+ t.Fatal(err)
}
- prnew := NewFsPersister(dir)
+ prnew := NewPersister(store)
err = prnew.Load("xyzzy")
if err != nil {
- t.Error(err)
+ t.Fatal(err)
}
- if !reflect.DeepEqual(prnew.State.ExecPath, pr.State.ExecPath) {
- t.Fatalf("expected %s, got %s", prnew.State.ExecPath, pr.State.ExecPath)
+ stNew := prnew.GetState()
+ stOld := pr.GetState()
+ caNew := prnew.GetMemory()
+ caOld := pr.GetMemory()
+
+ if !reflect.DeepEqual(stNew.ExecPath, stOld.ExecPath) {
+ t.Fatalf("expected %s, got %s", stNew.ExecPath, stOld.ExecPath)
}
- if !bytes.Equal(prnew.State.Code, pr.State.Code) {
- t.Fatalf("expected %x, got %x", prnew.State.Code, pr.State.Code)
+ if !bytes.Equal(stNew.Code, stOld.Code) {
+ t.Fatalf("expected %x, got %x", stNew.Code, stOld.Code)
}
- if prnew.State.BitSize != pr.State.BitSize {
- t.Fatalf("expected %v, got %v", prnew.State.BitSize, pr.State.BitSize)
+ if stNew.BitSize != stOld.BitSize {
+ t.Fatalf("expected %v, got %v", stNew.BitSize, stOld.BitSize)
}
- if prnew.State.SizeIdx != pr.State.SizeIdx {
- t.Fatalf("expected %v, got %v", prnew.State.SizeIdx, pr.State.SizeIdx)
+ if stNew.SizeIdx != stOld.SizeIdx {
+ t.Fatalf("expected %v, got %v", stNew.SizeIdx, stOld.SizeIdx)
}
- if !reflect.DeepEqual(prnew.Memory, pr.Memory) {
- t.Fatalf("expected %v, got %v", prnew.Memory, pr.Memory)
+ if !reflect.DeepEqual(caNew, caOld) {
+ t.Fatalf("expected %v, got %v", caNew, caOld)
}
}
@@ -108,20 +119,19 @@ func TestSaveLoadFlags(t *testing.T) {
st.SetFlag(8)
ca := cache.NewCache()
- dir, err := ioutil.TempDir("", "vise_persist")
- if err != nil {
- t.Error(err)
- }
- pr := NewFsPersister(dir).WithContent(&st, ca)
- err = pr.Save("xyzzy")
+ ctx := context.Background()
+ store := db.NewMemDb(ctx)
+ pr := NewPersister(store).WithContent(&st, ca)
+ err := pr.Save("xyzzy")
if err != nil {
- t.Error(err)
+ t.Fatal(err)
}
- prnew := NewFsPersister(dir)
+ prnew := NewPersister(store)
+
err = prnew.Load("xyzzy")
if err != nil {
- t.Error(err)
+ t.Fatal(err)
}
stnew := prnew.GetState()
if !stnew.GetFlag(8) {
diff --git a/persist/persist.go b/persist/persist.go
@@ -1,17 +1,88 @@
package persist
import (
- "git.defalsify.org/vise.git/cache"
+ "context"
+
+ "github.com/fxamacker/cbor/v2"
+
+ "git.defalsify.org/vise.git/db"
"git.defalsify.org/vise.git/state"
+ "git.defalsify.org/vise.git/cache"
)
-// Persister interface defines the methods needed for a component that can store the execution state to a storage location.
-type Persister interface {
- Serialize() ([]byte, error) // Output serializes representation of the state.
- Deserialize(b []byte) error // Restore state from a serialized state.
- Save(key string) error // Serialize and commit the state representation to persisted storage.
- Load(key string) error // Load the state representation from persisted storage and Deserialize.
- GetState() *state.State // Get the currently loaded State object.
- GetMemory() cache.Memory // Get the currently loaded Cache object.
+type Persister struct {
+ State *state.State
+ Memory *cache.Cache
+ ctx context.Context
+ db db.Db
+}
+
+func NewPersister(db db.Db) *Persister {
+ return &Persister{
+ db: db,
+ ctx: context.Background(),
+ }
+}
+
+func(p *Persister) WithContext(ctx context.Context) *Persister {
+ p.ctx = ctx
+ return p
+}
+
+func(p *Persister) WithSession(sessionId string) *Persister {
+ p.db.SetSession(sessionId)
+ return p
+}
+
+// WithContent sets a current State and Cache object.
+//
+// This method is normally called before Serialize / Save.
+func(p *Persister) WithContent(st *state.State, ca *cache.Cache) *Persister {
+ p.State = st
+ p.Memory = ca
+ return p
}
+// GetState implements the Persister interface.
+func(p *Persister) GetState() *state.State {
+ return p.State
+}
+
+// GetMemory implements the Persister interface.
+func(p *Persister) GetMemory() cache.Memory {
+ return p.Memory
+}
+
+// Serialize implements the Persister interface.
+func(p *Persister) Serialize() ([]byte, error) {
+ return cbor.Marshal(p)
+}
+
+// Deserialize implements the Persister interface.
+func(p *Persister) Deserialize(b []byte) error {
+ err := cbor.Unmarshal(b, p)
+ return err
+}
+
+func(p *Persister) Save(key string) error {
+ b, err := p.Serialize()
+ if err != nil {
+ return err
+ }
+ p.db.SetPrefix(db.DATATYPE_STATE)
+ return p.db.Put(p.ctx, []byte(key), b)
+}
+
+func(p *Persister) Load(key string) error {
+ p.db.SetPrefix(db.DATATYPE_STATE)
+ b, err := p.db.Get(p.ctx, []byte(key))
+ if err != nil {
+ return err
+ }
+ err = p.Deserialize(b)
+ if err != nil {
+ return err
+ }
+ Logg.Debugf("loaded state and cache", "key", key, "bytecode", p.State.Code)
+ return nil
+}