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  }