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