github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/kvledger/snapshot_mgmt.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package kvledger
     8  
     9  import (
    10  	"fmt"
    11  	"math"
    12  	"os"
    13  	"sync"
    14  
    15  	"github.com/hechain20/hechain/common/ledger/util"
    16  	"github.com/hechain20/hechain/common/ledger/util/leveldbhelper"
    17  	"github.com/pkg/errors"
    18  )
    19  
    20  type eventType string
    21  
    22  const (
    23  	commitStart         eventType = "commitStart"
    24  	commitDone          eventType = "commitDone"
    25  	requestAdd          eventType = "requestAdd"
    26  	requestCancel       eventType = "requestCancel"
    27  	snapshotDone        eventType = "snapshotDone"
    28  	snapshotMgrShutdown eventType = "snapshotMgrShutdown"
    29  )
    30  
    31  type nextBlockCommitStatus int
    32  
    33  const (
    34  	idle nextBlockCommitStatus = iota
    35  	blocked
    36  	inProcess
    37  )
    38  
    39  type snapshotMgr struct {
    40  	snapshotRequestBookkeeper *snapshotRequestBookkeeper
    41  	events                    chan *event
    42  	commitProceed             chan struct{}
    43  	requestResponses          chan *requestResponse
    44  	stopped                   bool
    45  	shutdownLock              sync.Mutex
    46  }
    47  
    48  type event struct {
    49  	typ         eventType
    50  	blockNumber uint64
    51  }
    52  
    53  func (e *event) String() string {
    54  	return fmt.Sprintf("{type=%s, blockNumber=%d}", e.typ, e.blockNumber)
    55  }
    56  
    57  type requestResponse struct {
    58  	err error
    59  }
    60  
    61  // SubmitSnapshotRequest submits a snapshot request for the specified block number.
    62  // The request will be stored in the ledger until the ledger commits the given block number
    63  // and the snapshot generation is completed.
    64  // When block number is 0, it will generate a snapshot at the last committed block.
    65  // It returns an error if the specified block number is smaller than the last committed block number
    66  // or the requested block number already exists.
    67  func (l *kvLedger) SubmitSnapshotRequest(blockNumber uint64) error {
    68  	l.snapshotMgr.events <- &event{requestAdd, blockNumber}
    69  	response := <-l.snapshotMgr.requestResponses
    70  	return response.err
    71  }
    72  
    73  // CancelSnapshotRequest cancels the previously submitted request.
    74  // It returns an error if such a request does not exist or is under processing.
    75  func (l *kvLedger) CancelSnapshotRequest(blockNumber uint64) error {
    76  	l.snapshotMgr.events <- &event{requestCancel, blockNumber}
    77  	response := <-l.snapshotMgr.requestResponses
    78  	return response.err
    79  }
    80  
    81  // PendingSnapshotRequests returns a list of block numbers for the pending (or under processing) snapshot requests.
    82  func (l *kvLedger) PendingSnapshotRequests() ([]uint64, error) {
    83  	return l.snapshotMgr.snapshotRequestBookkeeper.list()
    84  }
    85  
    86  // processSnapshotMgmtEvents handles each event in the events channel and performs synchronization acorss
    87  // block commits, snapshot generation, and snapshot request submission/cancellation.
    88  // It should be started in a separate goroutine when the ledger is created/opened.
    89  // There are 3 unbuffered channels and 5 events working together to process events one by one
    90  // and perform synchronization.
    91  // - events: a channel receiving all the events
    92  // - commitProceed: a channel indicating if commit can be proceeded. Commit is blocked if a snapshot generation is in progress.
    93  // - requestResponses: a channel returning the response for snapshot request submission/cancellation.
    94  // The 5 events are:
    95  // - commitStart: sent before committing a block
    96  // - commitDone: sent after a block is committed
    97  // - snapshotDone: sent when a snapshot generation is finished, regardless of success or failure
    98  // - requestAdd: sent when a snapshot request is submitted
    99  // - requestCancel: sent when a snapshot request is cancelled
   100  // In addition, the snapshotMgrShutdown event is sent when snapshotMgr shutdown is called. Upon receiving this event,
   101  // this function will return immediately.
   102  func (l *kvLedger) processSnapshotMgmtEvents(lastCommittedBlockNumber uint64) {
   103  	committerStatus := idle
   104  	// snapshotInProgress is set to true before a generateSnapshot is called when processing commitDone or requestAdd event
   105  	// and set to false when snapshotDone event is received
   106  	snapshotInProgress := false
   107  
   108  	events := l.snapshotMgr.events
   109  	commitProceed := l.snapshotMgr.commitProceed
   110  	requestResponses := l.snapshotMgr.requestResponses
   111  
   112  	for {
   113  		e := <-events
   114  		logger.Debugw("Event received",
   115  			"channelID", l.ledgerID, "event", e, "snapshotInProgress", snapshotInProgress,
   116  			"lastCommittedBlockNumber", lastCommittedBlockNumber, "committerStatus", committerStatus,
   117  		)
   118  
   119  		switch e.typ {
   120  		case commitStart:
   121  			committerStatus = blocked
   122  			if snapshotInProgress {
   123  				logger.Infow("Blocking the commit till snapshot generation completes", "channelID", l.ledgerID, "blockNumber", e.blockNumber)
   124  				continue
   125  			}
   126  			// no in-progress snapshot, let commit proceed
   127  			committerStatus = inProcess
   128  			commitProceed <- struct{}{}
   129  
   130  		case commitDone:
   131  			lastCommittedBlockNumber = e.blockNumber
   132  			committerStatus = idle
   133  			if lastCommittedBlockNumber != l.snapshotMgr.snapshotRequestBookkeeper.smallestRequestBlockNum {
   134  				continue
   135  			}
   136  			snapshotInProgress = true
   137  			go func() {
   138  				logger.Infow("Generating snapshot", "channelID", l.ledgerID, "lastCommittedBlockNumber", lastCommittedBlockNumber)
   139  				if err := l.generateSnapshot(); err != nil {
   140  					logger.Errorw("Failed to generate snapshot", "channelID", l.ledgerID, "lastCommittedBlockNumber", lastCommittedBlockNumber, "error", err)
   141  				} else {
   142  					logger.Infow("Generated snapshot", "channelID", l.ledgerID, "lastCommittedBlockNumber", lastCommittedBlockNumber)
   143  				}
   144  				events <- &event{snapshotDone, lastCommittedBlockNumber}
   145  			}()
   146  
   147  		case snapshotDone:
   148  			requestedBlockNum := e.blockNumber
   149  			if err := l.snapshotMgr.snapshotRequestBookkeeper.delete(e.blockNumber); err != nil {
   150  				logger.Errorw("Failed to delete snapshot request, the pending snapshot requests (if any) may not be processed", "channelID", l.ledgerID, "requestedBlockNum", requestedBlockNum, "error", err)
   151  			}
   152  			if committerStatus == blocked {
   153  				logger.Infow("Unblocking the commit", "channelID", l.ledgerID)
   154  				committerStatus = inProcess
   155  				commitProceed <- struct{}{}
   156  			}
   157  			snapshotInProgress = false
   158  
   159  		case requestAdd:
   160  			leastAcceptableBlockNum := lastCommittedBlockNumber
   161  			if committerStatus != idle {
   162  				leastAcceptableBlockNum++
   163  			}
   164  
   165  			requestedBlockNum := e.blockNumber
   166  			if requestedBlockNum == 0 {
   167  				requestedBlockNum = leastAcceptableBlockNum
   168  				logger.Infow("Converting the snapshot generation request from block number 0 to the latest committed block number",
   169  					"channelID", l.ledgerID, "convertedRequestBlockNumber", leastAcceptableBlockNum)
   170  			}
   171  
   172  			if requestedBlockNum < leastAcceptableBlockNum {
   173  				requestResponses <- &requestResponse{errors.Errorf("requested snapshot for block number %d cannot be less than the last committed block number %d", requestedBlockNum, leastAcceptableBlockNum)}
   174  				continue
   175  			}
   176  
   177  			if requestedBlockNum == lastCommittedBlockNumber {
   178  				// this is a corner case where no block has been committed since last snapshot was generated.
   179  				exists, err := l.snapshotExists(requestedBlockNum)
   180  				if err != nil {
   181  					requestResponses <- &requestResponse{err}
   182  					continue
   183  				}
   184  				if exists {
   185  					requestResponses <- &requestResponse{errors.Errorf("snapshot already generated for block number %d", requestedBlockNum)}
   186  					continue
   187  				}
   188  			}
   189  
   190  			if err := l.snapshotMgr.snapshotRequestBookkeeper.add(requestedBlockNum); err != nil {
   191  				requestResponses <- &requestResponse{err}
   192  				continue
   193  			}
   194  
   195  			if committerStatus == idle && requestedBlockNum == lastCommittedBlockNumber {
   196  				snapshotInProgress = true
   197  				go func() {
   198  					logger.Infow("Generating snapshot", "channelID", l.ledgerID, "lastCommittedBlockNumber", lastCommittedBlockNumber)
   199  					if err := l.generateSnapshot(); err != nil {
   200  						logger.Errorw("Failed to generate snapshot", "channelID", l.ledgerID, "lastCommittedBlockNumber", lastCommittedBlockNumber, "error", err)
   201  					} else {
   202  						logger.Infow("Generated snapshot", "channelID", l.ledgerID, "lastCommittedBlockNumber", lastCommittedBlockNumber)
   203  					}
   204  					events <- &event{snapshotDone, requestedBlockNum}
   205  				}()
   206  			}
   207  			requestResponses <- &requestResponse{}
   208  
   209  		case requestCancel:
   210  			requestedBlockNum := e.blockNumber
   211  			if snapshotInProgress && requestedBlockNum == lastCommittedBlockNumber {
   212  				requestResponses <- &requestResponse{errors.Errorf("cannot cancel the snapshot request because it is under processing")}
   213  				continue
   214  			}
   215  			requestResponses <- &requestResponse{l.snapshotMgr.snapshotRequestBookkeeper.delete(requestedBlockNum)}
   216  
   217  		case snapshotMgrShutdown:
   218  			return
   219  		}
   220  	}
   221  }
   222  
   223  func (l *kvLedger) regenrateMissedSnapshot(blockNumber uint64) error {
   224  	if blockNumber != l.snapshotMgr.snapshotRequestBookkeeper.smallestRequestBlockNum {
   225  		return nil
   226  	}
   227  	exists, err := l.snapshotExists(blockNumber)
   228  	if err != nil {
   229  		return err
   230  	}
   231  	if !exists {
   232  		// send commitDone event to generate the missing snapshot
   233  		l.snapshotMgr.events <- &event{typ: commitDone, blockNumber: blockNumber}
   234  	}
   235  	return nil
   236  }
   237  
   238  // snapshotExists checks if the snapshot for the given block number exists
   239  func (l *kvLedger) snapshotExists(blockNum uint64) (bool, error) {
   240  	snapshotDir := SnapshotDirForLedgerBlockNum(l.config.SnapshotsConfig.RootDir, l.ledgerID, blockNum)
   241  	stat, err := os.Stat(snapshotDir)
   242  	if os.IsNotExist(err) {
   243  		return false, nil
   244  	}
   245  	if err != nil {
   246  		return false, err
   247  	}
   248  	return stat != nil, nil
   249  }
   250  
   251  // shutdown sends a snapshotMgrShutdown event and close all the channels, which is called
   252  // when the ledger is closed. For simplicity, this function does not consider in-progress commit
   253  // or snapshot generation. The caller should make sure there is no in-progress commit or
   254  // snapshot generation. Otherwise, it may cause panic because the channels have been closed.
   255  func (m *snapshotMgr) shutdown() {
   256  	m.shutdownLock.Lock()
   257  	defer m.shutdownLock.Unlock()
   258  
   259  	if m.stopped {
   260  		return
   261  	}
   262  
   263  	m.stopped = true
   264  	m.events <- &event{typ: snapshotMgrShutdown}
   265  	close(m.events)
   266  	close(m.commitProceed)
   267  	close(m.requestResponses)
   268  }
   269  
   270  // snapshotRequestBookkeeper manages snapshot requests in a leveldb and maintains smallest block number for pending snapshot requests
   271  type snapshotRequestBookkeeper struct {
   272  	ledgerID                string
   273  	dbHandle                *leveldbhelper.DBHandle
   274  	smallestRequestBlockNum uint64
   275  }
   276  
   277  func newSnapshotRequestBookkeeper(ledgerID string, dbHandle *leveldbhelper.DBHandle) (*snapshotRequestBookkeeper, error) {
   278  	bk := &snapshotRequestBookkeeper{
   279  		ledgerID: ledgerID,
   280  		dbHandle: dbHandle,
   281  	}
   282  
   283  	var err error
   284  	if bk.smallestRequestBlockNum, err = bk.smallestRequest(); err != nil {
   285  		return nil, err
   286  	}
   287  
   288  	return bk, nil
   289  }
   290  
   291  // add adds the given block number to the bookkeeper db and returns an error if the block number already exists
   292  func (k *snapshotRequestBookkeeper) add(blockNumber uint64) error {
   293  	logger.Infow("Adding new request for snapshot", "channelID", k.ledgerID, "blockNumber", blockNumber)
   294  	key := encodeSnapshotRequestKey(blockNumber)
   295  
   296  	exists, err := k.exist(blockNumber)
   297  	if err != nil {
   298  		return err
   299  	}
   300  	if exists {
   301  		return errors.Errorf("duplicate snapshot request for block number %d", blockNumber)
   302  	}
   303  
   304  	if err := k.dbHandle.Put(key, []byte{}, true); err != nil {
   305  		return err
   306  	}
   307  
   308  	if blockNumber < k.smallestRequestBlockNum {
   309  		k.smallestRequestBlockNum = blockNumber
   310  	}
   311  	logger.Infow("Added new request for snapshot", "channelID", k.ledgerID, "blockNumber", blockNumber, "next snapshot blockNumber", k.smallestRequestBlockNum)
   312  	return nil
   313  }
   314  
   315  // delete deletes the given block number from the bookkeeper db and returns an error if the block number does not exist
   316  func (k *snapshotRequestBookkeeper) delete(blockNumber uint64) error {
   317  	logger.Infow("Deleting pending request for snapshot", "channelID", k.ledgerID, "blockNumber", blockNumber)
   318  	exists, err := k.exist(blockNumber)
   319  	if err != nil {
   320  		return err
   321  	}
   322  	if !exists {
   323  		return errors.Errorf("no snapshot request exists for block number %d", blockNumber)
   324  	}
   325  
   326  	if err = k.dbHandle.Delete(encodeSnapshotRequestKey(blockNumber), true); err != nil {
   327  		return err
   328  	}
   329  
   330  	if k.smallestRequestBlockNum != blockNumber {
   331  		return nil
   332  	}
   333  
   334  	if k.smallestRequestBlockNum, err = k.smallestRequest(); err != nil {
   335  		return err
   336  	}
   337  	logger.Infow("Deleted pending request for snapshot", "channelID", k.ledgerID, "blockNumber", blockNumber, "next snapshot blockNumber", k.smallestRequestBlockNum)
   338  	return nil
   339  }
   340  
   341  func (k *snapshotRequestBookkeeper) list() ([]uint64, error) {
   342  	requestedBlockNumbers := []uint64{}
   343  	itr, err := k.dbHandle.GetIterator(nil, nil)
   344  	if err != nil {
   345  		return nil, err
   346  	}
   347  	defer itr.Release()
   348  
   349  	for {
   350  		hasMore := itr.Next()
   351  		if err = itr.Error(); err != nil {
   352  			return nil, errors.Wrapf(err, "internal leveldb error while iterating for snapshot requests")
   353  		}
   354  		if !hasMore {
   355  			break
   356  		}
   357  		blockNumber, _, err := decodeSnapshotRequestKey(itr.Key())
   358  		if err != nil {
   359  			return nil, err
   360  		}
   361  		requestedBlockNumbers = append(requestedBlockNumbers, blockNumber)
   362  	}
   363  
   364  	return requestedBlockNumbers, nil
   365  }
   366  
   367  func (k *snapshotRequestBookkeeper) exist(blockNumber uint64) (bool, error) {
   368  	val, err := k.dbHandle.Get(encodeSnapshotRequestKey(blockNumber))
   369  	if err != nil {
   370  		return false, err
   371  	}
   372  	exists := val != nil
   373  	return exists, nil
   374  }
   375  
   376  const defaultSmallestBlockNumber uint64 = math.MaxUint64
   377  
   378  func (k *snapshotRequestBookkeeper) smallestRequest() (uint64, error) {
   379  	itr, err := k.dbHandle.GetIterator(nil, nil)
   380  	if err != nil {
   381  		return 0, err
   382  	}
   383  	defer itr.Release()
   384  
   385  	hasMore := itr.Next()
   386  	if err = itr.Error(); err != nil {
   387  		return 0, errors.Wrapf(err, "internal leveldb error while iterating for snapshot requests")
   388  	}
   389  	if !hasMore {
   390  		return defaultSmallestBlockNumber, nil
   391  	}
   392  	smallestBlockNumber, _, err := decodeSnapshotRequestKey(itr.Key())
   393  	if err != nil {
   394  		return 0, err
   395  	}
   396  	return smallestBlockNumber, nil
   397  }
   398  
   399  var snapshotRequestKeyPrefix = []byte("s")
   400  
   401  func encodeSnapshotRequestKey(blockNumber uint64) []byte {
   402  	return append(snapshotRequestKeyPrefix, util.EncodeOrderPreservingVarUint64(blockNumber)...)
   403  }
   404  
   405  func decodeSnapshotRequestKey(key []byte) (uint64, int, error) {
   406  	return util.DecodeOrderPreservingVarUint64(key[len(snapshotRequestKeyPrefix):])
   407  }