github.com/Finschia/finschia-sdk@v0.48.1/store/tracekv/store.go (about) 1 package tracekv 2 3 import ( 4 "encoding/base64" 5 "encoding/json" 6 "io" 7 8 "github.com/Finschia/finschia-sdk/store/types" 9 "github.com/Finschia/finschia-sdk/types/errors" 10 ) 11 12 const ( 13 writeOp operation = "write" 14 readOp operation = "read" 15 deleteOp operation = "delete" 16 iterKeyOp operation = "iterKey" 17 iterValueOp operation = "iterValue" 18 ) 19 20 type ( 21 // Store implements the KVStore interface with tracing enabled. 22 // Operations are traced on each core KVStore call and written to the 23 // underlying io.writer. 24 // 25 // TODO: Should we use a buffered writer and implement Commit on 26 // Store? 27 Store struct { 28 parent types.KVStore 29 writer io.Writer 30 context types.TraceContext 31 } 32 33 // operation represents an IO operation 34 operation string 35 36 // traceOperation implements a traced KVStore operation 37 traceOperation struct { 38 Operation operation `json:"operation"` 39 Key string `json:"key"` 40 Value string `json:"value"` 41 Metadata map[string]interface{} `json:"metadata"` 42 } 43 ) 44 45 // NewStore returns a reference to a new traceKVStore given a parent 46 // KVStore implementation and a buffered writer. 47 func NewStore(parent types.KVStore, writer io.Writer, tc types.TraceContext) *Store { 48 return &Store{parent: parent, writer: writer, context: tc} 49 } 50 51 // Get implements the KVStore interface. It traces a read operation and 52 // delegates a Get call to the parent KVStore. 53 func (tkv *Store) Get(key []byte) []byte { 54 value := tkv.parent.Get(key) 55 56 writeOperation(tkv.writer, readOp, tkv.context, key, value) 57 return value 58 } 59 60 // Set implements the KVStore interface. It traces a write operation and 61 // delegates the Set call to the parent KVStore. 62 func (tkv *Store) Set(key []byte, value []byte) { 63 types.AssertValidKey(key) 64 writeOperation(tkv.writer, writeOp, tkv.context, key, value) 65 tkv.parent.Set(key, value) 66 } 67 68 // Delete implements the KVStore interface. It traces a write operation and 69 // delegates the Delete call to the parent KVStore. 70 func (tkv *Store) Delete(key []byte) { 71 writeOperation(tkv.writer, deleteOp, tkv.context, key, nil) 72 tkv.parent.Delete(key) 73 } 74 75 // Has implements the KVStore interface. It delegates the Has call to the 76 // parent KVStore. 77 func (tkv *Store) Has(key []byte) bool { 78 return tkv.parent.Has(key) 79 } 80 81 // Iterator implements the KVStore interface. It delegates the Iterator call 82 // the to the parent KVStore. 83 func (tkv *Store) Iterator(start, end []byte) types.Iterator { 84 return tkv.iterator(start, end, true) 85 } 86 87 // ReverseIterator implements the KVStore interface. It delegates the 88 // ReverseIterator call the to the parent KVStore. 89 func (tkv *Store) ReverseIterator(start, end []byte) types.Iterator { 90 return tkv.iterator(start, end, false) 91 } 92 93 // iterator facilitates iteration over a KVStore. It delegates the necessary 94 // calls to it's parent KVStore. 95 func (tkv *Store) iterator(start, end []byte, ascending bool) types.Iterator { 96 var parent types.Iterator 97 98 if ascending { 99 parent = tkv.parent.Iterator(start, end) 100 } else { 101 parent = tkv.parent.ReverseIterator(start, end) 102 } 103 104 return newTraceIterator(tkv.writer, parent, tkv.context) 105 } 106 107 type traceIterator struct { 108 parent types.Iterator 109 writer io.Writer 110 context types.TraceContext 111 } 112 113 func newTraceIterator(w io.Writer, parent types.Iterator, tc types.TraceContext) types.Iterator { 114 return &traceIterator{writer: w, parent: parent, context: tc} 115 } 116 117 // Domain implements the Iterator interface. 118 func (ti *traceIterator) Domain() (start []byte, end []byte) { 119 return ti.parent.Domain() 120 } 121 122 // Valid implements the Iterator interface. 123 func (ti *traceIterator) Valid() bool { 124 return ti.parent.Valid() 125 } 126 127 // Next implements the Iterator interface. 128 func (ti *traceIterator) Next() { 129 ti.parent.Next() 130 } 131 132 // Key implements the Iterator interface. 133 func (ti *traceIterator) Key() []byte { 134 key := ti.parent.Key() 135 136 writeOperation(ti.writer, iterKeyOp, ti.context, key, nil) 137 return key 138 } 139 140 // Value implements the Iterator interface. 141 func (ti *traceIterator) Value() []byte { 142 value := ti.parent.Value() 143 144 writeOperation(ti.writer, iterValueOp, ti.context, nil, value) 145 return value 146 } 147 148 // Close implements the Iterator interface. 149 func (ti *traceIterator) Close() error { 150 return ti.parent.Close() 151 } 152 153 // Error delegates the Error call to the parent iterator. 154 func (ti *traceIterator) Error() error { 155 return ti.parent.Error() 156 } 157 158 // GetStoreType implements the KVStore interface. It returns the underlying 159 // KVStore type. 160 func (tkv *Store) GetStoreType() types.StoreType { 161 return tkv.parent.GetStoreType() 162 } 163 164 // CacheWrap implements the KVStore interface. It panics because a Store 165 // cannot be branched. 166 func (tkv *Store) CacheWrap() types.CacheWrap { 167 panic("cannot CacheWrap a TraceKVStore") 168 } 169 170 // CacheWrapWithTrace implements the KVStore interface. It panics as a 171 // Store cannot be branched. 172 func (tkv *Store) CacheWrapWithTrace(_ io.Writer, _ types.TraceContext) types.CacheWrap { 173 panic("cannot CacheWrapWithTrace a TraceKVStore") 174 } 175 176 // CacheWrapWithListeners implements the CacheWrapper interface. 177 func (tkv *Store) CacheWrapWithListeners(_ types.StoreKey, _ []types.WriteListener) types.CacheWrap { 178 panic("cannot CacheWrapWithListeners a TraceKVStore") 179 } 180 181 // writeOperation writes a KVStore operation to the underlying io.Writer as 182 // JSON-encoded data where the key/value pair is base64 encoded. 183 func writeOperation(w io.Writer, op operation, tc types.TraceContext, key, value []byte) { 184 traceOp := traceOperation{ 185 Operation: op, 186 Key: base64.StdEncoding.EncodeToString(key), 187 Value: base64.StdEncoding.EncodeToString(value), 188 } 189 190 if tc != nil { 191 traceOp.Metadata = tc 192 } 193 194 raw, err := json.Marshal(traceOp) 195 if err != nil { 196 panic(errors.Wrap(err, "failed to serialize trace operation")) 197 } 198 199 if _, err := w.Write(raw); err != nil { 200 panic(errors.Wrap(err, "failed to write trace operation")) 201 } 202 203 io.WriteString(w, "\n") 204 }