github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/kvledger/txmgmt/statedb/statecouchdb/redolog_test.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package statecouchdb
     8  
     9  import (
    10  	"fmt"
    11  	"io/ioutil"
    12  	"os"
    13  	"testing"
    14  
    15  	"github.com/davecgh/go-spew/spew"
    16  	"github.com/hechain20/hechain/core/ledger/internal/version"
    17  	"github.com/hechain20/hechain/core/ledger/kvledger/txmgmt/statedb"
    18  	"github.com/stretchr/testify/require"
    19  )
    20  
    21  func TestRedoLogger(t *testing.T) {
    22  	provider, cleanup := redologTestSetup(t)
    23  	defer cleanup()
    24  
    25  	loggers := []*redoLogger{}
    26  	records := []*redoRecord{}
    27  
    28  	verifyLogRecords := func() {
    29  		for i := 0; i < len(loggers); i++ {
    30  			retrievedRec, err := loggers[i].load()
    31  			require.NoError(t, err)
    32  			require.Equal(t, records[i], retrievedRec)
    33  		}
    34  	}
    35  
    36  	// write log records for multiple channels
    37  	for i := 0; i < 10; i++ {
    38  		logger := provider.newRedoLogger(fmt.Sprintf("channel-%d", i))
    39  		rec, err := logger.load()
    40  		require.NoError(t, err)
    41  		require.Nil(t, rec)
    42  		loggers = append(loggers, logger)
    43  		batch := statedb.NewUpdateBatch()
    44  		blkNum := uint64(i)
    45  		batch.Put("ns1", "key1", []byte("value1"), version.NewHeight(blkNum, 1))
    46  		batch.Put("ns2", string([]byte{0x00, 0xff}), []byte("value3"), version.NewHeight(blkNum, 3))
    47  		batch.PutValAndMetadata("ns2", string([]byte{0x00, 0xff}), []byte("value3"), []byte("metadata"), version.NewHeight(blkNum, 4))
    48  		batch.Delete("ns2", string([]byte{0xff, 0xff}), version.NewHeight(blkNum, 5))
    49  		rec = &redoRecord{
    50  			UpdateBatch: batch,
    51  			Version:     version.NewHeight(blkNum, 10),
    52  		}
    53  		records = append(records, rec)
    54  		require.NoError(t, logger.persist(rec))
    55  	}
    56  
    57  	verifyLogRecords()
    58  	// overwrite logrecord for one channel
    59  	records[5].UpdateBatch = statedb.NewUpdateBatch()
    60  	records[5].Version = version.NewHeight(5, 5)
    61  	require.NoError(t, loggers[5].persist(records[5]))
    62  	verifyLogRecords()
    63  }
    64  
    65  func TestCouchdbRedoLogger(t *testing.T) {
    66  	vdbEnv.init(t, nil)
    67  	defer vdbEnv.cleanup()
    68  
    69  	// commitToRedologAndRestart - a helper function that commits directly to redologs and restart the statedb
    70  	commitToRedologAndRestart := func(newVal string, version *version.Height) {
    71  		batch := statedb.NewUpdateBatch()
    72  		batch.Put("ns1", "key1", []byte(newVal), version)
    73  		db, err := vdbEnv.DBProvider.GetDBHandle("testcouchdbredologger", nil)
    74  		require.NoError(t, err)
    75  		vdb := db.(*VersionedDB)
    76  		require.NoError(t,
    77  			vdb.redoLogger.persist(
    78  				&redoRecord{
    79  					UpdateBatch: batch,
    80  					Version:     version,
    81  				},
    82  			),
    83  		)
    84  		vdbEnv.closeAndReopen()
    85  	}
    86  	// verifyExpectedVal - a helper function that verifies the statedb contents
    87  	verifyExpectedVal := func(expectedVal string, expectedSavepoint *version.Height) {
    88  		db, err := vdbEnv.DBProvider.GetDBHandle("testcouchdbredologger", nil)
    89  		require.NoError(t, err)
    90  		vdb := db.(*VersionedDB)
    91  		vv, err := vdb.GetState("ns1", "key1")
    92  		require.NoError(t, err)
    93  		require.Equal(t, expectedVal, string(vv.Value))
    94  		savepoint, err := vdb.GetLatestSavePoint()
    95  		require.NoError(t, err)
    96  		require.Equal(t, expectedSavepoint, savepoint)
    97  	}
    98  
    99  	// initialize statedb with initial set of writes
   100  	db, err := vdbEnv.DBProvider.GetDBHandle("testcouchdbredologger", nil)
   101  	if err != nil {
   102  		t.Fatalf("Failed to get database handle: %s", err)
   103  	}
   104  	vdb := db.(*VersionedDB)
   105  	batch1 := statedb.NewUpdateBatch()
   106  	batch1.Put("ns1", "key1", []byte("value1"), version.NewHeight(1, 1))
   107  	require.NoError(t, vdb.ApplyUpdates(batch1, version.NewHeight(1, 1)))
   108  
   109  	// make redolog one block ahead than statedb - upon restart the redolog should get applied
   110  	commitToRedologAndRestart("value2", version.NewHeight(2, 1))
   111  	verifyExpectedVal("value2", version.NewHeight(2, 1))
   112  
   113  	// make redolog two blocks ahead than statedb - upon restart the redolog should be ignored
   114  	commitToRedologAndRestart("value3", version.NewHeight(4, 1))
   115  	verifyExpectedVal("value2", version.NewHeight(2, 1))
   116  
   117  	// make redolog one block behind than statedb - upon restart the redolog should be ignored
   118  	commitToRedologAndRestart("value3", version.NewHeight(1, 5))
   119  	verifyExpectedVal("value2", version.NewHeight(2, 1))
   120  
   121  	// A nil height should cause skipping the writing of redo-record
   122  	db, _ = vdbEnv.DBProvider.GetDBHandle("testcouchdbredologger", nil)
   123  	vdb = db.(*VersionedDB)
   124  	require.NoError(t, vdb.ApplyUpdates(batch1, nil))
   125  	record, err := vdb.redoLogger.load()
   126  	require.NoError(t, err)
   127  	require.Equal(t, version.NewHeight(1, 5), record.Version)
   128  	require.Equal(t, []byte("value3"), record.UpdateBatch.Get("ns1", "key1").Value)
   129  
   130  	// A batch that does not contain PostOrderWrites should cause skipping the writing of redo-record
   131  	db, _ = vdbEnv.DBProvider.GetDBHandle("testcouchdbredologger", nil)
   132  	vdb = db.(*VersionedDB)
   133  	batchWithNoGeneratedWrites := batch1
   134  	batchWithNoGeneratedWrites.ContainsPostOrderWrites = false
   135  	require.NoError(t, vdb.ApplyUpdates(batchWithNoGeneratedWrites, version.NewHeight(2, 5)))
   136  	record, err = vdb.redoLogger.load()
   137  	require.NoError(t, err)
   138  	require.Equal(t, version.NewHeight(1, 5), record.Version)
   139  	require.Equal(t, []byte("value3"), record.UpdateBatch.Get("ns1", "key1").Value)
   140  
   141  	// A batch that contains PostOrderWrites should cause writing of redo-record
   142  	db, _ = vdbEnv.DBProvider.GetDBHandle("testcouchdbredologger", nil)
   143  	vdb = db.(*VersionedDB)
   144  	batchWithGeneratedWrites := batch1
   145  	batchWithGeneratedWrites.ContainsPostOrderWrites = true
   146  	require.NoError(t, vdb.ApplyUpdates(batchWithNoGeneratedWrites, version.NewHeight(3, 4)))
   147  	record, err = vdb.redoLogger.load()
   148  	require.NoError(t, err)
   149  	require.Equal(t, version.NewHeight(3, 4), record.Version)
   150  	require.Equal(t, []byte("value1"), record.UpdateBatch.Get("ns1", "key1").Value)
   151  }
   152  
   153  func redologTestSetup(t *testing.T) (p *redoLoggerProvider, cleanup func()) {
   154  	dbPath, err := ioutil.TempDir("", "redolog")
   155  	if err != nil {
   156  		t.Fatalf("Failed to create redo log directory: %s", err)
   157  	}
   158  	p, err = newRedoLoggerProvider(dbPath)
   159  	require.NoError(t, err)
   160  	cleanup = func() {
   161  		p.close()
   162  		require.NoError(t, os.RemoveAll(dbPath))
   163  	}
   164  	return
   165  }
   166  
   167  func TestReadExistingRedoRecord(t *testing.T) {
   168  	b, err := ioutil.ReadFile("testdata/persisted_redo_record")
   169  	require.NoError(t, err)
   170  	rec, err := decodeRedologVal(b)
   171  	require.NoError(t, err)
   172  	t.Logf("rec = %s", spew.Sdump(rec))
   173  	require.Equal(t, constructSampleRedoRecord(), rec)
   174  }
   175  
   176  func constructSampleRedoRecord() *redoRecord {
   177  	batch := statedb.NewUpdateBatch()
   178  	batch.Put("ns1", "key1", []byte("value1"), version.NewHeight(1, 1))
   179  	batch.Put("ns2", string([]byte{0x00, 0xff}), []byte("value3"), version.NewHeight(3, 3))
   180  	batch.PutValAndMetadata("ns2", string([]byte{0x00, 0xff}), []byte("value3"), []byte("metadata"), version.NewHeight(4, 4))
   181  	batch.Delete("ns2", string([]byte{0xff, 0xff}), version.NewHeight(5, 5))
   182  	return &redoRecord{
   183  		UpdateBatch: batch,
   184  		Version:     version.NewHeight(10, 10),
   185  	}
   186  }