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 }