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
+}