github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/kvledger/txmgmt/statedb/stateleveldb/stateleveldb.go (about) 1 /* 2 Copyright hechain. 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/hechain20/hechain/common/flogging" 13 "github.com/hechain20/hechain/common/ledger/dataformat" 14 "github.com/hechain20/hechain/common/ledger/util/leveldbhelper" 15 "github.com/hechain20/hechain/core/ledger/internal/version" 16 "github.com/hechain20/hechain/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 maxDataImportBatchSize = 4 * 1024 * 1024 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 // ImportFromSnapshot loads the public state and pvtdata hashes from the snapshot files previously generated 57 func (provider *VersionedDBProvider) ImportFromSnapshot( 58 dbName string, 59 savepoint *version.Height, 60 itr statedb.FullScanIterator, 61 ) error { 62 vdb := newVersionedDB(provider.dbProvider.GetDBHandle(dbName), dbName) 63 return vdb.importState(itr, savepoint) 64 } 65 66 // BytesKeySupported returns true if a db created supports bytes as a key 67 func (provider *VersionedDBProvider) BytesKeySupported() bool { 68 return true 69 } 70 71 // Close closes the underlying db 72 func (provider *VersionedDBProvider) Close() { 73 provider.dbProvider.Close() 74 } 75 76 // Drop drops channel-specific data from the state leveldb. 77 // It is not an error if a database does not exist. 78 func (provider *VersionedDBProvider) Drop(dbName string) error { 79 return provider.dbProvider.Drop(dbName) 80 } 81 82 // VersionedDB implements VersionedDB interface 83 type versionedDB struct { 84 db *leveldbhelper.DBHandle 85 dbName string 86 } 87 88 // newVersionedDB constructs an instance of VersionedDB 89 func newVersionedDB(db *leveldbhelper.DBHandle, dbName string) *versionedDB { 90 return &versionedDB{db, dbName} 91 } 92 93 // Open implements method in VersionedDB interface 94 func (vdb *versionedDB) Open() error { 95 // do nothing because shared db is used 96 return nil 97 } 98 99 // Close implements method in VersionedDB interface 100 func (vdb *versionedDB) Close() { 101 // do nothing because shared db is used 102 } 103 104 // ValidateKeyValue implements method in VersionedDB interface 105 func (vdb *versionedDB) ValidateKeyValue(key string, value []byte) error { 106 return nil 107 } 108 109 // BytesKeySupported implements method in VersionedDB interface 110 func (vdb *versionedDB) BytesKeySupported() bool { 111 return true 112 } 113 114 // GetState implements method in VersionedDB interface 115 func (vdb *versionedDB) GetState(namespace string, key string) (*statedb.VersionedValue, error) { 116 logger.Debugf("GetState(). ns=%s, key=%s", namespace, key) 117 dbVal, err := vdb.db.Get(encodeDataKey(namespace, key)) 118 if err != nil { 119 return nil, err 120 } 121 if dbVal == nil { 122 return nil, nil 123 } 124 return decodeValue(dbVal) 125 } 126 127 // GetVersion implements method in VersionedDB interface 128 func (vdb *versionedDB) GetVersion(namespace string, key string) (*version.Height, error) { 129 versionedValue, err := vdb.GetState(namespace, key) 130 if err != nil { 131 return nil, err 132 } 133 if versionedValue == nil { 134 return nil, nil 135 } 136 return versionedValue.Version, nil 137 } 138 139 // GetStateMultipleKeys implements method in VersionedDB interface 140 func (vdb *versionedDB) GetStateMultipleKeys(namespace string, keys []string) ([]*statedb.VersionedValue, error) { 141 vals := make([]*statedb.VersionedValue, len(keys)) 142 for i, key := range keys { 143 val, err := vdb.GetState(namespace, key) 144 if err != nil { 145 return nil, err 146 } 147 vals[i] = val 148 } 149 return vals, nil 150 } 151 152 // GetStateRangeScanIterator implements method in VersionedDB interface 153 // startKey is inclusive 154 // endKey is exclusive 155 func (vdb *versionedDB) GetStateRangeScanIterator(namespace string, startKey string, endKey string) (statedb.ResultsIterator, error) { 156 // pageSize = 0 denotes unlimited page size 157 return vdb.GetStateRangeScanIteratorWithPagination(namespace, startKey, endKey, 0) 158 } 159 160 // GetStateRangeScanIteratorWithPagination implements method in VersionedDB interface 161 func (vdb *versionedDB) GetStateRangeScanIteratorWithPagination(namespace string, startKey string, endKey string, pageSize int32) (statedb.QueryResultsIterator, error) { 162 dataStartKey := encodeDataKey(namespace, startKey) 163 dataEndKey := encodeDataKey(namespace, endKey) 164 if endKey == "" { 165 dataEndKey[len(dataEndKey)-1] = lastKeyIndicator 166 } 167 dbItr, err := vdb.db.GetIterator(dataStartKey, dataEndKey) 168 if err != nil { 169 return nil, err 170 } 171 return newKVScanner(namespace, dbItr, pageSize), nil 172 } 173 174 // ExecuteQuery implements method in VersionedDB interface 175 func (vdb *versionedDB) ExecuteQuery(namespace, query string) (statedb.ResultsIterator, error) { 176 return nil, errors.New("ExecuteQuery not supported for leveldb") 177 } 178 179 // ExecuteQueryWithPagination implements method in VersionedDB interface 180 func (vdb *versionedDB) ExecuteQueryWithPagination(namespace, query, bookmark string, pageSize int32) (statedb.QueryResultsIterator, error) { 181 return nil, errors.New("ExecuteQueryWithMetadata not supported for leveldb") 182 } 183 184 // ApplyUpdates implements method in VersionedDB interface 185 func (vdb *versionedDB) ApplyUpdates(batch *statedb.UpdateBatch, height *version.Height) error { 186 dbBatch := vdb.db.NewUpdateBatch() 187 namespaces := batch.GetUpdatedNamespaces() 188 for _, ns := range namespaces { 189 updates := batch.GetUpdates(ns) 190 for k, vv := range updates { 191 dataKey := encodeDataKey(ns, k) 192 logger.Debugf("Channel [%s]: Applying key(string)=[%s] key(bytes)=[%#v]", vdb.dbName, string(dataKey), dataKey) 193 194 if vv.Value == nil { 195 dbBatch.Delete(dataKey) 196 } else { 197 encodedVal, err := encodeValue(vv) 198 if err != nil { 199 return err 200 } 201 dbBatch.Put(dataKey, encodedVal) 202 } 203 } 204 } 205 // Record a savepoint at a given height 206 // If a given height is nil, it denotes that we are committing pvt data of old blocks. 207 // In this case, we should not store a savepoint for recovery. The lastUpdatedOldBlockList 208 // in the pvtstore acts as a savepoint for pvt data. 209 if height != nil { 210 dbBatch.Put(savePointKey, height.ToBytes()) 211 } 212 // Setting snyc to true as a precaution, false may be an ok optimization after further testing. 213 return vdb.db.WriteBatch(dbBatch, true) 214 } 215 216 // GetLatestSavePoint implements method in VersionedDB interface 217 func (vdb *versionedDB) GetLatestSavePoint() (*version.Height, error) { 218 versionBytes, err := vdb.db.Get(savePointKey) 219 if err != nil { 220 return nil, err 221 } 222 if versionBytes == nil { 223 return nil, nil 224 } 225 version, _, err := version.NewHeightFromBytes(versionBytes) 226 if err != nil { 227 return nil, err 228 } 229 return version, nil 230 } 231 232 // GetFullScanIterator implements method in VersionedDB interface. This function returns a 233 // FullScanIterator that can be used to iterate over entire data in the statedb for a channel. 234 // `skipNamespace` parameter can be used to control if the consumer wants the FullScanIterator 235 // to skip one or more namespaces from the returned results. The intended use of this iterator 236 // is to generate the snapshot files for the stateleveldb 237 func (vdb *versionedDB) GetFullScanIterator(skipNamespace func(string) bool) (statedb.FullScanIterator, error) { 238 return newFullDBScanner(vdb.db, skipNamespace) 239 } 240 241 // importState implements method in VersionedDB interface. The function is expected to be used 242 // for importing the state from a previously snapshotted state. The parameter itr provides access to 243 // the snapshotted state. 244 func (vdb *versionedDB) importState(itr statedb.FullScanIterator, savepoint *version.Height) error { 245 if itr == nil { 246 return vdb.db.Put(savePointKey, savepoint.ToBytes(), true) 247 } 248 dbBatch := vdb.db.NewUpdateBatch() 249 batchSize := 0 250 for { 251 versionedKV, err := itr.Next() 252 if err != nil { 253 return err 254 } 255 if versionedKV == nil { 256 break 257 } 258 dbKey := encodeDataKey(versionedKV.Namespace, versionedKV.Key) 259 dbValue, err := encodeValue(versionedKV.VersionedValue) 260 if err != nil { 261 return err 262 } 263 batchSize += len(dbKey) + len(dbValue) 264 dbBatch.Put(dbKey, dbValue) 265 if batchSize >= maxDataImportBatchSize { 266 if err := vdb.db.WriteBatch(dbBatch, true); err != nil { 267 return err 268 } 269 batchSize = 0 270 dbBatch.Reset() 271 } 272 } 273 dbBatch.Put(savePointKey, savepoint.ToBytes()) 274 return vdb.db.WriteBatch(dbBatch, true) 275 } 276 277 // IsEmpty return true if the statedb does not have any content 278 func (vdb *versionedDB) IsEmpty() (bool, error) { 279 return vdb.db.IsEmpty() 280 } 281 282 func encodeDataKey(ns, key string) []byte { 283 k := append(dataKeyPrefix, []byte(ns)...) 284 k = append(k, nsKeySep...) 285 return append(k, []byte(key)...) 286 } 287 288 func decodeDataKey(encodedDataKey []byte) (string, string) { 289 split := bytes.SplitN(encodedDataKey, nsKeySep, 2) 290 return string(split[0][1:]), string(split[1]) 291 } 292 293 func dataKeyStarterForNextNamespace(ns string) []byte { 294 k := append(dataKeyPrefix, []byte(ns)...) 295 return append(k, lastKeyIndicator) 296 } 297 298 type kvScanner struct { 299 namespace string 300 dbItr iterator.Iterator 301 requestedLimit int32 302 totalRecordsReturned int32 303 } 304 305 func newKVScanner(namespace string, dbItr iterator.Iterator, requestedLimit int32) *kvScanner { 306 return &kvScanner{namespace, dbItr, requestedLimit, 0} 307 } 308 309 func (scanner *kvScanner) Next() (*statedb.VersionedKV, error) { 310 if scanner.requestedLimit > 0 && scanner.totalRecordsReturned >= scanner.requestedLimit { 311 return nil, nil 312 } 313 if !scanner.dbItr.Next() { 314 return nil, nil 315 } 316 317 dbKey := scanner.dbItr.Key() 318 dbVal := scanner.dbItr.Value() 319 dbValCopy := make([]byte, len(dbVal)) 320 copy(dbValCopy, dbVal) 321 _, key := decodeDataKey(dbKey) 322 vv, err := decodeValue(dbValCopy) 323 if err != nil { 324 return nil, err 325 } 326 327 scanner.totalRecordsReturned++ 328 return &statedb.VersionedKV{ 329 CompositeKey: &statedb.CompositeKey{ 330 Namespace: scanner.namespace, 331 Key: key, 332 }, 333 VersionedValue: vv, 334 }, nil 335 } 336 337 func (scanner *kvScanner) Close() { 338 scanner.dbItr.Release() 339 } 340 341 func (scanner *kvScanner) GetBookmarkAndClose() string { 342 retval := "" 343 if scanner.dbItr.Next() { 344 dbKey := scanner.dbItr.Key() 345 _, key := decodeDataKey(dbKey) 346 retval = key 347 } 348 scanner.Close() 349 return retval 350 } 351 352 type fullDBScanner struct { 353 db *leveldbhelper.DBHandle 354 dbItr iterator.Iterator 355 toSkip func(namespace string) bool 356 } 357 358 func newFullDBScanner(db *leveldbhelper.DBHandle, skipNamespace func(namespace string) bool) (*fullDBScanner, error) { 359 dbItr, err := db.GetIterator(dataKeyPrefix, dataKeyStopper) 360 if err != nil { 361 return nil, err 362 } 363 return &fullDBScanner{ 364 db: db, 365 dbItr: dbItr, 366 toSkip: skipNamespace, 367 }, 368 nil 369 } 370 371 // Next returns the key-values in the lexical order of <Namespace, key> 372 func (s *fullDBScanner) Next() (*statedb.VersionedKV, error) { 373 for s.dbItr.Next() { 374 ns, key := decodeDataKey(s.dbItr.Key()) 375 compositeKey := &statedb.CompositeKey{ 376 Namespace: ns, 377 Key: key, 378 } 379 380 versionedVal, err := decodeValue(s.dbItr.Value()) 381 if err != nil { 382 return nil, err 383 } 384 385 switch { 386 case !s.toSkip(ns): 387 return &statedb.VersionedKV{ 388 CompositeKey: compositeKey, 389 VersionedValue: versionedVal, 390 }, nil 391 default: 392 s.dbItr.Seek(dataKeyStarterForNextNamespace(ns)) 393 s.dbItr.Prev() 394 } 395 } 396 return nil, errors.Wrap(s.dbItr.Error(), "internal leveldb error while retrieving data from db iterator") 397 } 398 399 func (s *fullDBScanner) Close() { 400 if s == nil { 401 return 402 } 403 s.dbItr.Release() 404 }