persist.go (3474B)
1 package persist 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/fxamacker/cbor/v2" 8 9 "git.defalsify.org/vise.git/db" 10 "git.defalsify.org/vise.git/state" 11 "git.defalsify.org/vise.git/cache" 12 ) 13 14 // Persister abstracts storage and retrieval of state and cache. 15 type Persister struct { 16 State *state.State 17 Memory *cache.Cache 18 ctx context.Context 19 db db.Db 20 flush bool 21 } 22 23 // NewPersister creates a new Persister instance. 24 func NewPersister(db db.Db) *Persister { 25 return &Persister{ 26 db: db, 27 ctx: context.Background(), 28 } 29 } 30 31 // WithSession is a chainable function that sets the current golang context of the persister. 32 func(p *Persister) WithContext(ctx context.Context) *Persister { 33 p.ctx = ctx 34 return p 35 } 36 37 // WithSession is a chainable function that sets the current session context of the persister. 38 func(p *Persister) WithSession(sessionId string) *Persister { 39 p.db.SetSession(sessionId) 40 return p 41 } 42 43 44 // WithContent is a chainable function that sets a current State and Cache object. 45 // 46 // This method is normally called before Serialize / Save. 47 func(p *Persister) WithContent(st *state.State, ca *cache.Cache) *Persister { 48 p.State = st 49 p.Memory = ca 50 return p 51 } 52 53 // WithFlush is a chainable function that instructs the persister to flush its memory and state 54 // after successful Save. 55 func(p *Persister) WithFlush() *Persister { 56 p.flush = true 57 return p 58 } 59 60 // Invalid checks if the underlying state has been invalidated. 61 // 62 // An invalid state will cause Save to panic. 63 func(p *Persister) Invalid() bool { 64 return p.GetState().Invalid() || p.GetMemory().Invalid() 65 } 66 67 // GetState returns the state enclosed by the Persister. 68 func(p *Persister) GetState() *state.State { 69 return p.State 70 } 71 72 // GetMemory returns the cache (memory) enclosed by the Persister. 73 func(p *Persister) GetMemory() cache.Memory { 74 return p.Memory 75 } 76 77 // Serialize encodes the state and cache into byte form for storage. 78 func(p *Persister) Serialize() ([]byte, error) { 79 return cbor.Marshal(p) 80 } 81 82 // Deserialize decodes the state and cache from storage, and applies them to the persister. 83 func(p *Persister) Deserialize(b []byte) error { 84 err := cbor.Unmarshal(b, p) 85 return err 86 } 87 88 // Save perists the state and cache to the db.Db backend. 89 // 90 // If save is successful and WithFlush() has been called, the state and memory 91 // will be empty when the method returns. 92 func(p *Persister) Save(key string) error { 93 if p.Invalid() { 94 panic("persister has been invalidated") 95 } 96 b, err := p.Serialize() 97 if err != nil { 98 return err 99 } 100 p.db.SetPrefix(db.DATATYPE_STATE) 101 logg.Debugf("saving state and cache", "self", p, "key", key, "state", p.State) 102 logg.Tracef("saving bytecode", "code", p.State.Code) 103 err = p.db.Put(p.ctx, []byte(key), b) 104 if err != nil { 105 return err 106 } 107 if p.flush { 108 logg.Tracef("state and cache flushed from persister") 109 p.Memory.Reset() 110 p.Memory.Pop() 111 p.State = p.State.CloneEmpty() 112 } 113 return nil 114 } 115 116 // Load retrieves state and cache from the db.Db backend. 117 func(p *Persister) Load(key string) error { 118 p.db.SetPrefix(db.DATATYPE_STATE) 119 b, err := p.db.Get(p.ctx, []byte(key)) 120 if err != nil { 121 return err 122 } 123 err = p.Deserialize(b) 124 if err != nil { 125 return err 126 } 127 logg.Debugf("loaded state and cache", "self", p, "key", key, "state", p.State) 128 logg.Tracef("loaded bytecode", "code", p.State.Code) 129 return nil 130 } 131 132 // String implements the String interface 133 func(p *Persister) String() string { 134 return fmt.Sprintf("perister @%p state:%p cache:%p", p, p.State, p.Memory) 135 }