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  }