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