github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/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/osdi23p228/fabric/common/flogging" 13 "github.com/osdi23p228/fabric/common/ledger/dataformat" 14 "github.com/osdi23p228/fabric/common/ledger/util/leveldbhelper" 15 "github.com/osdi23p228/fabric/core/ledger/internal/version" 16 "github.com/osdi23p228/fabric/core/ledger/kvledger/txmgmt/statedb" 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 dataKeyStopper = []byte{'e'} 26 nsKeySep = []byte{0x00} 27 lastKeyIndicator = byte(0x01) 28 savePointKey = []byte{'s'} 29 fullScanIteratorValueFormat = byte(1) 30 ) 31 32 // VersionedDBProvider implements interface VersionedDBProvider 33 type VersionedDBProvider struct { 34 dbProvider *leveldbhelper.Provider 35 } 36 37 // NewVersionedDBProvider instantiates VersionedDBProvider 38 func NewVersionedDBProvider(dbPath string) (*VersionedDBProvider, error) { 39 logger.Debugf("constructing VersionedDBProvider dbPath=%s", dbPath) 40 dbProvider, err := leveldbhelper.NewProvider( 41 &leveldbhelper.Conf{ 42 DBPath: dbPath, 43 ExpectedFormat: dataformat.CurrentFormat, 44 }) 45 if err != nil { 46 return nil, err 47 } 48 return &VersionedDBProvider{dbProvider}, nil 49 } 50 51 // GetDBHandle gets the handle to a named database 52 func (provider *VersionedDBProvider) GetDBHandle(dbName string, namespaceProvider statedb.NamespaceProvider) (statedb.VersionedDB, error) { 53 return newVersionedDB(provider.dbProvider.GetDBHandle(dbName), dbName), nil 54 } 55 56 // Close closes the underlying db 57 func (provider *VersionedDBProvider) Close() { 58 provider.dbProvider.Close() 59 } 60 61 // VersionedDB implements VersionedDB interface 62 type versionedDB struct { 63 db *leveldbhelper.DBHandle 64 dbName string 65 } 66 67 // newVersionedDB constructs an instance of VersionedDB 68 func newVersionedDB(db *leveldbhelper.DBHandle, dbName string) *versionedDB { 69 return &versionedDB{db, dbName} 70 } 71 72 // Open implements method in VersionedDB interface 73 func (vdb *versionedDB) Open() error { 74 // do nothing because shared db is used 75 return nil 76 } 77 78 // Close implements method in VersionedDB interface 79 func (vdb *versionedDB) Close() { 80 // do nothing because shared db is used 81 } 82 83 // ValidateKeyValue implements method in VersionedDB interface 84 func (vdb *versionedDB) ValidateKeyValue(key string, value []byte) error { 85 return nil 86 } 87 88 // BytesKeySupported implements method in VersionedDB interface 89 func (vdb *versionedDB) BytesKeySupported() bool { 90 return true 91 } 92 93 // GetState implements method in VersionedDB interface 94 func (vdb *versionedDB) GetState(namespace string, key string) (*statedb.VersionedValue, error) { 95 logger.Debugf("GetState(). ns=%s, key=%s", namespace, key) 96 dbVal, err := vdb.db.Get(encodeDataKey(namespace, key)) 97 if err != nil { 98 return nil, err 99 } 100 if dbVal == nil { 101 return nil, nil 102 } 103 return decodeValue(dbVal) 104 } 105 106 // GetVersion implements method in VersionedDB interface 107 func (vdb *versionedDB) GetVersion(namespace string, key string) (*version.Height, error) { 108 versionedValue, err := vdb.GetState(namespace, key) 109 if err != nil { 110 return nil, err 111 } 112 if versionedValue == nil { 113 return nil, nil 114 } 115 return versionedValue.Version, nil 116 } 117 118 // GetStateMultipleKeys implements method in VersionedDB interface 119 func (vdb *versionedDB) GetStateMultipleKeys(namespace string, keys []string) ([]*statedb.VersionedValue, error) { 120 vals := make([]*statedb.VersionedValue, len(keys)) 121 for i, key := range keys { 122 val, err := vdb.GetState(namespace, key) 123 if err != nil { 124 return nil, err 125 } 126 vals[i] = val 127 } 128 return vals, nil 129 } 130 131 // GetStateRangeScanIterator implements method in VersionedDB interface 132 // startKey is inclusive 133 // endKey is exclusive 134 func (vdb *versionedDB) GetStateRangeScanIterator(namespace string, startKey string, endKey string) (statedb.ResultsIterator, error) { 135 // pageSize = 0 denotes unlimited page size 136 return vdb.GetStateRangeScanIteratorWithPagination(namespace, startKey, endKey, 0) 137 } 138 139 // GetStateRangeScanIteratorWithPagination implements method in VersionedDB interface 140 func (vdb *versionedDB) GetStateRangeScanIteratorWithPagination(namespace string, startKey string, endKey string, pageSize int32) (statedb.QueryResultsIterator, error) { 141 dataStartKey := encodeDataKey(namespace, startKey) 142 dataEndKey := encodeDataKey(namespace, endKey) 143 if endKey == "" { 144 dataEndKey[len(dataEndKey)-1] = lastKeyIndicator 145 } 146 dbItr, err := vdb.db.GetIterator(dataStartKey, dataEndKey) 147 if err != nil { 148 return nil, err 149 } 150 return newKVScanner(namespace, dbItr, pageSize), nil 151 } 152 153 // ExecuteQuery implements method in VersionedDB interface 154 func (vdb *versionedDB) ExecuteQuery(namespace, query string) (statedb.ResultsIterator, error) { 155 return nil, errors.New("ExecuteQuery not supported for leveldb") 156 } 157 158 // ExecuteQueryWithPagination implements method in VersionedDB interface 159 func (vdb *versionedDB) ExecuteQueryWithPagination(namespace, query, bookmark string, pageSize int32) (statedb.QueryResultsIterator, error) { 160 return nil, errors.New("ExecuteQueryWithMetadata not supported for leveldb") 161 } 162 163 // ApplyUpdates implements method in VersionedDB interface 164 func (vdb *versionedDB) ApplyUpdates(batch *statedb.UpdateBatch, height *version.Height) error { 165 dbBatch := vdb.db.NewUpdateBatch() 166 namespaces := batch.GetUpdatedNamespaces() 167 for _, ns := range namespaces { 168 updates := batch.GetUpdates(ns) 169 for k, vv := range updates { 170 dataKey := encodeDataKey(ns, k) 171 logger.Debugf("Channel [%s]: Applying key(string)=[%s] key(bytes)=[%#v]", vdb.dbName, string(dataKey), dataKey) 172 173 if vv.Value == nil { 174 dbBatch.Delete(dataKey) 175 } else { 176 encodedVal, err := encodeValue(vv) 177 if err != nil { 178 return err 179 } 180 dbBatch.Put(dataKey, encodedVal) 181 } 182 } 183 } 184 // Record a savepoint at a given height 185 // If a given height is nil, it denotes that we are committing pvt data of old blocks. 186 // In this case, we should not store a savepoint for recovery. The lastUpdatedOldBlockList 187 // in the pvtstore acts as a savepoint for pvt data. 188 if height != nil { 189 dbBatch.Put(savePointKey, height.ToBytes()) 190 } 191 // Setting snyc to true as a precaution, false may be an ok optimization after further testing. 192 if err := vdb.db.WriteBatch(dbBatch, true); err != nil { 193 return err 194 } 195 return nil 196 } 197 198 // GetLatestSavePoint implements method in VersionedDB interface 199 func (vdb *versionedDB) GetLatestSavePoint() (*version.Height, error) { 200 versionBytes, err := vdb.db.Get(savePointKey) 201 if err != nil { 202 return nil, err 203 } 204 if versionBytes == nil { 205 return nil, nil 206 } 207 version, _, err := version.NewHeightFromBytes(versionBytes) 208 if err != nil { 209 return nil, err 210 } 211 return version, nil 212 } 213 214 // GetFullScanIterator implements method in VersionedDB interface. This function returns a 215 // FullScanIterator that can be used to iterate over entire data in the statedb for a channel. 216 // `skipNamespace` parameter can be used to control if the consumer wants the FullScanIterator 217 // to skip one or more namespaces from the returned results. The intended use of this iterator 218 // is to generate the snapshot files for the stateleveldb 219 func (vdb *versionedDB) GetFullScanIterator(skipNamespace func(string) bool) (statedb.FullScanIterator, byte, error) { 220 return newFullDBScanner(vdb.db, skipNamespace) 221 } 222 223 func encodeDataKey(ns, key string) []byte { 224 k := append(dataKeyPrefix, []byte(ns)...) 225 k = append(k, nsKeySep...) 226 return append(k, []byte(key)...) 227 } 228 229 func decodeDataKey(encodedDataKey []byte) (string, string) { 230 split := bytes.SplitN(encodedDataKey, nsKeySep, 2) 231 return string(split[0][1:]), string(split[1]) 232 } 233 234 func dataKeyStarterForNextNamespace(ns string) []byte { 235 k := append(dataKeyPrefix, []byte(ns)...) 236 return append(k, lastKeyIndicator) 237 } 238 239 type kvScanner struct { 240 namespace string 241 dbItr iterator.Iterator 242 requestedLimit int32 243 totalRecordsReturned int32 244 } 245 246 func newKVScanner(namespace string, dbItr iterator.Iterator, requestedLimit int32) *kvScanner { 247 return &kvScanner{namespace, dbItr, requestedLimit, 0} 248 } 249 250 func (scanner *kvScanner) Next() (statedb.QueryResult, error) { 251 if scanner.requestedLimit > 0 && scanner.totalRecordsReturned >= scanner.requestedLimit { 252 return nil, nil 253 } 254 if !scanner.dbItr.Next() { 255 return nil, nil 256 } 257 258 dbKey := scanner.dbItr.Key() 259 dbVal := scanner.dbItr.Value() 260 dbValCopy := make([]byte, len(dbVal)) 261 copy(dbValCopy, dbVal) 262 _, key := decodeDataKey(dbKey) 263 vv, err := decodeValue(dbValCopy) 264 if err != nil { 265 return nil, err 266 } 267 268 scanner.totalRecordsReturned++ 269 return &statedb.VersionedKV{ 270 CompositeKey: statedb.CompositeKey{Namespace: scanner.namespace, Key: key}, 271 // TODO remove dereferrencing below by changing the type of the field 272 // `VersionedValue` in `statedb.VersionedKV` to a pointer 273 VersionedValue: *vv}, nil 274 } 275 276 func (scanner *kvScanner) Close() { 277 scanner.dbItr.Release() 278 } 279 280 func (scanner *kvScanner) GetBookmarkAndClose() string { 281 retval := "" 282 if scanner.dbItr.Next() { 283 dbKey := scanner.dbItr.Key() 284 _, key := decodeDataKey(dbKey) 285 retval = key 286 } 287 scanner.Close() 288 return retval 289 } 290 291 type fullDBScanner struct { 292 db *leveldbhelper.DBHandle 293 dbItr iterator.Iterator 294 toSkip func(namespace string) bool 295 } 296 297 func newFullDBScanner(db *leveldbhelper.DBHandle, skipNamespace func(namespace string) bool) (*fullDBScanner, byte, error) { 298 dbItr, err := db.GetIterator(dataKeyPrefix, dataKeyStopper) 299 if err != nil { 300 return nil, byte(0), err 301 } 302 return &fullDBScanner{ 303 db: db, 304 dbItr: dbItr, 305 toSkip: skipNamespace, 306 }, 307 fullScanIteratorValueFormat, 308 nil 309 } 310 311 // Next returns the key-values in the lexical order of <Namespace, key> 312 // The bytes returned for the <version, value, metadata> are the same as they are stored in the leveldb. 313 // Since, the primary intended use of this function is to generate the snapshot files for the statedb, the same 314 // bytes can be consumed back as is. Hence, we do not decode or transform these bytes for the efficiency 315 func (s *fullDBScanner) Next() (*statedb.CompositeKey, []byte, error) { 316 for s.dbItr.Next() { 317 dbKey := s.dbItr.Key() 318 dbVal := s.dbItr.Value() 319 ns, key := decodeDataKey(dbKey) 320 compositeKey := &statedb.CompositeKey{ 321 Namespace: ns, 322 Key: key, 323 } 324 325 switch { 326 case !s.toSkip(ns): 327 return compositeKey, dbVal, nil 328 default: 329 s.dbItr.Seek(dataKeyStarterForNextNamespace(ns)) 330 s.dbItr.Prev() 331 } 332 } 333 return nil, nil, errors.Wrap(s.dbItr.Error(), "internal leveldb error while retrieving data from db iterator") 334 } 335 336 func (s *fullDBScanner) Close() { 337 if s == nil { 338 return 339 } 340 s.dbItr.Release() 341 }