github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/core/ledger/kvledger/txmgmt/statedb/statedb.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package statedb 8 9 import ( 10 "sort" 11 12 "github.com/osdi23p228/fabric/core/ledger/internal/version" 13 "github.com/osdi23p228/fabric/core/ledger/util" 14 ) 15 16 //go:generate counterfeiter -o mock/results_iterator.go -fake-name ResultsIterator . ResultsIterator 17 //go:generate counterfeiter -o mock/versioned_db.go -fake-name VersionedDB . VersionedDB 18 //go:generate counterfeiter -o mock/namespace_provider.go -fake-name NamespaceProvider . NamespaceProvider 19 20 // VersionedDBProvider provides an instance of an versioned DB 21 type VersionedDBProvider interface { 22 // GetDBHandle returns a handle to a VersionedDB 23 GetDBHandle(id string, namespaceProvider NamespaceProvider) (VersionedDB, error) 24 // Close closes all the VersionedDB instances and releases any resources held by VersionedDBProvider 25 Close() 26 } 27 28 // VersionedDB lists methods that a db is supposed to implement 29 type VersionedDB interface { 30 // GetState gets the value for given namespace and key. For a chaincode, the namespace corresponds to the chaincodeId 31 GetState(namespace string, key string) (*VersionedValue, error) 32 // GetVersion gets the version for given namespace and key. For a chaincode, the namespace corresponds to the chaincodeId 33 GetVersion(namespace string, key string) (*version.Height, error) 34 // GetStateMultipleKeys gets the values for multiple keys in a single call 35 GetStateMultipleKeys(namespace string, keys []string) ([]*VersionedValue, error) 36 // GetStateRangeScanIterator returns an iterator that contains all the key-values between given key ranges. 37 // startKey is inclusive 38 // endKey is exclusive 39 // The returned ResultsIterator contains results of type *VersionedKV 40 GetStateRangeScanIterator(namespace string, startKey string, endKey string) (ResultsIterator, error) 41 // GetStateRangeScanIteratorWithPagination returns an iterator that contains all the key-values between given key ranges. 42 // startKey is inclusive 43 // endKey is exclusive 44 // pageSize parameter limits the number of returned results 45 // The returned ResultsIterator contains results of type *VersionedKV 46 GetStateRangeScanIteratorWithPagination(namespace string, startKey string, endKey string, pageSize int32) (QueryResultsIterator, error) 47 // ExecuteQuery executes the given query and returns an iterator that contains results of type *VersionedKV. 48 ExecuteQuery(namespace, query string) (ResultsIterator, error) 49 // ExecuteQueryWithPagination executes the given query and 50 // returns an iterator that contains results of type *VersionedKV. 51 // The bookmark and page size parameters are associated with the pagination query. 52 ExecuteQueryWithPagination(namespace, query, bookmark string, pageSize int32) (QueryResultsIterator, error) 53 // ApplyUpdates applies the batch to the underlying db. 54 // height is the height of the highest transaction in the Batch that 55 // a state db implementation is expected to ues as a save point 56 ApplyUpdates(batch *UpdateBatch, height *version.Height) error 57 // GetLatestSavePoint returns the height of the highest transaction upto which 58 // the state db is consistent 59 GetLatestSavePoint() (*version.Height, error) 60 // ValidateKeyValue tests whether the key and value is supported by the db implementation. 61 // For instance, leveldb supports any bytes for the key while the couchdb supports only valid utf-8 string 62 // TODO make the function ValidateKeyValue return a specific error say ErrInvalidKeyValue 63 // However, as of now, the both implementations of this function (leveldb and couchdb) are deterministic in returing an error 64 // i.e., an error is returned only if the key-value are found to be invalid for the underlying db 65 ValidateKeyValue(key string, value []byte) error 66 // BytesKeySupported returns true if the implementation (underlying db) supports the any bytes to be used as key. 67 // For instance, leveldb supports any bytes for the key while the couchdb supports only valid utf-8 string 68 BytesKeySupported() bool 69 // GetFullScanIterator returns a FullScanIterator that can be used to iterate over entire data in the statedb. 70 // `skipNamespace` parameter can be used to control if the consumer wants the FullScanIterator 71 // to skip one or more namespaces from the returned results. The second parameter returns the format information 72 // about the value bytes returned by the Next function in the returned FullScanIterator. 73 // The intended use of this iterator is to generate the snapshot files for the statedb. 74 GetFullScanIterator(skipNamespace func(string) bool) (FullScanIterator, byte, error) 75 // Open opens the db 76 Open() error 77 // Close closes the db 78 Close() 79 } 80 81 // NamespaceProvider provides a mean for statedb to get all the possible namespaces for a channel. 82 // The intended use is for statecouchdb to retroactively build channel metadata when it is missing, 83 // e.g., when opening a statecouchdb from v2.0/2.1 version. 84 type NamespaceProvider interface { 85 // PossibleNamespaces returns all possible namespaces for the statedb. Note that it is a superset 86 // of the actual namespaces. Therefore, the caller should compare with the existing databases to 87 // filter out the namespaces that have no matched databases. 88 PossibleNamespaces(vdb VersionedDB) ([]string, error) 89 } 90 91 //BulkOptimizable interface provides additional functions for 92 //databases capable of batch operations 93 type BulkOptimizable interface { 94 LoadCommittedVersions(keys []*CompositeKey) error 95 GetCachedVersion(namespace, key string) (*version.Height, bool) 96 ClearCachedVersions() 97 } 98 99 //IndexCapable interface provides additional functions for 100 //databases capable of index operations 101 type IndexCapable interface { 102 GetDBType() string 103 ProcessIndexesForChaincodeDeploy(namespace string, indexFilesData map[string][]byte) error 104 } 105 106 // FullScanIterator provides a mean to iterate over entire statedb. The intended use of this iterator 107 // is to generate the snapshot files for the statedb 108 type FullScanIterator interface { 109 // Next returns the key-values in the lexical order of <Namespace, key> 110 // A particular statedb implementation is free to chose any deterministic bytes representation for the <version, value, metadata> 111 Next() (*CompositeKey, []byte, error) 112 // Close releases any resources held with the implementation 113 Close() 114 } 115 116 // CompositeKey encloses Namespace and Key components 117 type CompositeKey struct { 118 Namespace string 119 Key string 120 } 121 122 // VersionedValue encloses value and corresponding version 123 type VersionedValue struct { 124 Value []byte 125 Metadata []byte 126 Version *version.Height 127 } 128 129 // IsDelete returns true if this update indicates delete of a key 130 func (vv *VersionedValue) IsDelete() bool { 131 return vv.Value == nil 132 } 133 134 // VersionedKV encloses key and corresponding VersionedValue 135 type VersionedKV struct { 136 CompositeKey 137 VersionedValue 138 } 139 140 // ResultsIterator iterates over query results 141 type ResultsIterator interface { 142 Next() (QueryResult, error) 143 Close() 144 } 145 146 // QueryResultsIterator adds GetBookmarkAndClose method 147 type QueryResultsIterator interface { 148 ResultsIterator 149 GetBookmarkAndClose() string 150 } 151 152 // QueryResult - a general interface for supporting different types of query results. Actual types differ for different queries 153 type QueryResult interface{} 154 155 type nsUpdates struct { 156 M map[string]*VersionedValue 157 } 158 159 func newNsUpdates() *nsUpdates { 160 return &nsUpdates{make(map[string]*VersionedValue)} 161 } 162 163 // UpdateBatch encloses the details of multiple `updates` 164 type UpdateBatch struct { 165 ContainsPostOrderWrites bool 166 Updates map[string]*nsUpdates 167 } 168 169 // NewUpdateBatch constructs an instance of a Batch 170 func NewUpdateBatch() *UpdateBatch { 171 return &UpdateBatch{false, make(map[string]*nsUpdates)} 172 } 173 174 // Get returns the VersionedValue for the given namespace and key 175 func (batch *UpdateBatch) Get(ns string, key string) *VersionedValue { 176 nsUpdates, ok := batch.Updates[ns] 177 if !ok { 178 return nil 179 } 180 vv, ok := nsUpdates.M[key] 181 if !ok { 182 return nil 183 } 184 return vv 185 } 186 187 // Put adds a key with value only. The metadata is assumed to be nil 188 func (batch *UpdateBatch) Put(ns string, key string, value []byte, version *version.Height) { 189 batch.PutValAndMetadata(ns, key, value, nil, version) 190 } 191 192 // PutValAndMetadata adds a key with value and metadata 193 // TODO introducing a new function to limit the refactoring. Later in a separate CR, the 'Put' function above should be removed 194 func (batch *UpdateBatch) PutValAndMetadata(ns string, key string, value []byte, metadata []byte, version *version.Height) { 195 if value == nil { 196 panic("Nil value not allowed. Instead call 'Delete' function") 197 } 198 batch.Update(ns, key, &VersionedValue{value, metadata, version}) 199 } 200 201 // Delete deletes a Key and associated value 202 func (batch *UpdateBatch) Delete(ns string, key string, version *version.Height) { 203 batch.Update(ns, key, &VersionedValue{nil, nil, version}) 204 } 205 206 // Exists checks whether the given key exists in the batch 207 func (batch *UpdateBatch) Exists(ns string, key string) bool { 208 nsUpdates, ok := batch.Updates[ns] 209 if !ok { 210 return false 211 } 212 _, ok = nsUpdates.M[key] 213 return ok 214 } 215 216 // GetUpdatedNamespaces returns the names of the namespaces that are updated 217 func (batch *UpdateBatch) GetUpdatedNamespaces() []string { 218 namespaces := make([]string, len(batch.Updates)) 219 i := 0 220 for ns := range batch.Updates { 221 namespaces[i] = ns 222 i++ 223 } 224 return namespaces 225 } 226 227 // Update updates the batch with a latest entry for a namespace and a key 228 func (batch *UpdateBatch) Update(ns string, key string, vv *VersionedValue) { 229 batch.getOrCreateNsUpdates(ns).M[key] = vv 230 } 231 232 // GetUpdates returns all the updates for a namespace 233 func (batch *UpdateBatch) GetUpdates(ns string) map[string]*VersionedValue { 234 nsUpdates, ok := batch.Updates[ns] 235 if !ok { 236 return nil 237 } 238 return nsUpdates.M 239 } 240 241 // GetRangeScanIterator returns an iterator that iterates over keys of a specific namespace in sorted order 242 // In other word this gives the same functionality over the contents in the `UpdateBatch` as 243 // `VersionedDB.GetStateRangeScanIterator()` method gives over the contents in the statedb 244 // This function can be used for querying the contents in the updateBatch before they are committed to the statedb. 245 // For instance, a validator implementation can used this to verify the validity of a range query of a transaction 246 // where the UpdateBatch represents the union of the modifications performed by the preceding valid transactions in the same block 247 // (Assuming Group commit approach where we commit all the updates caused by a block together). 248 func (batch *UpdateBatch) GetRangeScanIterator(ns string, startKey string, endKey string) QueryResultsIterator { 249 return newNsIterator(ns, startKey, endKey, batch) 250 } 251 252 // Merge merges another updates batch with this updates batch 253 func (batch *UpdateBatch) Merge(toMerge *UpdateBatch) { 254 batch.ContainsPostOrderWrites = batch.ContainsPostOrderWrites || toMerge.ContainsPostOrderWrites 255 for ns, nsUpdates := range toMerge.Updates { 256 for key, vv := range nsUpdates.M { 257 batch.Update(ns, key, vv) 258 } 259 } 260 } 261 262 func (batch *UpdateBatch) getOrCreateNsUpdates(ns string) *nsUpdates { 263 nsUpdates := batch.Updates[ns] 264 if nsUpdates == nil { 265 nsUpdates = newNsUpdates() 266 batch.Updates[ns] = nsUpdates 267 } 268 return nsUpdates 269 } 270 271 type nsIterator struct { 272 ns string 273 nsUpdates *nsUpdates 274 sortedKeys []string 275 nextIndex int 276 lastIndex int 277 } 278 279 func newNsIterator(ns string, startKey string, endKey string, batch *UpdateBatch) *nsIterator { 280 nsUpdates, ok := batch.Updates[ns] 281 if !ok { 282 return &nsIterator{} 283 } 284 sortedKeys := util.GetSortedKeys(nsUpdates.M) 285 var nextIndex int 286 var lastIndex int 287 if startKey == "" { 288 nextIndex = 0 289 } else { 290 nextIndex = sort.SearchStrings(sortedKeys, startKey) 291 } 292 if endKey == "" { 293 lastIndex = len(sortedKeys) 294 } else { 295 lastIndex = sort.SearchStrings(sortedKeys, endKey) 296 } 297 return &nsIterator{ns, nsUpdates, sortedKeys, nextIndex, lastIndex} 298 } 299 300 // Next gives next key and versioned value. It returns a nil when exhausted 301 func (itr *nsIterator) Next() (QueryResult, error) { 302 if itr.nextIndex >= itr.lastIndex { 303 return nil, nil 304 } 305 key := itr.sortedKeys[itr.nextIndex] 306 vv := itr.nsUpdates.M[key] 307 itr.nextIndex++ 308 return &VersionedKV{CompositeKey{itr.ns, key}, VersionedValue{vv.Value, vv.Metadata, vv.Version}}, nil 309 } 310 311 // Close implements the method from QueryResult interface 312 func (itr *nsIterator) Close() { 313 // do nothing 314 } 315 316 // GetBookmarkAndClose implements the method from QueryResult interface 317 func (itr *nsIterator) GetBookmarkAndClose() string { 318 // do nothing 319 return "" 320 }