github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/kvledger/txmgmt/statedb/statecouchdb/statecouchdb.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package statecouchdb 8 9 import ( 10 "bytes" 11 "context" 12 "encoding/json" 13 "sort" 14 "sync" 15 16 "github.com/hechain20/hechain/common/flogging" 17 "github.com/hechain20/hechain/common/ledger/dataformat" 18 "github.com/hechain20/hechain/common/metrics" 19 "github.com/hechain20/hechain/core/ledger" 20 "github.com/hechain20/hechain/core/ledger/internal/version" 21 "github.com/hechain20/hechain/core/ledger/kvledger/txmgmt/statedb" 22 "github.com/pkg/errors" 23 ) 24 25 var logger = flogging.MustGetLogger("statecouchdb") 26 27 const ( 28 // savepointDocID is used as a key for maintaining savepoint (maintained in metadatadb for a channel) 29 savepointDocID = "statedb_savepoint" 30 // channelMetadataDocID is used as a key to store the channel metadata for a channel (maintained in the channel's metadatadb). 31 // Due to CouchDB's length restriction on db names, channel names and namepsaces may be truncated in db names. 32 // The metadata is used for dropping channel-specific databases and snapshot support. 33 channelMetadataDocID = "channel_metadata" 34 // fabricInternalDBName is used to create a db in couch that would be used for internal data such as the version of the data format 35 // a double underscore ensures that the dbname does not clash with the dbnames created for the chaincodes 36 fabricInternalDBName = "fabric__internal" 37 // dataformatVersionDocID is used as a key for maintaining version of the data format (maintained in fabric internal db) 38 dataformatVersionDocID = "dataformatVersion" 39 ) 40 41 var maxDataImportBatchMemorySize = 2 * 1024 * 1024 42 43 // VersionedDBProvider implements interface VersionedDBProvider 44 type VersionedDBProvider struct { 45 couchInstance *couchInstance 46 databases map[string]*VersionedDB 47 mux sync.Mutex 48 openCounts uint64 49 redoLoggerProvider *redoLoggerProvider 50 cache *cache 51 } 52 53 // NewVersionedDBProvider instantiates VersionedDBProvider 54 func NewVersionedDBProvider(config *ledger.CouchDBConfig, metricsProvider metrics.Provider, sysNamespaces []string) (*VersionedDBProvider, error) { 55 logger.Debugf("constructing CouchDB VersionedDBProvider") 56 couchInstance, err := createCouchInstance(config, metricsProvider) 57 if err != nil { 58 return nil, err 59 } 60 if err := checkExpectedDataformatVersion(couchInstance); err != nil { 61 return nil, err 62 } 63 p, err := newRedoLoggerProvider(config.RedoLogPath) 64 if err != nil { 65 return nil, err 66 } 67 68 cache := newCache(config.UserCacheSizeMBs, sysNamespaces) 69 return &VersionedDBProvider{ 70 couchInstance: couchInstance, 71 databases: make(map[string]*VersionedDB), 72 mux: sync.Mutex{}, 73 openCounts: 0, 74 redoLoggerProvider: p, 75 cache: cache, 76 }, 77 nil 78 } 79 80 func checkExpectedDataformatVersion(couchInstance *couchInstance) error { 81 databasesToIgnore := []string{fabricInternalDBName} 82 isEmpty, err := couchInstance.isEmpty(databasesToIgnore) 83 if err != nil { 84 return err 85 } 86 if isEmpty { 87 logger.Debugf("couch instance is empty. Setting dataformat version to %s", dataformat.CurrentFormat) 88 return writeDataFormatVersion(couchInstance, dataformat.CurrentFormat) 89 } 90 dataformatVersion, err := readDataformatVersion(couchInstance) 91 if err != nil { 92 return err 93 } 94 if dataformatVersion != dataformat.CurrentFormat { 95 return &dataformat.ErrFormatMismatch{ 96 DBInfo: "CouchDB for state database", 97 ExpectedFormat: dataformat.CurrentFormat, 98 Format: dataformatVersion, 99 } 100 } 101 return nil 102 } 103 104 func readDataformatVersion(couchInstance *couchInstance) (string, error) { 105 db, err := createCouchDatabase(couchInstance, fabricInternalDBName) 106 if err != nil { 107 return "", err 108 } 109 doc, _, err := db.readDoc(dataformatVersionDocID) 110 if err != nil || doc == nil { 111 return "", err 112 } 113 return decodeDataformatInfo(doc) 114 } 115 116 func writeDataFormatVersion(couchInstance *couchInstance, dataformatVersion string) error { 117 db, err := createCouchDatabase(couchInstance, fabricInternalDBName) 118 if err != nil { 119 return err 120 } 121 doc, err := encodeDataformatInfo(dataformatVersion) 122 if err != nil { 123 return err 124 } 125 _, err = db.saveDoc(dataformatVersionDocID, "", doc) 126 return err 127 } 128 129 // GetDBHandle gets the handle to a named database 130 func (provider *VersionedDBProvider) GetDBHandle(dbName string, nsProvider statedb.NamespaceProvider) (statedb.VersionedDB, error) { 131 provider.mux.Lock() 132 defer provider.mux.Unlock() 133 vdb := provider.databases[dbName] 134 if vdb != nil { 135 return vdb, nil 136 } 137 138 var err error 139 vdb, err = newVersionedDB( 140 provider.couchInstance, 141 provider.redoLoggerProvider.newRedoLogger(dbName), 142 dbName, 143 provider.cache, 144 nsProvider, 145 ) 146 if err != nil { 147 return nil, err 148 } 149 provider.databases[dbName] = vdb 150 return vdb, nil 151 } 152 153 // ImportFromSnapshot loads the public state and pvtdata hashes from the snapshot files previously generated 154 func (provider *VersionedDBProvider) ImportFromSnapshot( 155 dbName string, 156 savepoint *version.Height, 157 itr statedb.FullScanIterator, 158 ) error { 159 metadataDB, err := createCouchDatabase(provider.couchInstance, constructMetadataDBName(dbName)) 160 if err != nil { 161 return errors.WithMessagef(err, "error while creating the metadata database for channel %s", dbName) 162 } 163 164 vdb := &VersionedDB{ 165 chainName: dbName, 166 couchInstance: provider.couchInstance, 167 metadataDB: metadataDB, 168 channelMetadata: &channelMetadata{ 169 ChannelName: dbName, 170 NamespaceDBsInfo: make(map[string]*namespaceDBInfo), 171 }, 172 namespaceDBs: make(map[string]*couchDatabase), 173 } 174 if err := vdb.writeChannelMetadata(); err != nil { 175 return errors.WithMessage(err, "error while writing channel metadata") 176 } 177 178 s := &snapshotImporter{ 179 vdb: vdb, 180 itr: itr, 181 } 182 if err := s.importState(); err != nil { 183 return err 184 } 185 186 return vdb.recordSavepoint(savepoint) 187 } 188 189 // BytesKeySupported returns true if a db created supports bytes as a key 190 func (provider *VersionedDBProvider) BytesKeySupported() bool { 191 return false 192 } 193 194 // Close closes the underlying db instance 195 func (provider *VersionedDBProvider) Close() { 196 // No close needed on Couch 197 provider.redoLoggerProvider.close() 198 } 199 200 // Drop drops the couch dbs and redologger data for the channel. 201 // It is not an error if a database does not exist. 202 func (provider *VersionedDBProvider) Drop(dbName string) error { 203 metadataDBName := constructMetadataDBName(dbName) 204 couchDBDatabase := couchDatabase{couchInstance: provider.couchInstance, dbName: metadataDBName} 205 _, couchDBReturn, err := couchDBDatabase.getDatabaseInfo() 206 if couchDBReturn != nil && couchDBReturn.StatusCode == 404 { 207 // db does not exist 208 return nil 209 } 210 if err != nil { 211 return err 212 } 213 214 metadataDB, err := createCouchDatabase(provider.couchInstance, metadataDBName) 215 if err != nil { 216 return err 217 } 218 channelMetadata, err := readChannelMetadata(metadataDB) 219 if err != nil { 220 return err 221 } 222 223 for _, dbInfo := range channelMetadata.NamespaceDBsInfo { 224 // do not drop metadataDB until all other dbs are dropped 225 if dbInfo.DBName == metadataDBName { 226 continue 227 } 228 if err := dropDB(provider.couchInstance, dbInfo.DBName); err != nil { 229 logger.Errorw("Error dropping database", "channel", dbName, "namespace", dbInfo.Namespace, "error", err) 230 return err 231 } 232 } 233 if err := dropDB(provider.couchInstance, metadataDBName); err != nil { 234 logger.Errorw("Error dropping metatdataDB", "channel", dbName, "error", err) 235 return err 236 } 237 238 delete(provider.databases, dbName) 239 240 return provider.redoLoggerProvider.leveldbProvider.Drop(dbName) 241 } 242 243 // HealthCheck checks to see if the couch instance of the peer is healthy 244 func (provider *VersionedDBProvider) HealthCheck(ctx context.Context) error { 245 return provider.couchInstance.healthCheck(ctx) 246 } 247 248 // VersionedDB implements VersionedDB interface 249 type VersionedDB struct { 250 couchInstance *couchInstance 251 metadataDB *couchDatabase // A database per channel to store metadata such as savepoint. 252 chainName string // The name of the chain/channel. 253 namespaceDBs map[string]*couchDatabase // One database per namespace. 254 channelMetadata *channelMetadata // Store channel name and namespaceDBInfo 255 committedDataCache *versionsCache // Used as a local cache during bulk processing of a block. 256 verCacheLock sync.RWMutex 257 mux sync.RWMutex 258 redoLogger *redoLogger 259 cache *cache 260 } 261 262 // newVersionedDB constructs an instance of VersionedDB 263 func newVersionedDB(couchInstance *couchInstance, redoLogger *redoLogger, dbName string, cache *cache, nsProvider statedb.NamespaceProvider) (*VersionedDB, error) { 264 // CreateCouchDatabase creates a CouchDB database object, as well as the underlying database if it does not exist 265 chainName := dbName 266 dbName = constructMetadataDBName(dbName) 267 268 metadataDB, err := createCouchDatabase(couchInstance, dbName) 269 if err != nil { 270 return nil, err 271 } 272 namespaceDBMap := make(map[string]*couchDatabase) 273 vdb := &VersionedDB{ 274 couchInstance: couchInstance, 275 metadataDB: metadataDB, 276 chainName: chainName, 277 namespaceDBs: namespaceDBMap, 278 committedDataCache: newVersionCache(), 279 redoLogger: redoLogger, 280 cache: cache, 281 } 282 283 logger.Debugf("chain [%s]: checking for redolog record", chainName) 284 redologRecord, err := redoLogger.load() 285 if err != nil { 286 return nil, err 287 } 288 savepoint, err := vdb.GetLatestSavePoint() 289 if err != nil { 290 return nil, err 291 } 292 293 isNewDB := savepoint == nil 294 if err = vdb.initChannelMetadata(isNewDB, nsProvider); err != nil { 295 return nil, err 296 } 297 298 // in normal circumstances, redolog is expected to be either equal to the last block 299 // committed to the statedb or one ahead (in the event of a crash). However, either of 300 // these or both could be nil on first time start (fresh start/rebuild) 301 if redologRecord == nil || savepoint == nil { 302 logger.Debugf("chain [%s]: No redo-record or save point present", chainName) 303 return vdb, nil 304 } 305 306 logger.Debugf("chain [%s]: save point = %#v, version of redolog record = %#v", 307 chainName, savepoint, redologRecord.Version) 308 309 if redologRecord.Version.BlockNum-savepoint.BlockNum == 1 { 310 logger.Debugf("chain [%s]: Re-applying last batch", chainName) 311 if err := vdb.applyUpdates(redologRecord.UpdateBatch, redologRecord.Version); err != nil { 312 return nil, err 313 } 314 } 315 return vdb, nil 316 } 317 318 // getNamespaceDBHandle gets the handle to a named chaincode database 319 func (vdb *VersionedDB) getNamespaceDBHandle(namespace string) (*couchDatabase, error) { 320 vdb.mux.RLock() 321 db := vdb.namespaceDBs[namespace] 322 vdb.mux.RUnlock() 323 if db != nil { 324 return db, nil 325 } 326 namespaceDBName := constructNamespaceDBName(vdb.chainName, namespace) 327 vdb.mux.Lock() 328 defer vdb.mux.Unlock() 329 330 db = vdb.namespaceDBs[namespace] 331 if db != nil { 332 return db, nil 333 } 334 335 var err error 336 if _, ok := vdb.channelMetadata.NamespaceDBsInfo[namespace]; !ok { 337 logger.Debugf("[%s] add namespaceDBInfo for namespace %s", vdb.chainName, namespace) 338 vdb.channelMetadata.NamespaceDBsInfo[namespace] = &namespaceDBInfo{ 339 Namespace: namespace, 340 DBName: namespaceDBName, 341 } 342 if err = vdb.writeChannelMetadata(); err != nil { 343 return nil, err 344 } 345 } 346 db, err = createCouchDatabase(vdb.couchInstance, namespaceDBName) 347 if err != nil { 348 return nil, err 349 } 350 vdb.namespaceDBs[namespace] = db 351 return db, nil 352 } 353 354 // ProcessIndexesForChaincodeDeploy creates indexes for a specified namespace 355 func (vdb *VersionedDB) ProcessIndexesForChaincodeDeploy(namespace string, indexFilesData map[string][]byte) error { 356 db, err := vdb.getNamespaceDBHandle(namespace) 357 if err != nil { 358 return err 359 } 360 // We need to satisfy two requirements while processing the index files. 361 // R1: all valid indexes should be processed. 362 // R2: the order of index creation must be the same in all peers. For example, if user 363 // passes two index files with the same index name but different index fields and we 364 // process these files in different orders in different peers, each peer would 365 // have different indexes (as one index definion could replace another if the index names 366 // are the same). 367 // To satisfy R1, we log the error and continue to process the next index file. 368 // To satisfy R2, we sort the indexFilesData map based on the filenames and process 369 // each index as per the sorted order. 370 var indexFilesName []string 371 for fileName := range indexFilesData { 372 indexFilesName = append(indexFilesName, fileName) 373 } 374 sort.Strings(indexFilesName) 375 for _, fileName := range indexFilesName { 376 _, err = db.createIndex(string(indexFilesData[fileName])) 377 switch { 378 case err != nil: 379 logger.Errorf("error creating index from file [%s] for chaincode [%s] on channel [%s]: %+v", 380 fileName, namespace, vdb.chainName, err) 381 default: 382 logger.Infof("successfully submitted index creation request present in the file [%s] for chaincode [%s] on channel [%s]", 383 fileName, namespace, vdb.chainName) 384 } 385 } 386 return nil 387 } 388 389 // GetDBType returns the hosted stateDB 390 func (vdb *VersionedDB) GetDBType() string { 391 return "couchdb" 392 } 393 394 // LoadCommittedVersions populates committedVersions and revisionNumbers into cache. 395 // A bulk retrieve from couchdb is used to populate the cache. 396 // committedVersions cache will be used for state validation of readsets 397 // revisionNumbers cache will be used during commit phase for couchdb bulk updates 398 func (vdb *VersionedDB) LoadCommittedVersions(keys []*statedb.CompositeKey) error { 399 missingKeys := map[string][]string{} 400 committedDataCache := newVersionCache() 401 for _, compositeKey := range keys { 402 ns, key := compositeKey.Namespace, compositeKey.Key 403 committedDataCache.setVerAndRev(ns, key, nil, "") 404 logger.Debugf("Load into version cache: %s~%s", ns, key) 405 406 if !vdb.cache.enabled(ns) { 407 missingKeys[ns] = append(missingKeys[ns], key) 408 continue 409 } 410 cv, err := vdb.cache.getState(vdb.chainName, ns, key) 411 if err != nil { 412 return err 413 } 414 if cv == nil { 415 missingKeys[ns] = append(missingKeys[ns], key) 416 continue 417 } 418 vv, err := constructVersionedValue(cv) 419 if err != nil { 420 return err 421 } 422 rev := string(cv.AdditionalInfo) 423 committedDataCache.setVerAndRev(ns, key, vv.Version, rev) 424 } 425 426 nsMetadataMap, err := vdb.retrieveMetadata(missingKeys) 427 logger.Debugf("missingKeys=%s", missingKeys) 428 logger.Debugf("nsMetadataMap=%v", nsMetadataMap) 429 if err != nil { 430 return err 431 } 432 for ns, nsMetadata := range nsMetadataMap { 433 for _, keyMetadata := range nsMetadata { 434 // TODO - why would version be ever zero if loaded from db? 435 if len(keyMetadata.Version) != 0 { 436 version, _, err := decodeVersionAndMetadata(keyMetadata.Version) 437 if err != nil { 438 return err 439 } 440 committedDataCache.setVerAndRev(ns, keyMetadata.ID, version, keyMetadata.Rev) 441 } 442 } 443 } 444 vdb.verCacheLock.Lock() 445 defer vdb.verCacheLock.Unlock() 446 vdb.committedDataCache = committedDataCache 447 return nil 448 } 449 450 // GetVersion implements method in VersionedDB interface 451 func (vdb *VersionedDB) GetVersion(namespace string, key string) (*version.Height, error) { 452 version, keyFound := vdb.GetCachedVersion(namespace, key) 453 if keyFound { 454 return version, nil 455 } 456 457 // This if block get executed only during simulation because during commit 458 // we always call `LoadCommittedVersions` before calling `GetVersion` 459 vv, err := vdb.GetState(namespace, key) 460 if err != nil || vv == nil { 461 return nil, err 462 } 463 version = vv.Version 464 return version, nil 465 } 466 467 // GetCachedVersion returns version from cache. `LoadCommittedVersions` function populates the cache 468 func (vdb *VersionedDB) GetCachedVersion(namespace string, key string) (*version.Height, bool) { 469 logger.Debugf("Retrieving cached version: %s~%s", key, namespace) 470 vdb.verCacheLock.RLock() 471 defer vdb.verCacheLock.RUnlock() 472 return vdb.committedDataCache.getVersion(namespace, key) 473 } 474 475 // ValidateKeyValue implements method in VersionedDB interface 476 func (vdb *VersionedDB) ValidateKeyValue(key string, value []byte) error { 477 err := validateKey(key) 478 if err != nil { 479 return err 480 } 481 return validateValue(value) 482 } 483 484 // BytesKeySupported implements method in VersionvdbedDB interface 485 func (vdb *VersionedDB) BytesKeySupported() bool { 486 return false 487 } 488 489 // GetState implements method in VersionedDB interface 490 func (vdb *VersionedDB) GetState(namespace string, key string) (*statedb.VersionedValue, error) { 491 logger.Debugf("GetState(). ns=%s, key=%s", namespace, key) 492 493 // (1) read the KV from the cache if available 494 cacheEnabled := vdb.cache.enabled(namespace) 495 if cacheEnabled { 496 cv, err := vdb.cache.getState(vdb.chainName, namespace, key) 497 if err != nil { 498 return nil, err 499 } 500 if cv != nil { 501 vv, err := constructVersionedValue(cv) 502 if err != nil { 503 return nil, err 504 } 505 return vv, nil 506 } 507 } 508 509 // (2) read from the database if cache miss occurs 510 kv, err := vdb.readFromDB(namespace, key) 511 if err != nil { 512 return nil, err 513 } 514 if kv == nil { 515 return nil, nil 516 } 517 518 // (3) if the value is not nil, store in the cache 519 if cacheEnabled { 520 cacheValue := constructCacheValue(kv.VersionedValue, kv.revision) 521 if err := vdb.cache.putState(vdb.chainName, namespace, key, cacheValue); err != nil { 522 return nil, err 523 } 524 } 525 526 return kv.VersionedValue, nil 527 } 528 529 func (vdb *VersionedDB) readFromDB(namespace, key string) (*keyValue, error) { 530 db, err := vdb.getNamespaceDBHandle(namespace) 531 if err != nil { 532 return nil, err 533 } 534 if err := validateKey(key); err != nil { 535 return nil, err 536 } 537 couchDoc, _, err := db.readDoc(key) 538 if err != nil { 539 return nil, err 540 } 541 if couchDoc == nil { 542 return nil, nil 543 } 544 kv, err := couchDocToKeyValue(couchDoc) 545 if err != nil { 546 return nil, err 547 } 548 return kv, nil 549 } 550 551 // GetStateMultipleKeys implements method in VersionedDB interface 552 func (vdb *VersionedDB) GetStateMultipleKeys(namespace string, keys []string) ([]*statedb.VersionedValue, error) { 553 vals := make([]*statedb.VersionedValue, len(keys)) 554 for i, key := range keys { 555 val, err := vdb.GetState(namespace, key) 556 if err != nil { 557 return nil, err 558 } 559 vals[i] = val 560 } 561 return vals, nil 562 } 563 564 // GetStateRangeScanIterator implements method in VersionedDB interface 565 // startKey is inclusive 566 // endKey is exclusive 567 func (vdb *VersionedDB) GetStateRangeScanIterator(namespace string, startKey string, endKey string) (statedb.ResultsIterator, error) { 568 return vdb.GetStateRangeScanIteratorWithPagination(namespace, startKey, endKey, 0) 569 } 570 571 // GetStateRangeScanIteratorWithPagination implements method in VersionedDB interface 572 // startKey is inclusive 573 // endKey is exclusive 574 // pageSize limits the number of results returned 575 func (vdb *VersionedDB) GetStateRangeScanIteratorWithPagination(namespace string, startKey string, endKey string, pageSize int32) (statedb.QueryResultsIterator, error) { 576 logger.Debugf("Entering GetStateRangeScanIteratorWithPagination namespace: %s startKey: %s endKey: %s pageSize: %d", namespace, startKey, endKey, pageSize) 577 internalQueryLimit := vdb.couchInstance.internalQueryLimit() 578 db, err := vdb.getNamespaceDBHandle(namespace) 579 if err != nil { 580 return nil, err 581 } 582 return newQueryScanner(namespace, db, "", internalQueryLimit, pageSize, "", startKey, endKey) 583 } 584 585 func (scanner *queryScanner) getNextStateRangeScanResults() error { 586 queryLimit := scanner.queryDefinition.internalQueryLimit 587 if scanner.paginationInfo.requestedLimit > 0 { 588 moreResultsNeeded := scanner.paginationInfo.requestedLimit - scanner.resultsInfo.totalRecordsReturned 589 if moreResultsNeeded < scanner.queryDefinition.internalQueryLimit { 590 queryLimit = moreResultsNeeded 591 } 592 } 593 queryResult, nextStartKey, err := rangeScanFilterCouchInternalDocs(scanner.db, 594 scanner.queryDefinition.startKey, scanner.queryDefinition.endKey, queryLimit) 595 if err != nil { 596 return err 597 } 598 scanner.resultsInfo.results = queryResult 599 scanner.paginationInfo.cursor = 0 600 if scanner.queryDefinition.endKey == nextStartKey { 601 // as we always set inclusive_end=false to match the behavior of 602 // goleveldb iterator, it is safe to mark the scanner as exhausted 603 scanner.exhausted = true 604 // we still need to update the startKey as it is returned as bookmark 605 } 606 scanner.queryDefinition.startKey = nextStartKey 607 return nil 608 } 609 610 func rangeScanFilterCouchInternalDocs(db *couchDatabase, 611 startKey, endKey string, queryLimit int32, 612 ) ([]*queryResult, string, error) { 613 var finalResults []*queryResult 614 var finalNextStartKey string 615 for { 616 results, nextStartKey, err := db.readDocRange(startKey, endKey, queryLimit) 617 if err != nil { 618 logger.Debugf("Error calling ReadDocRange(): %s\n", err.Error()) 619 return nil, "", err 620 } 621 var filteredResults []*queryResult 622 for _, doc := range results { 623 if !isCouchInternalKey(doc.id) { 624 filteredResults = append(filteredResults, doc) 625 } 626 } 627 628 finalResults = append(finalResults, filteredResults...) 629 finalNextStartKey = nextStartKey 630 queryLimit = int32(len(results) - len(filteredResults)) 631 if queryLimit == 0 || finalNextStartKey == "" { 632 break 633 } 634 startKey = finalNextStartKey 635 } 636 var err error 637 for i := 0; isCouchInternalKey(finalNextStartKey); i++ { 638 _, finalNextStartKey, err = db.readDocRange(finalNextStartKey, endKey, 1) 639 logger.Debugf("i=%d, finalNextStartKey=%s", i, finalNextStartKey) 640 if err != nil { 641 return nil, "", err 642 } 643 } 644 return finalResults, finalNextStartKey, nil 645 } 646 647 func isCouchInternalKey(key string) bool { 648 return len(key) != 0 && key[0] == '_' 649 } 650 651 // ExecuteQuery implements method in VersionedDB interface 652 func (vdb *VersionedDB) ExecuteQuery(namespace, query string) (statedb.ResultsIterator, error) { 653 queryResult, err := vdb.ExecuteQueryWithPagination(namespace, query, "", 0) 654 if err != nil { 655 return nil, err 656 } 657 return queryResult, nil 658 } 659 660 // ExecuteQueryWithPagination implements method in VersionedDB interface 661 func (vdb *VersionedDB) ExecuteQueryWithPagination(namespace, query, bookmark string, pageSize int32) (statedb.QueryResultsIterator, error) { 662 logger.Debugf("Entering ExecuteQueryWithPagination namespace: %s, query: %s, bookmark: %s, pageSize: %d", namespace, query, bookmark, pageSize) 663 internalQueryLimit := vdb.couchInstance.internalQueryLimit() 664 queryString, err := applyAdditionalQueryOptions(query, internalQueryLimit, bookmark) 665 if err != nil { 666 logger.Errorf("Error calling applyAdditionalQueryOptions(): %s", err.Error()) 667 return nil, err 668 } 669 db, err := vdb.getNamespaceDBHandle(namespace) 670 if err != nil { 671 return nil, err 672 } 673 return newQueryScanner(namespace, db, queryString, internalQueryLimit, pageSize, bookmark, "", "") 674 } 675 676 // executeQueryWithBookmark executes a "paging" query with a bookmark, this method allows a 677 // paged query without returning a new query iterator 678 func (scanner *queryScanner) executeQueryWithBookmark() error { 679 queryLimit := scanner.queryDefinition.internalQueryLimit 680 if scanner.paginationInfo.requestedLimit > 0 { 681 if scanner.paginationInfo.requestedLimit-scanner.resultsInfo.totalRecordsReturned < scanner.queryDefinition.internalQueryLimit { 682 queryLimit = scanner.paginationInfo.requestedLimit - scanner.resultsInfo.totalRecordsReturned 683 } 684 } 685 queryString, err := applyAdditionalQueryOptions(scanner.queryDefinition.query, 686 queryLimit, scanner.paginationInfo.bookmark) 687 if err != nil { 688 logger.Debugf("Error calling applyAdditionalQueryOptions(): %s\n", err.Error()) 689 return err 690 } 691 queryResult, bookmark, err := scanner.db.queryDocuments(queryString) 692 if err != nil { 693 logger.Debugf("Error calling QueryDocuments(): %s\n", err.Error()) 694 return err 695 } 696 scanner.resultsInfo.results = queryResult 697 scanner.paginationInfo.bookmark = bookmark 698 scanner.paginationInfo.cursor = 0 699 return nil 700 } 701 702 // ApplyUpdates implements method in VersionedDB interface 703 func (vdb *VersionedDB) ApplyUpdates(updates *statedb.UpdateBatch, height *version.Height) error { 704 if height != nil && updates.ContainsPostOrderWrites { 705 // height is passed nil when committing missing private data for previously committed blocks 706 r := &redoRecord{ 707 UpdateBatch: updates, 708 Version: height, 709 } 710 if err := vdb.redoLogger.persist(r); err != nil { 711 return err 712 } 713 } 714 return vdb.applyUpdates(updates, height) 715 } 716 717 func (vdb *VersionedDB) applyUpdates(updates *statedb.UpdateBatch, height *version.Height) error { 718 // TODO a note about https://jira.hyperledger.org/browse/FAB-8622 719 // The write lock is needed only for the stage 2. 720 721 // stage 1 - buildCommitters builds committers per namespace (per DB). Each committer transforms the 722 // given batch in the form of underlying db and keep it in memory. 723 committers, err := vdb.buildCommitters(updates) 724 if err != nil { 725 return err 726 } 727 728 // stage 2 -- executeCommitter executes each committer to push the changes to the DB 729 if err = vdb.executeCommitter(committers); err != nil { 730 return err 731 } 732 733 // Stgae 3 - postCommitProcessing - flush and record savepoint. 734 namespaces := updates.GetUpdatedNamespaces() 735 if err := vdb.postCommitProcessing(committers, namespaces, height); err != nil { 736 return err 737 } 738 739 return nil 740 } 741 742 func (vdb *VersionedDB) postCommitProcessing(committers []*committer, namespaces []string, height *version.Height) error { 743 var wg sync.WaitGroup 744 745 wg.Add(1) 746 errChan := make(chan error, 1) 747 defer close(errChan) 748 go func() { 749 defer wg.Done() 750 751 cacheUpdates := make(cacheUpdates) 752 for _, c := range committers { 753 if !c.cacheEnabled { 754 continue 755 } 756 cacheUpdates.add(c.namespace, c.cacheKVs) 757 } 758 759 if len(cacheUpdates) == 0 { 760 return 761 } 762 763 // update the cache 764 if err := vdb.cache.UpdateStates(vdb.chainName, cacheUpdates); err != nil { 765 vdb.cache.Reset() 766 errChan <- err 767 } 768 }() 769 770 // Record a savepoint at a given height 771 if err := vdb.recordSavepoint(height); err != nil { 772 logger.Errorf("Error during recordSavepoint: %s", err.Error()) 773 return err 774 } 775 776 wg.Wait() 777 select { 778 case err := <-errChan: 779 return errors.WithStack(err) 780 default: 781 return nil 782 } 783 } 784 785 // ClearCachedVersions clears committedVersions and revisionNumbers 786 func (vdb *VersionedDB) ClearCachedVersions() { 787 logger.Debugf("Clear Cache") 788 vdb.verCacheLock.Lock() 789 defer vdb.verCacheLock.Unlock() 790 vdb.committedDataCache = newVersionCache() 791 } 792 793 // Open implements method in VersionedDB interface 794 func (vdb *VersionedDB) Open() error { 795 // no need to open db since a shared couch instance is used 796 return nil 797 } 798 799 // Close implements method in VersionedDB interface 800 func (vdb *VersionedDB) Close() { 801 // no need to close db since a shared couch instance is used 802 } 803 804 // recordSavepoint records a savepoint in the metadata db for the channel. 805 func (vdb *VersionedDB) recordSavepoint(height *version.Height) error { 806 // If a given height is nil, it denotes that we are committing pvt data of old blocks. 807 // In this case, we should not store a savepoint for recovery. The lastUpdatedOldBlockList 808 // in the pvtstore acts as a savepoint for pvt data. 809 if height == nil { 810 return nil 811 } 812 savepointCouchDoc, err := encodeSavepoint(height) 813 if err != nil { 814 return err 815 } 816 _, err = vdb.metadataDB.saveDoc(savepointDocID, "", savepointCouchDoc) 817 if err != nil { 818 logger.Errorf("Failed to save the savepoint to DB %s", err.Error()) 819 return err 820 } 821 return nil 822 } 823 824 // GetLatestSavePoint implements method in VersionedDB interface 825 func (vdb *VersionedDB) GetLatestSavePoint() (*version.Height, error) { 826 var err error 827 couchDoc, _, err := vdb.metadataDB.readDoc(savepointDocID) 828 if err != nil { 829 logger.Errorf("Failed to read savepoint data %s", err.Error()) 830 return nil, err 831 } 832 // ReadDoc() not found (404) will result in nil response, in these cases return height nil 833 if couchDoc == nil || couchDoc.jsonValue == nil { 834 return nil, nil 835 } 836 return decodeSavepoint(couchDoc) 837 } 838 839 // initChannelMetadata initizlizes channelMetadata and build NamespaceDBInfo mapping if not present 840 func (vdb *VersionedDB) initChannelMetadata(isNewDB bool, namespaceProvider statedb.NamespaceProvider) error { 841 // create channelMetadata with empty NamespaceDBInfo mapping for a new DB 842 if isNewDB { 843 vdb.channelMetadata = &channelMetadata{ 844 ChannelName: vdb.chainName, 845 NamespaceDBsInfo: make(map[string]*namespaceDBInfo), 846 } 847 return vdb.writeChannelMetadata() 848 } 849 850 // read stored channelMetadata from an existing DB 851 var err error 852 vdb.channelMetadata, err = vdb.readChannelMetadata() 853 if vdb.channelMetadata != nil || err != nil { 854 return err 855 } 856 857 // channelMetadata is not present - this is the case when opening older dbs (e.g., v2.0/v2.1) for the first time 858 // create channelMetadata and build NamespaceDBInfo mapping retroactively 859 vdb.channelMetadata = &channelMetadata{ 860 ChannelName: vdb.chainName, 861 NamespaceDBsInfo: make(map[string]*namespaceDBInfo), 862 } 863 // retrieve existing DB names 864 dbNames, err := vdb.couchInstance.retrieveApplicationDBNames() 865 if err != nil { 866 return err 867 } 868 existingDBNames := make(map[string]struct{}, len(dbNames)) 869 for _, dbName := range dbNames { 870 existingDBNames[dbName] = struct{}{} 871 } 872 // get namespaces and add a namespace to channelMetadata only if its DB name already exists 873 namespaces, err := namespaceProvider.PossibleNamespaces(vdb) 874 if err != nil { 875 return err 876 } 877 for _, ns := range namespaces { 878 dbName := constructNamespaceDBName(vdb.chainName, ns) 879 if _, ok := existingDBNames[dbName]; ok { 880 vdb.channelMetadata.NamespaceDBsInfo[ns] = &namespaceDBInfo{ 881 Namespace: ns, 882 DBName: dbName, 883 } 884 } 885 } 886 return vdb.writeChannelMetadata() 887 } 888 889 // readChannelMetadata returns channel metadata stored in metadataDB 890 func (vdb *VersionedDB) readChannelMetadata() (*channelMetadata, error) { 891 return readChannelMetadata(vdb.metadataDB) 892 } 893 894 func readChannelMetadata(metadataDB *couchDatabase) (*channelMetadata, error) { 895 var err error 896 couchDoc, _, err := metadataDB.readDoc(channelMetadataDocID) 897 if err != nil { 898 logger.Errorf("Failed to read db name mapping data %s", err.Error()) 899 return nil, err 900 } 901 // ReadDoc() not found (404) will result in nil response, in these cases return nil 902 if couchDoc == nil || couchDoc.jsonValue == nil { 903 return nil, nil 904 } 905 return decodeChannelMetadata(couchDoc) 906 } 907 908 // writeChannelMetadata saves channel metadata to metadataDB 909 func (vdb *VersionedDB) writeChannelMetadata() error { 910 couchDoc, err := encodeChannelMetadata(vdb.channelMetadata) 911 if err != nil { 912 return err 913 } 914 _, err = vdb.metadataDB.saveDoc(channelMetadataDocID, "", couchDoc) 915 return err 916 } 917 918 // GetFullScanIterator implements method in VersionedDB interface. This function returns a 919 // FullScanIterator that can be used to iterate over entire data in the statedb for a channel. 920 // `skipNamespace` parameter can be used to control if the consumer wants the FullScanIterator 921 // to skip one or more namespaces from the returned results. 922 func (vdb *VersionedDB) GetFullScanIterator(skipNamespace func(string) bool) (statedb.FullScanIterator, error) { 923 namespacesToScan := []string{} 924 for ns := range vdb.channelMetadata.NamespaceDBsInfo { 925 if skipNamespace(ns) { 926 continue 927 } 928 namespacesToScan = append(namespacesToScan, ns) 929 } 930 sort.Strings(namespacesToScan) 931 932 // if namespacesToScan is empty, we can return early with a nil FullScanIterator. However, 933 // the implementation of this method needs be consistent with the same method implemented in 934 // the stateleveldb pkg. Hence, we don't return a nil FullScanIterator by checking the length 935 // of the namespacesToScan. 936 937 dbsToScan := []*namespaceDB{} 938 for _, ns := range namespacesToScan { 939 db, err := vdb.getNamespaceDBHandle(ns) 940 if err != nil { 941 return nil, errors.WithMessagef(err, "failed to get database handle for the namespace %s", ns) 942 } 943 dbsToScan = append(dbsToScan, &namespaceDB{ns, db}) 944 } 945 946 // the database which belong to an empty namespace contains 947 // internal keys. The scanner must skip these keys. 948 toSkipKeysFromEmptyNs := map[string]bool{ 949 savepointDocID: true, 950 channelMetadataDocID: true, 951 } 952 return newDBsScanner(dbsToScan, vdb.couchInstance.internalQueryLimit(), toSkipKeysFromEmptyNs) 953 } 954 955 // applyAdditionalQueryOptions will add additional fields to the query required for query processing 956 func applyAdditionalQueryOptions(queryString string, queryLimit int32, queryBookmark string) (string, error) { 957 const jsonQueryFields = "fields" 958 const jsonQueryLimit = "limit" 959 const jsonQueryBookmark = "bookmark" 960 // create a generic map for the query json 961 jsonQueryMap := make(map[string]interface{}) 962 // unmarshal the selector json into the generic map 963 decoder := json.NewDecoder(bytes.NewBuffer([]byte(queryString))) 964 decoder.UseNumber() 965 err := decoder.Decode(&jsonQueryMap) 966 if err != nil { 967 return "", err 968 } 969 if fieldsJSONArray, ok := jsonQueryMap[jsonQueryFields]; ok { 970 switch fieldsJSONArray := fieldsJSONArray.(type) { 971 case []interface{}: 972 // Add the "_id", and "version" fields, these are needed by default 973 jsonQueryMap[jsonQueryFields] = append(fieldsJSONArray, idField, versionField) 974 default: 975 return "", errors.New("fields definition must be an array") 976 } 977 } 978 // Add limit 979 // This will override any limit passed in the query. 980 // Explicit paging not yet supported. 981 jsonQueryMap[jsonQueryLimit] = queryLimit 982 // Add the bookmark if provided 983 if queryBookmark != "" { 984 jsonQueryMap[jsonQueryBookmark] = queryBookmark 985 } 986 // Marshal the updated json query 987 editedQuery, err := json.Marshal(jsonQueryMap) 988 if err != nil { 989 return "", err 990 } 991 logger.Debugf("Rewritten query: %s", editedQuery) 992 return string(editedQuery), nil 993 } 994 995 type queryScanner struct { 996 namespace string 997 db *couchDatabase 998 queryDefinition *queryDefinition 999 paginationInfo *paginationInfo 1000 resultsInfo *resultsInfo 1001 exhausted bool 1002 } 1003 1004 type queryDefinition struct { 1005 startKey string 1006 endKey string 1007 query string 1008 internalQueryLimit int32 1009 } 1010 1011 type paginationInfo struct { 1012 cursor int32 1013 requestedLimit int32 1014 bookmark string 1015 } 1016 1017 type resultsInfo struct { 1018 totalRecordsReturned int32 1019 results []*queryResult 1020 } 1021 1022 func newQueryScanner(namespace string, db *couchDatabase, query string, internalQueryLimit, 1023 limit int32, bookmark, startKey, endKey string) (*queryScanner, error) { 1024 scanner := &queryScanner{namespace, db, &queryDefinition{startKey, endKey, query, internalQueryLimit}, &paginationInfo{-1, limit, bookmark}, &resultsInfo{0, nil}, false} 1025 var err error 1026 // query is defined, then execute the query and return the records and bookmark 1027 if scanner.queryDefinition.query != "" { 1028 err = scanner.executeQueryWithBookmark() 1029 } else { 1030 err = scanner.getNextStateRangeScanResults() 1031 } 1032 if err != nil { 1033 return nil, err 1034 } 1035 scanner.paginationInfo.cursor = -1 1036 return scanner, nil 1037 } 1038 1039 func (scanner *queryScanner) Next() (*statedb.VersionedKV, error) { 1040 doc, err := scanner.next() 1041 if err != nil { 1042 return nil, err 1043 } 1044 if doc == nil { 1045 return nil, nil 1046 } 1047 kv, err := couchDocToKeyValue(doc) 1048 if err != nil { 1049 return nil, err 1050 } 1051 scanner.resultsInfo.totalRecordsReturned++ 1052 return &statedb.VersionedKV{ 1053 CompositeKey: &statedb.CompositeKey{ 1054 Namespace: scanner.namespace, 1055 Key: kv.key, 1056 }, 1057 VersionedValue: kv.VersionedValue, 1058 }, nil 1059 } 1060 1061 func (scanner *queryScanner) next() (*couchDoc, error) { 1062 if len(scanner.resultsInfo.results) == 0 { 1063 return nil, nil 1064 } 1065 scanner.paginationInfo.cursor++ 1066 if scanner.paginationInfo.cursor >= scanner.queryDefinition.internalQueryLimit { 1067 if scanner.exhausted { 1068 return nil, nil 1069 } 1070 var err error 1071 if scanner.queryDefinition.query != "" { 1072 err = scanner.executeQueryWithBookmark() 1073 } else { 1074 err = scanner.getNextStateRangeScanResults() 1075 } 1076 if err != nil { 1077 return nil, err 1078 } 1079 if len(scanner.resultsInfo.results) == 0 { 1080 return nil, nil 1081 } 1082 } 1083 if scanner.paginationInfo.cursor >= int32(len(scanner.resultsInfo.results)) { 1084 return nil, nil 1085 } 1086 result := scanner.resultsInfo.results[scanner.paginationInfo.cursor] 1087 return &couchDoc{ 1088 jsonValue: result.value, 1089 attachments: result.attachments, 1090 }, nil 1091 } 1092 1093 func (scanner *queryScanner) Close() {} 1094 1095 func (scanner *queryScanner) GetBookmarkAndClose() string { 1096 retval := "" 1097 if scanner.queryDefinition.query != "" { 1098 retval = scanner.paginationInfo.bookmark 1099 } else { 1100 retval = scanner.queryDefinition.startKey 1101 } 1102 scanner.Close() 1103 return retval 1104 } 1105 1106 func constructCacheValue(v *statedb.VersionedValue, rev string) *CacheValue { 1107 return &CacheValue{ 1108 Version: v.Version.ToBytes(), 1109 Value: v.Value, 1110 Metadata: v.Metadata, 1111 AdditionalInfo: []byte(rev), 1112 } 1113 } 1114 1115 func constructVersionedValue(cv *CacheValue) (*statedb.VersionedValue, error) { 1116 height, _, err := version.NewHeightFromBytes(cv.Version) 1117 if err != nil { 1118 return nil, err 1119 } 1120 1121 return &statedb.VersionedValue{ 1122 Value: cv.Value, 1123 Version: height, 1124 Metadata: cv.Metadata, 1125 }, nil 1126 } 1127 1128 type dbsScanner struct { 1129 dbs []*namespaceDB 1130 nextDBToScanIndex int 1131 resultItr *queryScanner 1132 currentNamespace string 1133 prefetchLimit int32 1134 toSkipKeysFromEmptyNs map[string]bool 1135 } 1136 1137 type namespaceDB struct { 1138 ns string 1139 db *couchDatabase 1140 } 1141 1142 func newDBsScanner(dbsToScan []*namespaceDB, prefetchLimit int32, toSkipKeysFromEmptyNs map[string]bool) (*dbsScanner, error) { 1143 if len(dbsToScan) == 0 { 1144 return nil, nil 1145 } 1146 s := &dbsScanner{ 1147 dbs: dbsToScan, 1148 prefetchLimit: prefetchLimit, 1149 toSkipKeysFromEmptyNs: toSkipKeysFromEmptyNs, 1150 } 1151 if err := s.beginNextDBScan(); err != nil { 1152 return nil, err 1153 } 1154 return s, nil 1155 } 1156 1157 func (s *dbsScanner) beginNextDBScan() error { 1158 dbUnderScan := s.dbs[s.nextDBToScanIndex] 1159 queryScanner, err := newQueryScanner(dbUnderScan.ns, dbUnderScan.db, "", s.prefetchLimit, 0, "", "", "") 1160 if err != nil { 1161 return errors.WithMessagef( 1162 err, 1163 "failed to create a query scanner for the database %s associated with the namespace %s", 1164 dbUnderScan.db.dbName, 1165 dbUnderScan.ns, 1166 ) 1167 } 1168 s.resultItr = queryScanner 1169 s.currentNamespace = dbUnderScan.ns 1170 s.nextDBToScanIndex++ 1171 return nil 1172 } 1173 1174 // Next returns the key-values present in the namespaceDB. Once a namespaceDB 1175 // is processed, it moves to the next namespaceDB till all are processed. 1176 func (s *dbsScanner) Next() (*statedb.VersionedKV, error) { 1177 if s == nil { 1178 return nil, nil 1179 } 1180 for { 1181 couchDoc, err := s.resultItr.next() 1182 if err != nil { 1183 return nil, errors.WithMessagef( 1184 err, 1185 "failed to retrieve the next entry from scanner associated with namespace %s", 1186 s.currentNamespace, 1187 ) 1188 } 1189 if couchDoc == nil { 1190 s.resultItr.Close() 1191 if len(s.dbs) <= s.nextDBToScanIndex { 1192 break 1193 } 1194 if err := s.beginNextDBScan(); err != nil { 1195 return nil, err 1196 } 1197 continue 1198 } 1199 if s.currentNamespace == "" { 1200 key, err := couchDoc.key() 1201 if err != nil { 1202 return nil, errors.WithMessagef( 1203 err, 1204 "failed to retrieve key from the couchdoc present in the empty namespace", 1205 ) 1206 } 1207 if s.toSkipKeysFromEmptyNs[key] { 1208 continue 1209 } 1210 } 1211 kv, err := couchDocToKeyValue(couchDoc) 1212 if err != nil { 1213 return nil, errors.WithMessagef( 1214 err, 1215 "failed to validate and retrieve fields from couch doc with id %s", 1216 kv.key, 1217 ) 1218 } 1219 return &statedb.VersionedKV{ 1220 CompositeKey: &statedb.CompositeKey{ 1221 Namespace: s.currentNamespace, 1222 Key: kv.key, 1223 }, 1224 VersionedValue: kv.VersionedValue, 1225 }, 1226 nil 1227 } 1228 return nil, nil 1229 } 1230 1231 func (s *dbsScanner) Close() { 1232 if s == nil { 1233 return 1234 } 1235 s.resultItr.Close() 1236 } 1237 1238 type snapshotImporter struct { 1239 vdb *VersionedDB 1240 itr statedb.FullScanIterator 1241 currentNs string 1242 currentNsDB *couchDatabase 1243 pendingDocsBatch []*couchDoc 1244 batchMemorySize int 1245 } 1246 1247 func (s *snapshotImporter) importState() error { 1248 if s.itr == nil { 1249 return nil 1250 } 1251 for { 1252 versionedKV, err := s.itr.Next() 1253 if err != nil { 1254 return err 1255 } 1256 if versionedKV == nil { 1257 break 1258 } 1259 1260 switch { 1261 case s.currentNsDB == nil: 1262 if err := s.createDBForNamespace(versionedKV.Namespace); err != nil { 1263 return err 1264 } 1265 case s.currentNs != versionedKV.Namespace: 1266 if err := s.storePendingDocs(); err != nil { 1267 return err 1268 } 1269 if err := s.createDBForNamespace(versionedKV.Namespace); err != nil { 1270 return err 1271 } 1272 } 1273 1274 doc, err := keyValToCouchDoc( 1275 &keyValue{ 1276 key: versionedKV.Key, 1277 VersionedValue: versionedKV.VersionedValue, 1278 }, 1279 ) 1280 if err != nil { 1281 return err 1282 } 1283 s.pendingDocsBatch = append(s.pendingDocsBatch, doc) 1284 s.batchMemorySize += doc.len() 1285 1286 if s.batchMemorySize >= maxDataImportBatchMemorySize || 1287 len(s.pendingDocsBatch) == s.vdb.couchInstance.maxBatchUpdateSize() { 1288 if err := s.storePendingDocs(); err != nil { 1289 return err 1290 } 1291 } 1292 } 1293 1294 return s.storePendingDocs() 1295 } 1296 1297 func (s *snapshotImporter) createDBForNamespace(ns string) error { 1298 s.currentNs = ns 1299 var err error 1300 s.currentNsDB, err = s.vdb.getNamespaceDBHandle(ns) 1301 return errors.WithMessagef(err, "error while creating database for the namespace %s", ns) 1302 } 1303 1304 func (s *snapshotImporter) storePendingDocs() error { 1305 if len(s.pendingDocsBatch) == 0 { 1306 return nil 1307 } 1308 1309 if err := s.currentNsDB.insertDocuments(s.pendingDocsBatch); err != nil { 1310 return errors.WithMessagef( 1311 err, 1312 "error while storing %d states associated with namespace %s", 1313 len(s.pendingDocsBatch), s.currentNs, 1314 ) 1315 } 1316 s.batchMemorySize = 0 1317 s.pendingDocsBatch = nil 1318 1319 return nil 1320 }