github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/kvledger/snapshot_mgmt_test.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  	"testing"
    12  	"time"
    13  
    14  	"github.com/hechain20/hechain/common/ledger/testutil"
    15  	"github.com/hechain20/hechain/common/util"
    16  	"github.com/hechain20/hechain/core/ledger"
    17  	"github.com/hechain20/hechain/core/ledger/kvledger/bookkeeping"
    18  	"github.com/hechain20/hechain/core/ledger/mock"
    19  	"github.com/hechain20/hechain/protoutil"
    20  	"github.com/hyperledger/fabric-protos-go/common"
    21  	"github.com/stretchr/testify/require"
    22  )
    23  
    24  func TestSnapshotRequestBookKeeper(t *testing.T) {
    25  	conf, cleanup := testConfig(t)
    26  	defer cleanup()
    27  	provider := testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{})
    28  	defer provider.Close()
    29  
    30  	ledgerID := "testsnapshotrequestbookkeeper"
    31  	dbHandle := provider.bookkeepingProvider.GetDBHandle(ledgerID, bookkeeping.SnapshotRequest)
    32  	bookkeeper, err := newSnapshotRequestBookkeeper("test-ledger", dbHandle)
    33  	require.NoError(t, err)
    34  
    35  	// add requests and verify smallestRequest
    36  	require.NoError(t, bookkeeper.add(100))
    37  	require.Equal(t, uint64(100), bookkeeper.smallestRequestBlockNum)
    38  
    39  	require.NoError(t, bookkeeper.add(15))
    40  	require.Equal(t, uint64(15), bookkeeper.smallestRequestBlockNum)
    41  
    42  	require.NoError(t, bookkeeper.add(50))
    43  	require.Equal(t, uint64(15), bookkeeper.smallestRequestBlockNum)
    44  
    45  	requestBlockNums, err := bookkeeper.list()
    46  	require.NoError(t, err)
    47  	require.ElementsMatch(t, requestBlockNums, []uint64{15, 50, 100})
    48  
    49  	for _, blockNumber := range []uint64{15, 50, 100} {
    50  		exist, err := bookkeeper.exist(blockNumber)
    51  		require.NoError(t, err)
    52  		require.True(t, exist)
    53  	}
    54  
    55  	exist, err := bookkeeper.exist(10)
    56  	require.NoError(t, err)
    57  	require.False(t, exist)
    58  
    59  	provider.Close()
    60  
    61  	// reopen the provider and verify snapshotRequestBookkeeper is initialized correctly
    62  	provider2 := testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{})
    63  	defer provider2.Close()
    64  	dbHandle2 := provider2.bookkeepingProvider.GetDBHandle(ledgerID, bookkeeping.SnapshotRequest)
    65  	bookkeeper2, err := newSnapshotRequestBookkeeper("test-ledger", dbHandle2)
    66  	require.NoError(t, err)
    67  
    68  	requestBlockNums, err = bookkeeper2.list()
    69  	require.NoError(t, err)
    70  	require.ElementsMatch(t, requestBlockNums, []uint64{15, 50, 100})
    71  
    72  	require.Equal(t, uint64(15), bookkeeper2.smallestRequestBlockNum)
    73  
    74  	// delete requests and verify smallest request
    75  	require.NoError(t, bookkeeper2.delete(100))
    76  	require.Equal(t, uint64(15), bookkeeper2.smallestRequestBlockNum)
    77  
    78  	require.NoError(t, bookkeeper2.delete(15))
    79  	require.Equal(t, uint64(50), bookkeeper2.smallestRequestBlockNum)
    80  
    81  	require.NoError(t, bookkeeper2.delete(50))
    82  	require.Equal(t, defaultSmallestBlockNumber, bookkeeper2.smallestRequestBlockNum)
    83  
    84  	requestBlockNums, err = bookkeeper2.list()
    85  	require.NoError(t, err)
    86  	require.ElementsMatch(t, requestBlockNums, []uint64{})
    87  }
    88  
    89  func TestSnapshotRequestBookKeeperErrorPaths(t *testing.T) {
    90  	conf, cleanup := testConfig(t)
    91  	defer cleanup()
    92  	provider := testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{})
    93  	defer provider.Close()
    94  
    95  	dbHandle := provider.bookkeepingProvider.GetDBHandle("testrequestbookkeepererrorpaths", bookkeeping.SnapshotRequest)
    96  	bookkeeper2, err := newSnapshotRequestBookkeeper("test-ledger", dbHandle)
    97  	require.NoError(t, err)
    98  
    99  	require.NoError(t, bookkeeper2.add(20))
   100  	require.EqualError(t, bookkeeper2.add(20), "duplicate snapshot request for block number 20")
   101  	require.EqualError(t, bookkeeper2.delete(100), "no snapshot request exists for block number 100")
   102  
   103  	provider.Close()
   104  
   105  	_, err = newSnapshotRequestBookkeeper("test-ledger", dbHandle)
   106  	require.EqualError(t, err, "internal leveldb error while obtaining db iterator: leveldb: closed")
   107  
   108  	err = bookkeeper2.add(20)
   109  	require.Contains(t, err.Error(), "leveldb: closed")
   110  
   111  	err = bookkeeper2.delete(1)
   112  	require.Contains(t, err.Error(), "leveldb: closed")
   113  
   114  	_, err = bookkeeper2.list()
   115  	require.Contains(t, err.Error(), "leveldb: closed")
   116  
   117  	_, err = bookkeeper2.exist(20)
   118  	require.Contains(t, err.Error(), "leveldb: closed")
   119  }
   120  
   121  func TestSnapshotRequests(t *testing.T) {
   122  	conf, cleanup := testConfig(t)
   123  	defer cleanup()
   124  	provider := testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{})
   125  	defer provider.Close()
   126  
   127  	// create a ledger with genesis block
   128  	ledgerID := "testsnapshotrequests"
   129  	bg, gb := testutil.NewBlockGenerator(t, ledgerID, false)
   130  	gbHash := protoutil.BlockHeaderHash(gb.Header)
   131  	l, err := provider.CreateFromGenesisBlock(gb)
   132  	require.NoError(t, err)
   133  	defer l.Close()
   134  	kvledger := l.(*kvLedger)
   135  
   136  	// Test 1: submit requests in parallel and verify PendingSnapshotRequest
   137  	for _, blockNumber := range []uint64{100, 5, 3, 10, 30} {
   138  		go func(blockNumber uint64) {
   139  			require.NoError(t, l.SubmitSnapshotRequest(blockNumber))
   140  		}(blockNumber)
   141  	}
   142  	// wait until all requests are submitted
   143  	requestsUpdated := func() bool {
   144  		requests, err := l.PendingSnapshotRequests()
   145  		require.NoError(t, err)
   146  		return equal(requests, []uint64{3, 5, 10, 30, 100})
   147  	}
   148  	require.Eventually(t, requestsUpdated, time.Minute, 100*time.Millisecond)
   149  
   150  	// Test 2: cancel requests in parallel and verify PendingSnapshotRequest
   151  	for _, blockNumber := range []uint64{3, 30} {
   152  		go func(blockNumber uint64) {
   153  			require.NoError(t, l.CancelSnapshotRequest(blockNumber))
   154  		}(blockNumber)
   155  	}
   156  	// wait until all requests are cancelled
   157  	requestsUpdated = func() bool {
   158  		requests, err := l.PendingSnapshotRequests()
   159  		require.NoError(t, err)
   160  		return equal(requests, []uint64{5, 10, 100})
   161  	}
   162  	require.Eventually(t, requestsUpdated, time.Minute, 100*time.Millisecond)
   163  
   164  	// Test 3: commit blocks and verify snapshots are generated for blocknumber=5 and blocknumber=10
   165  	lastBlock := testutilCommitBlocks(t, l, bg, 10, gbHash)
   166  	// verify snapshot has been generated for blocknumber=5
   167  	exists, err := kvledger.snapshotExists(5)
   168  	require.NoError(t, err)
   169  	require.True(t, exists)
   170  	// verify snapshot is eventually generated blocknumber=10
   171  	snapshotExists := func() bool {
   172  		exists, err := kvledger.snapshotExists(10)
   173  		require.NoError(t, err)
   174  		return exists
   175  	}
   176  	require.Eventually(t, snapshotExists, time.Minute, 100*time.Millisecond)
   177  
   178  	// Test 4: commit blocks and submit a request with default value (blocknumber=0) to trigger snapshot generation
   179  	// for the latest committed block
   180  	lastBlock = testutilCommitBlocks(t, l, bg, 20, protoutil.BlockHeaderHash(lastBlock.Header))
   181  	require.NoError(t, l.SubmitSnapshotRequest(0))
   182  	// wait until snapshot is generated for blocknumber=20
   183  	snapshotExists = func() bool {
   184  		exists, err := kvledger.snapshotExists(20)
   185  		require.NoError(t, err)
   186  		return exists
   187  	}
   188  	require.Eventually(t, snapshotExists, time.Minute, 100*time.Millisecond)
   189  
   190  	// wait until previous snapshotDone event deletes the request since it is in a separate goroutine
   191  	requestsUpdated = func() bool {
   192  		requests, err := l.PendingSnapshotRequests()
   193  		require.NoError(t, err)
   194  		return equal(requests, []uint64{100})
   195  	}
   196  	require.Eventually(t, requestsUpdated, time.Minute, 100*time.Millisecond)
   197  
   198  	// prepare to test recoverSnapshot when a ledger is reopened
   199  	// commit blocks upto block number 25 and add a request for block number 25 to the leveldb directly
   200  	// snapshot should not be generated and will be recovered after the ledger is reopened
   201  	testutilCommitBlocks(t, l, bg, 25, protoutil.BlockHeaderHash(lastBlock.Header))
   202  	require.NoError(t, kvledger.snapshotMgr.snapshotRequestBookkeeper.dbHandle.Put(encodeSnapshotRequestKey(25), []byte{}, true))
   203  	exists, err = kvledger.snapshotExists(25)
   204  	require.NoError(t, err)
   205  	require.False(t, exists)
   206  
   207  	// reopen the provider and ledger
   208  	l.Close()
   209  	provider.Close()
   210  	provider2 := testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{})
   211  	defer provider2.Close()
   212  	l2, err := provider2.Open(ledgerID)
   213  	require.NoError(t, err)
   214  	kvledger2 := l2.(*kvLedger)
   215  
   216  	// Test 5: verify snapshot for blocknumber=25 is generated at startup time and pending snapshot requests are correct
   217  	snapshotExists = func() bool {
   218  		exists, err := kvledger2.snapshotExists(25)
   219  		require.NoError(t, err)
   220  		return exists
   221  	}
   222  	require.Eventually(t, snapshotExists, time.Minute, 100*time.Millisecond)
   223  
   224  	// wait until previous snapshotDone event deletes the request since it is in a separate goroutine
   225  	requestsUpdated = func() bool {
   226  		requests, err := l2.PendingSnapshotRequests()
   227  		require.NoError(t, err)
   228  		return equal(requests, []uint64{100})
   229  	}
   230  	require.Eventually(t, requestsUpdated, time.Minute, 100*time.Millisecond)
   231  }
   232  
   233  func TestSnapshotMgmtConcurrency(t *testing.T) {
   234  	conf, cleanup := testConfig(t)
   235  	defer cleanup()
   236  	provider := testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{})
   237  	defer provider.Close()
   238  
   239  	ledgerID := "testsnapshotmgmtconcurrency"
   240  	bg, gb := testutil.NewBlockGenerator(t, ledgerID, false)
   241  	gbHash := protoutil.BlockHeaderHash(gb.Header)
   242  	l, err := provider.CreateFromGenesisBlock(gb)
   243  	require.NoError(t, err)
   244  	kvledger := l.(*kvLedger)
   245  	defer kvledger.Close()
   246  
   247  	testutilCommitBlocks(t, l, bg, 5, gbHash)
   248  
   249  	// Artificially, send event to background goroutine to indicate that commit for block 6 has started
   250  	// and then submit snapshot request for block 0, while not sending the event for commit done for block 6
   251  	kvledger.snapshotMgr.events <- &event{typ: commitStart, blockNumber: 6}
   252  	<-kvledger.snapshotMgr.commitProceed
   253  
   254  	require.NoError(t, kvledger.SubmitSnapshotRequest(0))
   255  	require.Eventually(t,
   256  		func() bool {
   257  			r, err := kvledger.snapshotMgr.snapshotRequestBookkeeper.smallestRequest()
   258  			require.NoError(t, err)
   259  			return r == 6
   260  		},
   261  		10*time.Millisecond, 1*time.Millisecond,
   262  	)
   263  }
   264  
   265  func TestSnapshotMgrShutdown(t *testing.T) {
   266  	conf, cleanup := testConfig(t)
   267  	defer cleanup()
   268  	provider := testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{})
   269  	defer provider.Close()
   270  
   271  	ledgerID := "testsnapshotmgrshutdown"
   272  	_, gb := testutil.NewBlockGenerator(t, ledgerID, false)
   273  	l, err := provider.CreateFromGenesisBlock(gb)
   274  	require.NoError(t, err)
   275  	kvledger := l.(*kvLedger)
   276  
   277  	// close ledger to shutdown snapshotMgr
   278  	l.Close()
   279  
   280  	// verify snapshotMgr.stopped and channels are stopped
   281  	require.True(t, kvledger.snapshotMgr.stopped)
   282  	require.PanicsWithError(
   283  		t,
   284  		"send on closed channel",
   285  		func() { kvledger.snapshotMgr.events <- &event{typ: commitStart, blockNumber: 1} },
   286  	)
   287  	require.PanicsWithError(
   288  		t,
   289  		"send on closed channel",
   290  		func() { kvledger.snapshotMgr.commitProceed <- struct{}{} },
   291  	)
   292  	require.PanicsWithError(
   293  		t,
   294  		"send on closed channel",
   295  		func() { kvledger.snapshotMgr.requestResponses <- &requestResponse{} },
   296  	)
   297  
   298  	// close ledger again is fine
   299  	l.Close()
   300  }
   301  
   302  func TestSnapshotRequestsErrorPaths(t *testing.T) {
   303  	conf, cleanup := testConfig(t)
   304  	defer cleanup()
   305  	provider := testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{})
   306  
   307  	// create a ledger with genesis block
   308  	ledgerID := "testsnapshotrequestserrorpaths"
   309  	bg, gb := testutil.NewBlockGenerator(t, ledgerID, false)
   310  	gbHash := protoutil.BlockHeaderHash(gb.Header)
   311  	l, err := provider.CreateFromGenesisBlock(gb)
   312  	require.NoError(t, err)
   313  	defer l.Close()
   314  
   315  	// commit blocks and submit a request
   316  	testutilCommitBlocks(t, l, bg, 5, gbHash)
   317  	require.NoError(t, l.SubmitSnapshotRequest(5))
   318  
   319  	// wait until snapshot blocknumber=5 is generated
   320  	kvledger := l.(*kvLedger)
   321  	snapshotExists := func() bool {
   322  		exists, err := kvledger.snapshotExists(5)
   323  		require.NoError(t, err)
   324  		return exists
   325  	}
   326  	require.Eventually(t, snapshotExists, time.Minute, 100*time.Millisecond)
   327  
   328  	// verify various error paths
   329  	require.EqualError(t, l.SubmitSnapshotRequest(5), "snapshot already generated for block number 5")
   330  
   331  	require.EqualError(t, l.SubmitSnapshotRequest(3), "requested snapshot for block number 3 cannot be less than the last committed block number 5")
   332  
   333  	require.NoError(t, l.SubmitSnapshotRequest(20))
   334  	require.EqualError(t, l.SubmitSnapshotRequest(20), "duplicate snapshot request for block number 20")
   335  
   336  	require.EqualError(t, l.CancelSnapshotRequest(100), "no snapshot request exists for block number 100")
   337  
   338  	provider.Close()
   339  
   340  	_, err = provider.Open(ledgerID)
   341  	require.Contains(t, err.Error(), "leveldb: closed")
   342  
   343  	err = l.SubmitSnapshotRequest(20)
   344  	require.Contains(t, err.Error(), "leveldb: closed")
   345  
   346  	err = l.CancelSnapshotRequest(1)
   347  	require.Contains(t, err.Error(), "leveldb: closed")
   348  
   349  	_, err = l.PendingSnapshotRequests()
   350  	require.EqualError(t, err, "internal leveldb error while obtaining db iterator: leveldb: closed")
   351  }
   352  
   353  func equal(slice1 []uint64, slice2 []uint64) bool {
   354  	if len(slice1) != len(slice2) {
   355  		return false
   356  	}
   357  	for i := range slice1 {
   358  		if slice1[i] != slice2[i] {
   359  			return false
   360  		}
   361  	}
   362  	return true
   363  }
   364  
   365  func testutilCommitBlocks(t *testing.T, l ledger.PeerLedger, bg *testutil.BlockGenerator, finalBlockNum uint64, previousBlockHash []byte) *common.Block {
   366  	bcInfo, err := l.GetBlockchainInfo()
   367  	require.NoError(t, err)
   368  	startBlockNum := bcInfo.Height
   369  
   370  	var block *common.Block
   371  	for i := startBlockNum; i <= finalBlockNum; i++ {
   372  		txid := util.GenerateUUID()
   373  		simulator, err := l.NewTxSimulator(txid)
   374  		require.NoError(t, err)
   375  		require.NoError(t, simulator.SetState("ns1", fmt.Sprintf("key%d", i), []byte(fmt.Sprintf("value%d", i))))
   376  		simulator.Done()
   377  		require.NoError(t, err)
   378  		simRes, err := simulator.GetTxSimulationResults()
   379  		require.NoError(t, err)
   380  		pubSimBytes, err := simRes.GetPubSimulationBytes()
   381  		require.NoError(t, err)
   382  		block = bg.NextBlock([][]byte{pubSimBytes})
   383  		require.NoError(t, l.CommitLegacy(&ledger.BlockAndPvtData{Block: block}, &ledger.CommitOptions{}))
   384  
   385  		bcInfo, err := l.GetBlockchainInfo()
   386  		require.NoError(t, err)
   387  		blockHash := protoutil.BlockHeaderHash(block.Header)
   388  		require.Equal(t, &common.BlockchainInfo{
   389  			Height: uint64(i + 1), CurrentBlockHash: blockHash, PreviousBlockHash: previousBlockHash,
   390  		}, bcInfo)
   391  		previousBlockHash = blockHash
   392  	}
   393  
   394  	return block
   395  }