github.com/s7techlab/cckit@v0.10.5/state/state_cached.go (about) 1 package state 2 3 import ( 4 "sort" 5 "strings" 6 7 "github.com/hyperledger/fabric-chaincode-go/shim" 8 "github.com/hyperledger/fabric-protos-go/ledger/queryresult" 9 "github.com/pkg/errors" 10 ) 11 12 type ( 13 TxWriteSet map[string][]byte 14 TxDeleteSet map[string]interface{} 15 16 Cached struct { 17 State 18 TxWriteSet TxWriteSet 19 TxDeleteSet TxDeleteSet 20 } 21 22 CachedQueryIterator struct { 23 current int 24 closed bool 25 KVs []*queryresult.KV 26 } 27 ) 28 29 // WithCached returns state with tx level state cache 30 func WithCache(ss State) *Cached { 31 s := ss.(*Impl) 32 cached := &Cached{ 33 State: s, 34 TxWriteSet: make(map[string][]byte), 35 TxDeleteSet: make(map[string]interface{}), 36 } 37 38 // PutState wrapper 39 s.PutState = func(key string, bb []byte) error { 40 cached.TxWriteSet[key] = bb 41 return s.stub.PutState(key, bb) 42 } 43 44 // GetState wrapper 45 s.GetState = func(key string) ([]byte, error) { 46 if bb, ok := cached.TxWriteSet[key]; ok { 47 return bb, nil 48 } 49 50 if _, ok := cached.TxDeleteSet[key]; ok { 51 return []byte{}, nil 52 } 53 return s.stub.GetState(key) 54 } 55 56 s.DelState = func(key string) error { 57 delete(cached.TxWriteSet, key) 58 cached.TxDeleteSet[key] = nil 59 60 return s.stub.DelState(key) 61 } 62 63 s.GetStateByPartialCompositeKey = func(objectType string, keys []string) (shim.StateQueryIteratorInterface, error) { 64 iterator, err := s.stub.GetStateByPartialCompositeKey(objectType, keys) 65 if err != nil { 66 return nil, err 67 } 68 69 prefix, err := s.stub.CreateCompositeKey(objectType, keys) 70 if err != nil { 71 return nil, err 72 } 73 74 return NewCachedQueryIterator(iterator, prefix, cached.TxWriteSet, cached.TxDeleteSet) 75 } 76 77 return cached 78 } 79 80 func NewCachedQueryIterator(iterator shim.StateQueryIteratorInterface, prefix string, writeSet TxWriteSet, deleteSet TxDeleteSet) (*CachedQueryIterator, error) { 81 queryIterator := &CachedQueryIterator{ 82 current: -1, 83 } 84 for iterator.HasNext() { 85 kv, err := iterator.Next() 86 if err != nil { 87 return nil, err 88 } 89 90 if _, ok := deleteSet[kv.Key]; ok { 91 continue 92 } 93 94 queryIterator.KVs = append(queryIterator.KVs, kv) 95 } 96 97 for wroteKey, wroteValue := range writeSet { 98 if strings.HasPrefix(wroteKey, prefix) { 99 queryIterator.KVs = append(queryIterator.KVs, &queryresult.KV{ 100 Namespace: "", 101 Key: wroteKey, 102 Value: wroteValue, 103 }) 104 } 105 } 106 107 sort.Slice(queryIterator.KVs, func(i, j int) bool { 108 return queryIterator.KVs[i].Key < queryIterator.KVs[j].Key 109 }) 110 111 return queryIterator, nil 112 } 113 114 func (i *CachedQueryIterator) Next() (*queryresult.KV, error) { 115 if !i.HasNext() { 116 return nil, errors.New(`no next items`) 117 } 118 119 i.current++ 120 return i.KVs[i.current], nil 121 } 122 123 // HasNext returns true if the range query iterator contains additional keys 124 // and values. 125 func (i *CachedQueryIterator) HasNext() bool { 126 return i.current < len(i.KVs)-1 127 } 128 129 // Close closes the iterator. This should be called when done 130 // reading from the iterator to free up resources. 131 func (i *CachedQueryIterator) Close() error { 132 i.closed = true 133 return nil 134 }