github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/pvtdatastorage/store.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package pvtdatastorage 8 9 import ( 10 "sync" 11 "sync/atomic" 12 "time" 13 14 "github.com/golang/protobuf/proto" 15 "github.com/hechain20/hechain/common/flogging" 16 "github.com/hechain20/hechain/common/ledger/util/leveldbhelper" 17 "github.com/hechain20/hechain/core/ledger" 18 "github.com/hechain20/hechain/core/ledger/confighistory" 19 "github.com/hechain20/hechain/core/ledger/pvtdatapolicy" 20 "github.com/hyperledger/fabric-protos-go/ledger/rwset" 21 "github.com/pkg/errors" 22 "github.com/willf/bitset" 23 ) 24 25 var logger = flogging.MustGetLogger("pvtdatastorage") 26 27 // Provider provides handle to specific 'Store' that in turn manages 28 // private write sets for a ledger 29 type Provider struct { 30 dbProvider *leveldbhelper.Provider 31 pvtData *PrivateDataConfig 32 } 33 34 // PrivateDataConfig encapsulates the configuration for private data storage on the ledger 35 type PrivateDataConfig struct { 36 // PrivateDataConfig is used to configure a private data storage provider 37 *ledger.PrivateDataConfig 38 // StorePath is the filesystem path for private data storage. 39 // It is internally computed by the ledger component, 40 // so it is not in ledger.PrivateDataConfig and not exposed to other components. 41 StorePath string 42 } 43 44 // Store manages the permanent storage of private write sets for a ledger 45 type Store struct { 46 db *leveldbhelper.DBHandle 47 ledgerid string 48 btlPolicy pvtdatapolicy.BTLPolicy 49 batchesInterval int 50 maxBatchSize int 51 purgeInterval uint64 52 53 isEmpty bool 54 lastCommittedBlock uint64 55 bootsnapshotInfo *bootsnapshotInfo 56 57 purgerLock sync.Mutex 58 collElgProcSync *collElgProcSync 59 // After committing the pvtdata of old blocks, 60 // the `isLastUpdatedOldBlocksSet` is set to true. 61 // Once the stateDB is updated with these pvtdata, 62 // the `isLastUpdatedOldBlocksSet` is set to false. 63 // isLastUpdatedOldBlocksSet is mainly used during the 64 // recovery process. During the peer startup, if the 65 // isLastUpdatedOldBlocksSet is set to true, the pvtdata 66 // in the stateDB needs to be updated before finishing the 67 // recovery operation. 68 isLastUpdatedOldBlocksSet bool 69 70 deprioritizedDataReconcilerInterval time.Duration 71 accessDeprioMissingDataAfter time.Time 72 } 73 74 type bootsnapshotInfo struct { 75 createdFromSnapshot bool 76 lastBlockInSnapshot uint64 77 } 78 79 type blkTranNumKey []byte 80 81 type dataEntry struct { 82 key *dataKey 83 value *rwset.CollectionPvtReadWriteSet 84 } 85 86 type expiryEntry struct { 87 key *expiryKey 88 value *ExpiryData 89 } 90 91 type expiryKey struct { 92 expiringBlk uint64 93 committingBlk uint64 94 } 95 96 type nsCollBlk struct { 97 ns, coll string 98 blkNum uint64 99 } 100 101 type dataKey struct { 102 nsCollBlk 103 txNum uint64 104 } 105 106 type missingDataKey struct { 107 nsCollBlk 108 } 109 110 type bootKVHashesKey struct { 111 blkNum uint64 112 txNum uint64 113 ns string 114 coll string 115 } 116 117 type storeEntries struct { 118 dataEntries []*dataEntry 119 expiryEntries []*expiryEntry 120 elgMissingDataEntries map[missingDataKey]*bitset.BitSet 121 inelgMissingDataEntries map[missingDataKey]*bitset.BitSet 122 } 123 124 // lastUpdatedOldBlocksList keeps the list of last updated blocks 125 // and is stored as the value of lastUpdatedOldBlocksKey (defined in kv_encoding.go) 126 type lastUpdatedOldBlocksList []uint64 127 128 //////// Provider functions ///////////// 129 ////////////////////////////////////////// 130 131 // NewProvider instantiates a StoreProvider 132 func NewProvider(conf *PrivateDataConfig) (*Provider, error) { 133 dbProvider, err := leveldbhelper.NewProvider(&leveldbhelper.Conf{DBPath: conf.StorePath}) 134 if err != nil { 135 return nil, err 136 } 137 return &Provider{ 138 dbProvider: dbProvider, 139 pvtData: conf, 140 }, nil 141 } 142 143 // SnapshotDataImporterFor returns an implementation of interface privacyenabledstate.SnapshotPvtdataHashesConsumer 144 // The returned struct is expected to be registered for receiving the pvtdata hashes from snapshot and loads the data 145 // into pvtdata store. 146 func (p *Provider) SnapshotDataImporterFor( 147 ledgerID string, 148 lastBlockInSnapshot uint64, 149 membershipProvider ledger.MembershipInfoProvider, 150 configHistoryRetriever *confighistory.Retriever, 151 tempDirRoot string, 152 ) (*SnapshotDataImporter, error) { 153 db := p.dbProvider.GetDBHandle(ledgerID) 154 batch := db.NewUpdateBatch() 155 batch.Put(lastBlockInBootSnapshotKey, encodeLastBlockInBootSnapshotVal(lastBlockInSnapshot)) 156 batch.Put(lastCommittedBlkkey, encodeLastCommittedBlockVal(lastBlockInSnapshot)) 157 if err := db.WriteBatch(batch, true); err != nil { 158 return nil, errors.WithMessage(err, "error while writing snapshot info to db") 159 } 160 161 return newSnapshotDataImporter( 162 ledgerID, 163 p.dbProvider.GetDBHandle(ledgerID), 164 membershipProvider, 165 configHistoryRetriever, 166 tempDirRoot, 167 ) 168 } 169 170 // OpenStore returns a handle to a store 171 func (p *Provider) OpenStore(ledgerid string) (*Store, error) { 172 dbHandle := p.dbProvider.GetDBHandle(ledgerid) 173 s := &Store{ 174 db: dbHandle, 175 ledgerid: ledgerid, 176 batchesInterval: p.pvtData.BatchesInterval, 177 maxBatchSize: p.pvtData.MaxBatchSize, 178 purgeInterval: uint64(p.pvtData.PurgeInterval), 179 deprioritizedDataReconcilerInterval: p.pvtData.DeprioritizedDataReconcilerInterval, 180 accessDeprioMissingDataAfter: time.Now().Add(p.pvtData.DeprioritizedDataReconcilerInterval), 181 collElgProcSync: &collElgProcSync{ 182 notification: make(chan bool, 1), 183 procComplete: make(chan bool, 1), 184 }, 185 } 186 if err := s.initState(); err != nil { 187 return nil, err 188 } 189 s.launchCollElgProc() 190 logger.Debugf("Pvtdata store opened. Initial state: isEmpty [%t], lastCommittedBlock [%d]", 191 s.isEmpty, s.lastCommittedBlock) 192 return s, nil 193 } 194 195 // Close closes the store 196 func (p *Provider) Close() { 197 p.dbProvider.Close() 198 } 199 200 // Drop drops channel-specific data from the pvtdata store 201 func (p *Provider) Drop(ledgerid string) error { 202 return p.dbProvider.Drop(ledgerid) 203 } 204 205 //////// store functions //////////////// 206 ////////////////////////////////////////// 207 208 func (s *Store) initState() error { 209 var err error 210 if s.isEmpty, s.lastCommittedBlock, err = s.getLastCommittedBlockNum(); err != nil { 211 return err 212 } 213 214 if s.bootsnapshotInfo, err = s.fetchBootSnapshotInfo(); err != nil { 215 return err 216 } 217 218 // TODO: FAB-16298 -- the concept of pendingBatch is no longer valid 219 // for pvtdataStore. We can remove it v2.1. We retain the concept in 220 // v2.0 to allow rolling upgrade from v142 to v2.0 221 batchPending, err := s.hasPendingCommit() 222 if err != nil { 223 return err 224 } 225 226 if batchPending { 227 committingBlockNum := s.nextBlockNum() 228 batch := s.db.NewUpdateBatch() 229 batch.Put(lastCommittedBlkkey, encodeLastCommittedBlockVal(committingBlockNum)) 230 batch.Delete(pendingCommitKey) 231 if err := s.db.WriteBatch(batch, true); err != nil { 232 return err 233 } 234 s.isEmpty = false 235 s.lastCommittedBlock = committingBlockNum 236 } 237 238 var blist lastUpdatedOldBlocksList 239 if blist, err = s.getLastUpdatedOldBlocksList(); err != nil { 240 return err 241 } 242 if len(blist) > 0 { 243 s.isLastUpdatedOldBlocksSet = true 244 } // false if not set 245 246 return nil 247 } 248 249 // Init initializes the store. This function is expected to be invoked before using the store 250 func (s *Store) Init(btlPolicy pvtdatapolicy.BTLPolicy) { 251 s.btlPolicy = btlPolicy 252 } 253 254 // Commit commits the pvt data as well as both the eligible and ineligible 255 // missing private data --- `eligible` denotes that the missing private data belongs to a collection 256 // for which this peer is a member; `ineligible` denotes that the missing private data belong to a 257 // collection for which this peer is not a member. 258 func (s *Store) Commit(blockNum uint64, pvtData []*ledger.TxPvtData, missingPvtData ledger.TxMissingPvtData) error { 259 expectedBlockNum := s.nextBlockNum() 260 if expectedBlockNum != blockNum { 261 return errors.Errorf("expected block number=%d, received block number=%d", expectedBlockNum, blockNum) 262 } 263 264 batch := s.db.NewUpdateBatch() 265 var err error 266 var key, val []byte 267 268 storeEntries, err := prepareStoreEntries(blockNum, pvtData, s.btlPolicy, missingPvtData) 269 if err != nil { 270 return err 271 } 272 273 for _, dataEntry := range storeEntries.dataEntries { 274 key = encodeDataKey(dataEntry.key) 275 if val, err = encodeDataValue(dataEntry.value); err != nil { 276 return err 277 } 278 batch.Put(key, val) 279 } 280 281 for _, expiryEntry := range storeEntries.expiryEntries { 282 key = encodeExpiryKey(expiryEntry.key) 283 if val, err = encodeExpiryValue(expiryEntry.value); err != nil { 284 return err 285 } 286 batch.Put(key, val) 287 } 288 289 for missingDataKey, missingDataValue := range storeEntries.elgMissingDataEntries { 290 key = encodeElgPrioMissingDataKey(&missingDataKey) 291 292 if val, err = encodeMissingDataValue(missingDataValue); err != nil { 293 return err 294 } 295 batch.Put(key, val) 296 } 297 298 for missingDataKey, missingDataValue := range storeEntries.inelgMissingDataEntries { 299 key = encodeInelgMissingDataKey(&missingDataKey) 300 301 if val, err = encodeMissingDataValue(missingDataValue); err != nil { 302 return err 303 } 304 batch.Put(key, val) 305 } 306 307 committingBlockNum := s.nextBlockNum() 308 logger.Debugf("Committing private data for block [%d]", committingBlockNum) 309 batch.Put(lastCommittedBlkkey, encodeLastCommittedBlockVal(committingBlockNum)) 310 if err := s.db.WriteBatch(batch, true); err != nil { 311 return err 312 } 313 314 s.isEmpty = false 315 atomic.StoreUint64(&s.lastCommittedBlock, committingBlockNum) 316 logger.Debugf("Committed private data for block [%d]", committingBlockNum) 317 s.performPurgeIfScheduled(committingBlockNum) 318 return nil 319 } 320 321 // GetLastUpdatedOldBlocksPvtData returns the pvtdata of blocks listed in `lastUpdatedOldBlocksList` 322 // TODO FAB-16293 -- GetLastUpdatedOldBlocksPvtData() can be removed either in v2.0 or in v2.1. 323 // If we decide to rebuild stateDB in v2.0, by default, the rebuild logic would take 324 // care of synching stateDB with pvtdataStore without calling GetLastUpdatedOldBlocksPvtData(). 325 // Hence, it can be safely removed. Suppose if we decide not to rebuild stateDB in v2.0, 326 // we can remove this function in v2.1. 327 func (s *Store) GetLastUpdatedOldBlocksPvtData() (map[uint64][]*ledger.TxPvtData, error) { 328 if !s.isLastUpdatedOldBlocksSet { 329 return nil, nil 330 } 331 332 updatedBlksList, err := s.getLastUpdatedOldBlocksList() 333 if err != nil { 334 return nil, err 335 } 336 337 blksPvtData := make(map[uint64][]*ledger.TxPvtData) 338 for _, blkNum := range updatedBlksList { 339 if blksPvtData[blkNum], err = s.GetPvtDataByBlockNum(blkNum, nil); err != nil { 340 return nil, err 341 } 342 } 343 return blksPvtData, nil 344 } 345 346 func (s *Store) getLastUpdatedOldBlocksList() ([]uint64, error) { 347 var v []byte 348 var err error 349 if v, err = s.db.Get(lastUpdatedOldBlocksKey); err != nil { 350 return nil, err 351 } 352 if v == nil { 353 return nil, nil 354 } 355 356 var updatedBlksList []uint64 357 buf := proto.NewBuffer(v) 358 numBlks, err := buf.DecodeVarint() 359 if err != nil { 360 return nil, err 361 } 362 for i := 0; i < int(numBlks); i++ { 363 blkNum, err := buf.DecodeVarint() 364 if err != nil { 365 return nil, err 366 } 367 updatedBlksList = append(updatedBlksList, blkNum) 368 } 369 return updatedBlksList, nil 370 } 371 372 // TODO FAB-16294 -- ResetLastUpdatedOldBlocksList() can be removed in v2.1. 373 // From v2.0 onwards, we do not store the last updatedBlksList. Only to support 374 // the rolling upgrade from v142 to v2.0, we retain the ResetLastUpdatedOldBlocksList() 375 // in v2.0. 376 377 // ResetLastUpdatedOldBlocksList removes the `lastUpdatedOldBlocksList` entry from the store 378 func (s *Store) ResetLastUpdatedOldBlocksList() error { 379 batch := s.db.NewUpdateBatch() 380 batch.Delete(lastUpdatedOldBlocksKey) 381 if err := s.db.WriteBatch(batch, true); err != nil { 382 return err 383 } 384 s.isLastUpdatedOldBlocksSet = false 385 return nil 386 } 387 388 // GetPvtDataByBlockNum returns only the pvt data corresponding to the given block number 389 // The pvt data is filtered by the list of 'ns/collections' supplied in the filter 390 // A nil filter does not filter any results 391 func (s *Store) GetPvtDataByBlockNum(blockNum uint64, filter ledger.PvtNsCollFilter) ([]*ledger.TxPvtData, error) { 392 logger.Debugf("Get private data for block [%d], filter=%#v", blockNum, filter) 393 if s.isEmpty { 394 return nil, errors.New("the store is empty") 395 } 396 lastCommittedBlock := atomic.LoadUint64(&s.lastCommittedBlock) 397 if blockNum > lastCommittedBlock { 398 return nil, errors.Errorf("last committed block number [%d] smaller than the requested block number [%d]", lastCommittedBlock, blockNum) 399 } 400 startKey, endKey := getDataKeysForRangeScanByBlockNum(blockNum) 401 logger.Debugf("Querying private data storage for write sets using startKey=%#v, endKey=%#v", startKey, endKey) 402 itr, err := s.db.GetIterator(startKey, endKey) 403 if err != nil { 404 return nil, err 405 } 406 defer itr.Release() 407 408 var blockPvtdata []*ledger.TxPvtData 409 var currentTxNum uint64 410 var currentTxWsetAssember *txPvtdataAssembler 411 firstItr := true 412 413 for itr.Next() { 414 dataKeyBytes := itr.Key() 415 v11Fmt, err := v11Format(dataKeyBytes) 416 if err != nil { 417 return nil, err 418 } 419 if v11Fmt { 420 return v11RetrievePvtdata(itr, filter) 421 } 422 dataValueBytes := itr.Value() 423 dataKey, err := decodeDatakey(dataKeyBytes) 424 if err != nil { 425 return nil, err 426 } 427 expired, err := isExpired(dataKey.nsCollBlk, s.btlPolicy, lastCommittedBlock) 428 if err != nil { 429 return nil, err 430 } 431 if expired || !passesFilter(dataKey, filter) { 432 continue 433 } 434 dataValue, err := decodeDataValue(dataValueBytes) 435 if err != nil { 436 return nil, err 437 } 438 439 if firstItr { 440 currentTxNum = dataKey.txNum 441 currentTxWsetAssember = newTxPvtdataAssembler(blockNum, currentTxNum) 442 firstItr = false 443 } 444 445 if dataKey.txNum != currentTxNum { 446 blockPvtdata = append(blockPvtdata, currentTxWsetAssember.getTxPvtdata()) 447 currentTxNum = dataKey.txNum 448 currentTxWsetAssember = newTxPvtdataAssembler(blockNum, currentTxNum) 449 } 450 currentTxWsetAssember.add(dataKey.ns, dataValue) 451 } 452 if currentTxWsetAssember != nil { 453 blockPvtdata = append(blockPvtdata, currentTxWsetAssember.getTxPvtdata()) 454 } 455 return blockPvtdata, nil 456 } 457 458 // GetMissingPvtDataInfoForMostRecentBlocks returns the missing private data information for the 459 // most recent `maxBlock` blocks which miss at least a private data of a eligible collection. 460 func (s *Store) GetMissingPvtDataInfoForMostRecentBlocks(maxBlock int) (ledger.MissingPvtDataInfo, error) { 461 // we assume that this function would be called by the gossip only after processing the 462 // last retrieved missing pvtdata info and committing the same. 463 if maxBlock < 1 { 464 return nil, nil 465 } 466 467 if time.Now().After(s.accessDeprioMissingDataAfter) { 468 s.accessDeprioMissingDataAfter = time.Now().Add(s.deprioritizedDataReconcilerInterval) 469 logger.Debug("fetching missing pvtdata entries from the deprioritized list") 470 return s.getMissingData(elgDeprioritizedMissingDataGroup, maxBlock) 471 } 472 473 logger.Debug("fetching missing pvtdata entries from the prioritized list") 474 return s.getMissingData(elgPrioritizedMissingDataGroup, maxBlock) 475 } 476 477 func (s *Store) getMissingData(group []byte, maxBlock int) (ledger.MissingPvtDataInfo, error) { 478 missingPvtDataInfo := make(ledger.MissingPvtDataInfo) 479 numberOfBlockProcessed := 0 480 lastProcessedBlock := uint64(0) 481 isMaxBlockLimitReached := false 482 483 // as we are not acquiring a read lock, new blocks can get committed while we 484 // construct the MissingPvtDataInfo. As a result, lastCommittedBlock can get 485 // changed. To ensure consistency, we atomically load the lastCommittedBlock value 486 lastCommittedBlock := atomic.LoadUint64(&s.lastCommittedBlock) 487 488 startKey, endKey := createRangeScanKeysForElgMissingData(lastCommittedBlock, group) 489 dbItr, err := s.db.GetIterator(startKey, endKey) 490 if err != nil { 491 return nil, err 492 } 493 defer dbItr.Release() 494 495 for dbItr.Next() { 496 missingDataKeyBytes := dbItr.Key() 497 missingDataKey := decodeElgMissingDataKey(missingDataKeyBytes) 498 499 if isMaxBlockLimitReached && (missingDataKey.blkNum != lastProcessedBlock) { 500 // ensures that exactly maxBlock number 501 // of blocks' entries are processed 502 break 503 } 504 505 // check whether the entry is expired. If so, move to the next item. 506 // As we may use the old lastCommittedBlock value, there is a possibility that 507 // this missing data is actually expired but we may get the stale information. 508 // Though it may leads to extra work of pulling the expired data, it will not 509 // affect the correctness. Further, as we try to fetch the most recent missing 510 // data (less possibility of expiring now), such scenario would be rare. In the 511 // best case, we can load the latest lastCommittedBlock value here atomically to 512 // make this scenario very rare. 513 lastCommittedBlock = atomic.LoadUint64(&s.lastCommittedBlock) 514 expired, err := isExpired(missingDataKey.nsCollBlk, s.btlPolicy, lastCommittedBlock) 515 if err != nil { 516 return nil, err 517 } 518 if expired { 519 continue 520 } 521 522 // check for an existing entry for the blkNum in the MissingPvtDataInfo. 523 // If no such entry exists, create one. Also, keep track of the number of 524 // processed block due to maxBlock limit. 525 if _, ok := missingPvtDataInfo[missingDataKey.blkNum]; !ok { 526 numberOfBlockProcessed++ 527 if numberOfBlockProcessed == maxBlock { 528 isMaxBlockLimitReached = true 529 // as there can be more than one entry for this block, 530 // we cannot `break` here 531 lastProcessedBlock = missingDataKey.blkNum 532 } 533 } 534 535 valueBytes := dbItr.Value() 536 bitmap, err := decodeMissingDataValue(valueBytes) 537 if err != nil { 538 return nil, err 539 } 540 541 // for each transaction which misses private data, make an entry in missingBlockPvtDataInfo 542 for index, isSet := bitmap.NextSet(0); isSet; index, isSet = bitmap.NextSet(index + 1) { 543 txNum := uint64(index) 544 missingPvtDataInfo.Add(missingDataKey.blkNum, txNum, missingDataKey.ns, missingDataKey.coll) 545 } 546 } 547 548 return missingPvtDataInfo, nil 549 } 550 551 // FetchBootKVHashes returns the KVHashes from the data that was loaded from a snapshot at the time of 552 // bootstrapping. This function returns an error if the supplied blkNum is greater than the last block 553 // number in the booting snapshot 554 func (s *Store) FetchBootKVHashes(blkNum, txNum uint64, ns, coll string) (map[string][]byte, error) { 555 if s.bootsnapshotInfo.createdFromSnapshot && blkNum > s.bootsnapshotInfo.lastBlockInSnapshot { 556 return nil, errors.New( 557 "unexpected call. Boot KV Hashes are persisted only for the data imported from snapshot", 558 ) 559 } 560 encVal, err := s.db.Get( 561 encodeBootKVHashesKey( 562 &bootKVHashesKey{ 563 blkNum: blkNum, 564 txNum: txNum, 565 ns: ns, 566 coll: coll, 567 }, 568 ), 569 ) 570 if err != nil || encVal == nil { 571 return nil, err 572 } 573 bootKVHashes, err := decodeBootKVHashesVal(encVal) 574 if err != nil { 575 return nil, err 576 } 577 return bootKVHashes.toMap(), nil 578 } 579 580 // ProcessCollsEligibilityEnabled notifies the store when the peer becomes eligible to receive data for an 581 // existing collection. Parameter 'committingBlk' refers to the block number that contains the corresponding 582 // collection upgrade transaction and the parameter 'nsCollMap' contains the collections for which the peer 583 // is now eligible to receive pvt data 584 func (s *Store) ProcessCollsEligibilityEnabled(committingBlk uint64, nsCollMap map[string][]string) error { 585 key := encodeCollElgKey(committingBlk) 586 m := newCollElgInfo(nsCollMap) 587 val, err := encodeCollElgVal(m) 588 if err != nil { 589 return err 590 } 591 batch := s.db.NewUpdateBatch() 592 batch.Put(key, val) 593 if err = s.db.WriteBatch(batch, true); err != nil { 594 return err 595 } 596 s.collElgProcSync.notify() 597 return nil 598 } 599 600 func (s *Store) performPurgeIfScheduled(latestCommittedBlk uint64) { 601 if latestCommittedBlk%s.purgeInterval != 0 { 602 return 603 } 604 go func() { 605 s.purgerLock.Lock() 606 logger.Debugf("Purger started: Purging expired private data till block number [%d]", latestCommittedBlk) 607 defer s.purgerLock.Unlock() 608 err := s.purgeExpiredData(0, latestCommittedBlk) 609 if err != nil { 610 logger.Warningf("Could not purge data from pvtdata store:%s", err) 611 } 612 logger.Debug("Purger finished") 613 }() 614 } 615 616 func (s *Store) purgeExpiredData(minBlkNum, maxBlkNum uint64) error { 617 expiryEntries, err := s.retrieveExpiryEntries(minBlkNum, maxBlkNum) 618 if err != nil || len(expiryEntries) == 0 { 619 return err 620 } 621 622 batch := s.db.NewUpdateBatch() 623 for _, expiryEntry := range expiryEntries { 624 batch.Delete(encodeExpiryKey(expiryEntry.key)) 625 dataKeys, missingDataKeys, bootKVHashesKeys := deriveKeys(expiryEntry) 626 627 for _, dataKey := range dataKeys { 628 batch.Delete(encodeDataKey(dataKey)) 629 } 630 631 for _, missingDataKey := range missingDataKeys { 632 batch.Delete( 633 encodeElgPrioMissingDataKey(missingDataKey), 634 ) 635 batch.Delete( 636 encodeElgDeprioMissingDataKey(missingDataKey), 637 ) 638 batch.Delete( 639 encodeInelgMissingDataKey(missingDataKey), 640 ) 641 } 642 643 for _, bootKVHashesKey := range bootKVHashesKeys { 644 batch.Delete(encodeBootKVHashesKey(bootKVHashesKey)) 645 } 646 647 if err := s.db.WriteBatch(batch, false); err != nil { 648 return err 649 } 650 batch.Reset() 651 } 652 653 logger.Infof("[%s] - [%d] Entries purged from private data storage till block number [%d]", s.ledgerid, len(expiryEntries), maxBlkNum) 654 return nil 655 } 656 657 func (s *Store) retrieveExpiryEntries(minBlkNum, maxBlkNum uint64) ([]*expiryEntry, error) { 658 startKey, endKey := getExpiryKeysForRangeScan(minBlkNum, maxBlkNum) 659 logger.Debugf("retrieveExpiryEntries(): startKey=%#v, endKey=%#v", startKey, endKey) 660 itr, err := s.db.GetIterator(startKey, endKey) 661 if err != nil { 662 return nil, err 663 } 664 defer itr.Release() 665 666 var expiryEntries []*expiryEntry 667 for itr.Next() { 668 expiryKeyBytes := itr.Key() 669 expiryValueBytes := itr.Value() 670 expiryKey, err := decodeExpiryKey(expiryKeyBytes) 671 if err != nil { 672 return nil, err 673 } 674 expiryValue, err := decodeExpiryValue(expiryValueBytes) 675 if err != nil { 676 return nil, err 677 } 678 expiryEntries = append(expiryEntries, &expiryEntry{key: expiryKey, value: expiryValue}) 679 } 680 return expiryEntries, nil 681 } 682 683 func (s *Store) launchCollElgProc() { 684 go func() { 685 if err := s.processCollElgEvents(); err != nil { 686 // process collection eligibility events when store is opened - 687 // in case there is an unprocessed events from previous run 688 logger.Errorw("failed to process collection eligibility events", "err", err) 689 } 690 for { 691 logger.Debugf("Waiting for collection eligibility event") 692 s.collElgProcSync.waitForNotification() 693 if err := s.processCollElgEvents(); err != nil { 694 logger.Errorw("failed to process collection eligibility events", "err", err) 695 } 696 s.collElgProcSync.done() 697 } 698 }() 699 } 700 701 func (s *Store) processCollElgEvents() error { 702 logger.Debugf("Starting to process collection eligibility events") 703 s.purgerLock.Lock() 704 defer s.purgerLock.Unlock() 705 collElgStartKey, collElgEndKey := createRangeScanKeysForCollElg() 706 eventItr, err := s.db.GetIterator(collElgStartKey, collElgEndKey) 707 if err != nil { 708 return err 709 } 710 defer eventItr.Release() 711 batch := s.db.NewUpdateBatch() 712 totalEntriesConverted := 0 713 714 for eventItr.Next() { 715 collElgKey, collElgVal := eventItr.Key(), eventItr.Value() 716 blkNum := decodeCollElgKey(collElgKey) 717 CollElgInfo, err := decodeCollElgVal(collElgVal) 718 logger.Debugf("Processing collection eligibility event [blkNum=%d], CollElgInfo=%s", blkNum, CollElgInfo) 719 if err != nil { 720 logger.Errorf("This error is not expected %s", err) 721 continue 722 } 723 for ns, colls := range CollElgInfo.NsCollMap { 724 var coll string 725 for _, coll = range colls.Entries { 726 logger.Infof("Converting missing data entries from ineligible to eligible for [ns=%s, coll=%s]", ns, coll) 727 startKey, endKey := createRangeScanKeysForInelgMissingData(blkNum, ns, coll) 728 collItr, err := s.db.GetIterator(startKey, endKey) 729 if err != nil { 730 return err 731 } 732 collEntriesConverted := 0 733 734 for collItr.Next() { // each entry 735 originalKey, originalVal := collItr.Key(), collItr.Value() 736 modifiedKey := decodeInelgMissingDataKey(originalKey) 737 batch.Delete(originalKey) 738 copyVal := make([]byte, len(originalVal)) 739 copy(copyVal, originalVal) 740 batch.Put( 741 encodeElgPrioMissingDataKey(modifiedKey), 742 copyVal, 743 ) 744 collEntriesConverted++ 745 if batch.Len() > s.maxBatchSize { 746 if err := s.db.WriteBatch(batch, true); err != nil { 747 return err 748 } 749 batch.Reset() 750 sleepTime := time.Duration(s.batchesInterval) 751 logger.Infof("Going to sleep for %d milliseconds between batches. Entries for [ns=%s, coll=%s] converted so far = %d", 752 sleepTime, ns, coll, collEntriesConverted) 753 s.purgerLock.Unlock() 754 time.Sleep(sleepTime * time.Millisecond) 755 s.purgerLock.Lock() 756 } 757 } // entry loop 758 759 collItr.Release() 760 logger.Infof("Converted all [%d] entries for [ns=%s, coll=%s]", collEntriesConverted, ns, coll) 761 totalEntriesConverted += collEntriesConverted 762 } // coll loop 763 } // ns loop 764 batch.Delete(collElgKey) // delete the collection eligibility event key as well 765 } // event loop 766 767 if err := s.db.WriteBatch(batch, true); err != nil { 768 return err 769 } 770 logger.Debugf("Converted [%d] ineligible missing data entries to eligible", totalEntriesConverted) 771 return nil 772 } 773 774 // LastCommittedBlockHeight returns the height of the last committed block 775 func (s *Store) LastCommittedBlockHeight() (uint64, error) { 776 if s.isEmpty { 777 return 0, nil 778 } 779 return atomic.LoadUint64(&s.lastCommittedBlock) + 1, nil 780 } 781 782 func (s *Store) nextBlockNum() uint64 { 783 if s.isEmpty { 784 return 0 785 } 786 return atomic.LoadUint64(&s.lastCommittedBlock) + 1 787 } 788 789 // TODO: FAB-16298 -- the concept of pendingBatch is no longer valid 790 // for pvtdataStore. We can remove it v2.1. We retain the concept in 791 // v2.0 to allow rolling upgrade from v142 to v2.0 792 func (s *Store) hasPendingCommit() (bool, error) { 793 var v []byte 794 var err error 795 if v, err = s.db.Get(pendingCommitKey); err != nil { 796 return false, err 797 } 798 return v != nil, nil 799 } 800 801 func (s *Store) getLastCommittedBlockNum() (bool, uint64, error) { 802 var v []byte 803 var err error 804 if v, err = s.db.Get(lastCommittedBlkkey); v == nil || err != nil { 805 return true, 0, err 806 } 807 return false, decodeLastCommittedBlockVal(v), nil 808 } 809 810 func (s *Store) fetchBootSnapshotInfo() (*bootsnapshotInfo, error) { 811 v, err := s.db.Get(lastBlockInBootSnapshotKey) 812 if err != nil { 813 return nil, err 814 } 815 if v == nil { 816 return &bootsnapshotInfo{}, nil 817 } 818 819 lastBlkInSnapshot, err := decodeLastBlockInBootSnapshotVal(v) 820 if err != nil { 821 return nil, err 822 } 823 824 return &bootsnapshotInfo{ 825 createdFromSnapshot: true, 826 lastBlockInSnapshot: lastBlkInSnapshot, 827 }, nil 828 } 829 830 type collElgProcSync struct { 831 notification, procComplete chan bool 832 } 833 834 func (c *collElgProcSync) notify() { 835 select { 836 case c.notification <- true: 837 logger.Debugf("Signaled to collection eligibility processing routine") 838 default: // noop 839 logger.Debugf("Previous signal still pending. Skipping new signal") 840 } 841 } 842 843 func (c *collElgProcSync) waitForNotification() { 844 <-c.notification 845 } 846 847 func (c *collElgProcSync) done() { 848 select { 849 case c.procComplete <- true: 850 default: 851 } 852 } 853 854 func (c *collElgProcSync) waitForDone() { 855 <-c.procComplete 856 }