github.com/true-sqn/fabric@v2.1.1+incompatible/core/ledger/kvledger/txmgmt/statedb/stateleveldb/stateleveldb.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package stateleveldb 8 9 import ( 10 "bytes" 11 12 "github.com/hyperledger/fabric/common/flogging" 13 "github.com/hyperledger/fabric/common/ledger/dataformat" 14 "github.com/hyperledger/fabric/common/ledger/util/leveldbhelper" 15 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb" 16 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version" 17 "github.com/pkg/errors" 18 "github.com/syndtr/goleveldb/leveldb/iterator" 19 ) 20 21 var logger = flogging.MustGetLogger("stateleveldb") 22 23 var ( 24 dataKeyPrefix = []byte{'d'} 25 nsKeySep = []byte{0x00} 26 lastKeyIndicator = byte(0x01) 27 savePointKey = []byte{'s'} 28 ) 29 30 // VersionedDBProvider implements interface VersionedDBProvider 31 type VersionedDBProvider struct { 32 dbProvider *leveldbhelper.Provider 33 } 34 35 // NewVersionedDBProvider instantiates VersionedDBProvider 36 func NewVersionedDBProvider(dbPath string) (*VersionedDBProvider, error) { 37 logger.Debugf("constructing VersionedDBProvider dbPath=%s", dbPath) 38 dbProvider, err := leveldbhelper.NewProvider( 39 &leveldbhelper.Conf{ 40 DBPath: dbPath, 41 ExpectedFormatVersion: dataformat.Version20, 42 }) 43 if err != nil { 44 return nil, err 45 } 46 return &VersionedDBProvider{dbProvider}, nil 47 } 48 49 // GetDBHandle gets the handle to a named database 50 func (provider *VersionedDBProvider) GetDBHandle(dbName string) (statedb.VersionedDB, error) { 51 return newVersionedDB(provider.dbProvider.GetDBHandle(dbName), dbName), nil 52 } 53 54 // Close closes the underlying db 55 func (provider *VersionedDBProvider) Close() { 56 provider.dbProvider.Close() 57 } 58 59 // VersionedDB implements VersionedDB interface 60 type versionedDB struct { 61 db *leveldbhelper.DBHandle 62 dbName string 63 } 64 65 // newVersionedDB constructs an instance of VersionedDB 66 func newVersionedDB(db *leveldbhelper.DBHandle, dbName string) *versionedDB { 67 return &versionedDB{db, dbName} 68 } 69 70 // Open implements method in VersionedDB interface 71 func (vdb *versionedDB) Open() error { 72 // do nothing because shared db is used 73 return nil 74 } 75 76 // Close implements method in VersionedDB interface 77 func (vdb *versionedDB) Close() { 78 // do nothing because shared db is used 79 } 80 81 // ValidateKeyValue implements method in VersionedDB interface 82 func (vdb *versionedDB) ValidateKeyValue(key string, value []byte) error { 83 return nil 84 } 85 86 // BytesKeySupported implements method in VersionedDB interface 87 func (vdb *versionedDB) BytesKeySupported() bool { 88 return true 89 } 90 91 // GetState implements method in VersionedDB interface 92 func (vdb *versionedDB) GetState(namespace string, key string) (*statedb.VersionedValue, error) { 93 logger.Debugf("GetState(). ns=%s, key=%s", namespace, key) 94 dbVal, err := vdb.db.Get(encodeDataKey(namespace, key)) 95 if err != nil { 96 return nil, err 97 } 98 if dbVal == nil { 99 return nil, nil 100 } 101 return decodeValue(dbVal) 102 } 103 104 // GetVersion implements method in VersionedDB interface 105 func (vdb *versionedDB) GetVersion(namespace string, key string) (*version.Height, error) { 106 versionedValue, err := vdb.GetState(namespace, key) 107 if err != nil { 108 return nil, err 109 } 110 if versionedValue == nil { 111 return nil, nil 112 } 113 return versionedValue.Version, nil 114 } 115 116 // GetStateMultipleKeys implements method in VersionedDB interface 117 func (vdb *versionedDB) GetStateMultipleKeys(namespace string, keys []string) ([]*statedb.VersionedValue, error) { 118 vals := make([]*statedb.VersionedValue, len(keys)) 119 for i, key := range keys { 120 val, err := vdb.GetState(namespace, key) 121 if err != nil { 122 return nil, err 123 } 124 vals[i] = val 125 } 126 return vals, nil 127 } 128 129 // GetStateRangeScanIterator implements method in VersionedDB interface 130 // startKey is inclusive 131 // endKey is exclusive 132 func (vdb *versionedDB) GetStateRangeScanIterator(namespace string, startKey string, endKey string) (statedb.ResultsIterator, error) { 133 return vdb.GetStateRangeScanIteratorWithMetadata(namespace, startKey, endKey, nil) 134 } 135 136 const optionLimit = "limit" 137 138 // GetStateRangeScanIteratorWithMetadata implements method in VersionedDB interface 139 func (vdb *versionedDB) GetStateRangeScanIteratorWithMetadata(namespace string, startKey string, endKey string, metadata map[string]interface{}) (statedb.QueryResultsIterator, error) { 140 141 requestedLimit := int32(0) 142 // if metadata is provided, validate and apply options 143 if metadata != nil { 144 //validate the metadata 145 err := statedb.ValidateRangeMetadata(metadata) 146 if err != nil { 147 return nil, err 148 } 149 if limitOption, ok := metadata[optionLimit]; ok { 150 requestedLimit = limitOption.(int32) 151 } 152 } 153 154 // Note: metadata is not used for the goleveldb implementation of the range query 155 dataStartKey := encodeDataKey(namespace, startKey) 156 dataEndKey := encodeDataKey(namespace, endKey) 157 if endKey == "" { 158 dataEndKey[len(dataEndKey)-1] = lastKeyIndicator 159 } 160 dbItr := vdb.db.GetIterator(dataStartKey, dataEndKey) 161 return newKVScanner(namespace, dbItr, requestedLimit), nil 162 } 163 164 // ExecuteQuery implements method in VersionedDB interface 165 func (vdb *versionedDB) ExecuteQuery(namespace, query string) (statedb.ResultsIterator, error) { 166 return nil, errors.New("ExecuteQuery not supported for leveldb") 167 } 168 169 // ExecuteQueryWithMetadata implements method in VersionedDB interface 170 func (vdb *versionedDB) ExecuteQueryWithMetadata(namespace, query string, metadata map[string]interface{}) (statedb.QueryResultsIterator, error) { 171 return nil, errors.New("ExecuteQueryWithMetadata not supported for leveldb") 172 } 173 174 // ApplyUpdates implements method in VersionedDB interface 175 func (vdb *versionedDB) ApplyUpdates(batch *statedb.UpdateBatch, height *version.Height) error { 176 dbBatch := leveldbhelper.NewUpdateBatch() 177 namespaces := batch.GetUpdatedNamespaces() 178 for _, ns := range namespaces { 179 updates := batch.GetUpdates(ns) 180 for k, vv := range updates { 181 dataKey := encodeDataKey(ns, k) 182 logger.Debugf("Channel [%s]: Applying key(string)=[%s] key(bytes)=[%#v]", vdb.dbName, string(dataKey), dataKey) 183 184 if vv.Value == nil { 185 dbBatch.Delete(dataKey) 186 } else { 187 encodedVal, err := encodeValue(vv) 188 if err != nil { 189 return err 190 } 191 dbBatch.Put(dataKey, encodedVal) 192 } 193 } 194 } 195 // Record a savepoint at a given height 196 // If a given height is nil, it denotes that we are committing pvt data of old blocks. 197 // In this case, we should not store a savepoint for recovery. The lastUpdatedOldBlockList 198 // in the pvtstore acts as a savepoint for pvt data. 199 if height != nil { 200 dbBatch.Put(savePointKey, height.ToBytes()) 201 } 202 // Setting snyc to true as a precaution, false may be an ok optimization after further testing. 203 if err := vdb.db.WriteBatch(dbBatch, true); err != nil { 204 return err 205 } 206 return nil 207 } 208 209 // GetLatestSavePoint implements method in VersionedDB interface 210 func (vdb *versionedDB) GetLatestSavePoint() (*version.Height, error) { 211 versionBytes, err := vdb.db.Get(savePointKey) 212 if err != nil { 213 return nil, err 214 } 215 if versionBytes == nil { 216 return nil, nil 217 } 218 version, _, err := version.NewHeightFromBytes(versionBytes) 219 if err != nil { 220 return nil, err 221 } 222 return version, nil 223 } 224 225 func encodeDataKey(ns, key string) []byte { 226 k := append(dataKeyPrefix, []byte(ns)...) 227 k = append(k, nsKeySep...) 228 return append(k, []byte(key)...) 229 } 230 231 func decodeDataKey(encodedDataKey []byte) (string, string) { 232 split := bytes.SplitN(encodedDataKey, nsKeySep, 2) 233 return string(split[0][1:]), string(split[1]) 234 } 235 236 type kvScanner struct { 237 namespace string 238 dbItr iterator.Iterator 239 requestedLimit int32 240 totalRecordsReturned int32 241 } 242 243 func newKVScanner(namespace string, dbItr iterator.Iterator, requestedLimit int32) *kvScanner { 244 return &kvScanner{namespace, dbItr, requestedLimit, 0} 245 } 246 247 func (scanner *kvScanner) Next() (statedb.QueryResult, error) { 248 if scanner.requestedLimit > 0 && scanner.totalRecordsReturned >= scanner.requestedLimit { 249 return nil, nil 250 } 251 if !scanner.dbItr.Next() { 252 return nil, nil 253 } 254 255 dbKey := scanner.dbItr.Key() 256 dbVal := scanner.dbItr.Value() 257 dbValCopy := make([]byte, len(dbVal)) 258 copy(dbValCopy, dbVal) 259 _, key := decodeDataKey(dbKey) 260 vv, err := decodeValue(dbValCopy) 261 if err != nil { 262 return nil, err 263 } 264 265 scanner.totalRecordsReturned++ 266 return &statedb.VersionedKV{ 267 CompositeKey: statedb.CompositeKey{Namespace: scanner.namespace, Key: key}, 268 // TODO remove dereferrencing below by changing the type of the field 269 // `VersionedValue` in `statedb.VersionedKV` to a pointer 270 VersionedValue: *vv}, nil 271 } 272 273 func (scanner *kvScanner) Close() { 274 scanner.dbItr.Release() 275 } 276 277 func (scanner *kvScanner) GetBookmarkAndClose() string { 278 retval := "" 279 if scanner.dbItr.Next() { 280 dbKey := scanner.dbItr.Key() 281 _, key := decodeDataKey(dbKey) 282 retval = key 283 } 284 scanner.Close() 285 return retval 286 }