github.com/iotexproject/iotex-core@v1.14.1-rc1/db/kvstorewithbuffer.go (about) 1 package db 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 8 "github.com/pkg/errors" 9 10 "github.com/iotexproject/iotex-core/db/batch" 11 "github.com/iotexproject/iotex-core/pkg/log" 12 ) 13 14 type ( 15 withBuffer interface { 16 batch.Snapshot 17 SerializeQueue(batch.WriteInfoSerialize, batch.WriteInfoFilter) []byte 18 MustPut(string, []byte, []byte) 19 MustDelete(string, []byte) 20 Size() int 21 } 22 23 // KVStoreWithBuffer defines a KVStore with a buffer, which enables snapshot, revert, 24 // and transaction with multiple writes 25 KVStoreWithBuffer interface { 26 KVStore 27 withBuffer 28 } 29 30 // kvStoreWithBuffer is an implementation of KVStore, which buffers all the changes 31 kvStoreWithBuffer struct { 32 store KVStore 33 buffer batch.CachedBatch 34 } 35 36 // KVStoreFlusher is a wrapper of KVStoreWithBuffer, which has flush api 37 KVStoreFlusher interface { 38 SerializeQueue() []byte 39 Flush() error 40 KVStoreWithBuffer() KVStoreWithBuffer 41 BaseKVStore() KVStore 42 } 43 44 flusher struct { 45 kvb *kvStoreWithBuffer 46 serializeFilter batch.WriteInfoFilter 47 serialize batch.WriteInfoSerialize 48 flushTranslate batch.WriteInfoTranslate 49 } 50 51 // KVStoreFlusherOption sets option for KVStoreFlusher 52 KVStoreFlusherOption func(*flusher) error 53 ) 54 55 // SerializeFilterOption sets the filter for serialize write queue 56 func SerializeFilterOption(filter batch.WriteInfoFilter) KVStoreFlusherOption { 57 return func(f *flusher) error { 58 if filter == nil { 59 return errors.New("filter cannot be nil") 60 } 61 f.serializeFilter = filter 62 63 return nil 64 } 65 } 66 67 // SerializeOption sets the serialize function for write queue 68 func SerializeOption(wis batch.WriteInfoSerialize) KVStoreFlusherOption { 69 return func(f *flusher) error { 70 if wis == nil { 71 return errors.New("serialize function cannot be nil") 72 } 73 f.serialize = wis 74 75 return nil 76 } 77 } 78 79 // FlushTranslateOption sets the translate for flush 80 func FlushTranslateOption(wit batch.WriteInfoTranslate) KVStoreFlusherOption { 81 return func(f *flusher) error { 82 if wit == nil { 83 return errors.New("translate cannot be nil") 84 } 85 f.flushTranslate = wit 86 87 return nil 88 } 89 } 90 91 // NewKVStoreFlusher returns kv store flusher 92 func NewKVStoreFlusher(store KVStore, buffer batch.CachedBatch, opts ...KVStoreFlusherOption) (KVStoreFlusher, error) { 93 if store == nil { 94 return nil, errors.New("store cannot be nil") 95 } 96 if buffer == nil { 97 return nil, errors.New("buffer cannot be nil") 98 } 99 f := &flusher{ 100 kvb: &kvStoreWithBuffer{ 101 store: store, 102 buffer: buffer, 103 }, 104 } 105 for _, opt := range opts { 106 if err := opt(f); err != nil { 107 return nil, errors.Wrap(err, "failed to apply option") 108 } 109 } 110 111 return f, nil 112 } 113 114 func (f *flusher) Flush() error { 115 if err := f.kvb.store.WriteBatch(f.kvb.buffer.Translate(f.flushTranslate)); err != nil { 116 return err 117 } 118 119 f.kvb.buffer.Lock() 120 f.kvb.buffer.ClearAndUnlock() 121 122 return nil 123 } 124 125 func (f *flusher) SerializeQueue() []byte { 126 return f.kvb.SerializeQueue(f.serialize, f.serializeFilter) 127 } 128 129 func (f *flusher) KVStoreWithBuffer() KVStoreWithBuffer { 130 return f.kvb 131 } 132 133 func (f *flusher) BaseKVStore() KVStore { 134 return f.kvb.store 135 } 136 137 func (kvb *kvStoreWithBuffer) Start(ctx context.Context) error { 138 return kvb.store.Start(ctx) 139 } 140 141 func (kvb *kvStoreWithBuffer) Stop(ctx context.Context) error { 142 return kvb.store.Stop(ctx) 143 } 144 145 func (kvb *kvStoreWithBuffer) Snapshot() int { 146 return kvb.buffer.Snapshot() 147 } 148 149 func (kvb *kvStoreWithBuffer) RevertSnapshot(sid int) error { 150 return kvb.buffer.RevertSnapshot(sid) 151 } 152 153 func (kvb *kvStoreWithBuffer) ResetSnapshots() { 154 kvb.buffer.ResetSnapshots() 155 } 156 157 func (kvb *kvStoreWithBuffer) SerializeQueue( 158 serialize batch.WriteInfoSerialize, 159 filter batch.WriteInfoFilter, 160 ) []byte { 161 return kvb.buffer.SerializeQueue(serialize, filter) 162 } 163 164 func (kvb *kvStoreWithBuffer) Size() int { 165 return kvb.buffer.Size() 166 } 167 168 func (kvb *kvStoreWithBuffer) Get(ns string, key []byte) ([]byte, error) { 169 value, err := kvb.buffer.Get(ns, key) 170 if errors.Cause(err) == batch.ErrNotExist { 171 value, err = kvb.store.Get(ns, key) 172 } 173 if errors.Cause(err) == batch.ErrAlreadyDeleted { 174 err = errors.Wrapf(ErrNotExist, "failed to get key %x in %s, deleted in buffer level", key, ns) 175 } 176 return value, err 177 } 178 179 func (kvb *kvStoreWithBuffer) Put(ns string, key, value []byte) error { 180 kvb.buffer.Put(ns, key, value, fmt.Sprintf("faild to put %x in %s", key, ns)) 181 return nil 182 } 183 184 func (kvb *kvStoreWithBuffer) MustPut(ns string, key, value []byte) { 185 kvb.buffer.Put(ns, key, value, fmt.Sprintf("faild to put %x in %s", key, ns)) 186 } 187 188 func (kvb *kvStoreWithBuffer) Delete(ns string, key []byte) error { 189 kvb.buffer.Delete(ns, key, fmt.Sprintf("failed to delete %x in %s", key, ns)) 190 return nil 191 } 192 193 func (kvb *kvStoreWithBuffer) MustDelete(ns string, key []byte) { 194 kvb.buffer.Delete(ns, key, fmt.Sprintf("failed to delete %x in %s", key, ns)) 195 } 196 197 func (kvb *kvStoreWithBuffer) Filter(ns string, cond Condition, minKey, maxKey []byte) ([][]byte, [][]byte, error) { 198 fk, fv, err := kvb.store.Filter(ns, cond, minKey, maxKey) 199 if err != nil { 200 return fk, fv, err 201 } 202 203 // filter the entries in buffer 204 checkMin := len(minKey) > 0 205 checkMax := len(maxKey) > 0 206 for i := 0; i < kvb.buffer.Size(); i++ { 207 entry, err := kvb.buffer.Entry(i) 208 if err != nil { 209 return nil, nil, err 210 } 211 if entry.Namespace() != ns { 212 continue 213 } 214 k, v := entry.Key(), entry.Value() 215 216 if checkMin && bytes.Compare(k, minKey) == -1 { 217 continue 218 } 219 if checkMax && bytes.Compare(k, maxKey) == 1 { 220 continue 221 } 222 223 if cond(k, v) { 224 switch entry.WriteType() { 225 case batch.Put: 226 // if DB contains the same key, that should be obsoleted 227 for i := range fk { 228 if bytes.Equal(fk[i], k) { 229 fk = append(fk[:i], fk[i+1:]...) 230 fv = append(fv[:i], fv[i+1:]...) 231 break 232 } 233 } 234 fk = append(fk, k) 235 fv = append(fv, v) 236 case batch.Delete: 237 for i := range fk { 238 if bytes.Equal(fk[i], k) { 239 fk = append(fk[:i], fk[i+1:]...) 240 fv = append(fv[:i], fv[i+1:]...) 241 break 242 } 243 } 244 } 245 } 246 } 247 return fk, fv, nil 248 } 249 250 func (kvb *kvStoreWithBuffer) WriteBatch(b batch.KVStoreBatch) (err error) { 251 b.Lock() 252 defer func() { 253 if err == nil { 254 // clear the batch if commit succeeds 255 b.ClearAndUnlock() 256 } else { 257 b.Unlock() 258 } 259 }() 260 writes := make([]*batch.WriteInfo, b.Size()) 261 for i := 0; i < b.Size(); i++ { 262 write, e := b.Entry(i) 263 if e != nil { 264 return e 265 } 266 if write.WriteType() != batch.Put && write.WriteType() != batch.Delete { 267 return errors.Errorf("invalid write type %d", write.WriteType()) 268 } 269 writes[i] = write 270 } 271 kvb.buffer.Lock() 272 defer kvb.buffer.Unlock() 273 for _, write := range writes { 274 switch write.WriteType() { 275 case batch.Put: 276 kvb.buffer.Put(write.Namespace(), write.Key(), write.Value(), write.Error()) 277 case batch.Delete: 278 kvb.buffer.Delete(write.Namespace(), write.Key(), write.Error()) 279 default: 280 log.S().Panic("unexpected write type") 281 } 282 } 283 284 return nil 285 }