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  }