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