github.com/true-sqn/fabric@v2.1.1+incompatible/core/ledger/kvledger/txmgmt/pvtstatepurgemgmt/expiry_keeper.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package pvtstatepurgemgmt 8 9 import ( 10 proto "github.com/golang/protobuf/proto" 11 "github.com/hyperledger/fabric/common/flogging" 12 "github.com/hyperledger/fabric/common/ledger/util" 13 "github.com/hyperledger/fabric/common/ledger/util/leveldbhelper" 14 "github.com/hyperledger/fabric/core/ledger/kvledger/bookkeeping" 15 ) 16 17 var logger = flogging.MustGetLogger("pvtstatepurgemgmt") 18 19 const ( 20 expiryPrefix = '1' 21 ) 22 23 // expiryInfoKey is used as a key of an entry in the bookkeeper (backed by a leveldb instance) 24 type expiryInfoKey struct { 25 committingBlk uint64 26 expiryBlk uint64 27 } 28 29 // expiryInfo encapsulates an 'expiryInfoKey' and corresponding private data keys. 30 // In another words, this struct encapsulates the keys and key-hashes that are committed by 31 // the block number 'expiryInfoKey.committingBlk' and should be expired (and hence purged) 32 // with the commit of block number 'expiryInfoKey.expiryBlk' 33 type expiryInfo struct { 34 expiryInfoKey *expiryInfoKey 35 pvtdataKeys *PvtdataKeys 36 } 37 38 // expiryKeeper is used to keep track of the expired items in the pvtdata space 39 type expiryKeeper interface { 40 // updateBookkeeping keeps track of the list of keys and their corresponding expiry block number 41 updateBookkeeping(toTrack []*expiryInfo, toClear []*expiryInfoKey) error 42 // retrieve returns the keys info that are supposed to be expired by the given block number 43 retrieve(expiringAtBlkNum uint64) ([]*expiryInfo, error) 44 // retrieveByExpiryKey retrieves the expiryInfo for given expiryKey 45 retrieveByExpiryKey(expiryKey *expiryInfoKey) (*expiryInfo, error) 46 } 47 48 func newExpiryKeeper(ledgerid string, provider bookkeeping.Provider) expiryKeeper { 49 return &expKeeper{provider.GetDBHandle(ledgerid, bookkeeping.PvtdataExpiry)} 50 } 51 52 type expKeeper struct { 53 db *leveldbhelper.DBHandle 54 } 55 56 // updateBookkeeping updates the information stored in the bookkeeper 57 // 'toTrack' parameter causes new entries in the bookkeeper and 'toClear' parameter contains the entries that 58 // are to be removed from the bookkeeper. This function is invoked with the commit of every block. As an 59 // example, the commit of the block with block number 50, 'toTrack' parameter may contain following two entries: 60 // (1) &expiryInfo{&expiryInfoKey{committingBlk: 50, expiryBlk: 55}, pvtdataKeys....} and 61 // (2) &expiryInfo{&expiryInfoKey{committingBlk: 50, expiryBlk: 60}, pvtdataKeys....} 62 // The 'pvtdataKeys' in the first entry contains all the keys (and key-hashes) that are to be expired at block 55 (i.e., these collections have a BTL configured to 4) 63 // and the 'pvtdataKeys' in second entry contains all the keys (and key-hashes) that are to be expired at block 60 (i.e., these collections have a BTL configured to 9). 64 // Similarly, continuing with the above example, the parameter 'toClear' may contain following two entries 65 // (1) &expiryInfoKey{committingBlk: 45, expiryBlk: 50} and (2) &expiryInfoKey{committingBlk: 40, expiryBlk: 50}. The first entry was created 66 // at the time of the commit of the block number 45 and the second entry was created at the time of the commit of the block number 40, however 67 // both are expiring with the commit of block number 50. 68 func (ek *expKeeper) updateBookkeeping(toTrack []*expiryInfo, toClear []*expiryInfoKey) error { 69 updateBatch := leveldbhelper.NewUpdateBatch() 70 for _, expinfo := range toTrack { 71 k, v, err := encodeKV(expinfo) 72 if err != nil { 73 return err 74 } 75 updateBatch.Put(k, v) 76 } 77 for _, expinfokey := range toClear { 78 updateBatch.Delete(encodeExpiryInfoKey(expinfokey)) 79 } 80 return ek.db.WriteBatch(updateBatch, true) 81 } 82 83 func (ek *expKeeper) retrieve(expiringAtBlkNum uint64) ([]*expiryInfo, error) { 84 startKey := encodeExpiryInfoKey(&expiryInfoKey{expiryBlk: expiringAtBlkNum, committingBlk: 0}) 85 endKey := encodeExpiryInfoKey(&expiryInfoKey{expiryBlk: expiringAtBlkNum + 1, committingBlk: 0}) 86 itr := ek.db.GetIterator(startKey, endKey) 87 defer itr.Release() 88 89 var listExpinfo []*expiryInfo 90 for itr.Next() { 91 expinfo, err := decodeExpiryInfo(itr.Key(), itr.Value()) 92 if err != nil { 93 return nil, err 94 } 95 listExpinfo = append(listExpinfo, expinfo) 96 } 97 return listExpinfo, nil 98 } 99 100 func (ek *expKeeper) retrieveByExpiryKey(expiryKey *expiryInfoKey) (*expiryInfo, error) { 101 key := encodeExpiryInfoKey(expiryKey) 102 value, err := ek.db.Get(key) 103 if err != nil { 104 return nil, err 105 } 106 return decodeExpiryInfo(key, value) 107 } 108 109 func encodeKV(expinfo *expiryInfo) (key []byte, value []byte, err error) { 110 key = encodeExpiryInfoKey(expinfo.expiryInfoKey) 111 value, err = encodeExpiryInfoValue(expinfo.pvtdataKeys) 112 return 113 } 114 115 func encodeExpiryInfoKey(expinfoKey *expiryInfoKey) []byte { 116 key := append([]byte{expiryPrefix}, util.EncodeOrderPreservingVarUint64(expinfoKey.expiryBlk)...) 117 return append(key, util.EncodeOrderPreservingVarUint64(expinfoKey.committingBlk)...) 118 } 119 120 func encodeExpiryInfoValue(pvtdataKeys *PvtdataKeys) ([]byte, error) { 121 return proto.Marshal(pvtdataKeys) 122 } 123 124 func decodeExpiryInfo(key []byte, value []byte) (*expiryInfo, error) { 125 expiryBlk, n, err := util.DecodeOrderPreservingVarUint64(key[1:]) 126 if err != nil { 127 return nil, err 128 } 129 committingBlk, _, err := util.DecodeOrderPreservingVarUint64(key[n+1:]) 130 if err != nil { 131 return nil, err 132 } 133 pvtdataKeys := &PvtdataKeys{} 134 if err := proto.Unmarshal(value, pvtdataKeys); err != nil { 135 return nil, err 136 } 137 return &expiryInfo{ 138 expiryInfoKey: &expiryInfoKey{committingBlk: committingBlk, expiryBlk: expiryBlk}, 139 pvtdataKeys: pvtdataKeys}, 140 nil 141 }