github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/iavl/mutable_tree_fbc_test.go (about)

     1  package iavl
     2  
     3  import (
     4  	"fmt"
     5  	"math/rand"
     6  	"sync"
     7  	"testing"
     8  	"time"
     9  
    10  	db "github.com/fibonacci-chain/fbc/libs/tm-db"
    11  	"github.com/stretchr/testify/require"
    12  )
    13  
    14  func TestSaveVersion(t *testing.T) {
    15  	EnableAsyncCommit = true
    16  	defer func() {
    17  		EnableAsyncCommit = false
    18  		treeMap.resetMap()
    19  	}()
    20  	originData := make(map[string]string)
    21  	for i := 0; i < 100; i++ {
    22  		key := randstr(5)
    23  		value := randstr(5)
    24  		originData[key] = value
    25  	}
    26  
    27  	k1 := "k1"
    28  	k2 := "k2"
    29  	k3 := "k3"
    30  	value := "Fred"
    31  	originData[k1] = value
    32  	originData[k2] = value
    33  	originData[k3] = value
    34  
    35  	modifiedData := make(map[string]string)
    36  	for k, v := range originData {
    37  		modifiedData[k] = v
    38  	}
    39  	modifiedValue := "hhhhh"
    40  	modifiedData[k1] = modifiedValue
    41  	modifiedData[k2] = modifiedValue
    42  	modifiedData[k3] = modifiedValue
    43  
    44  	testTree := func(data map[string]string, tree *ImmutableTree) {
    45  		for k, v := range data {
    46  			_, value := tree.GetWithIndex([]byte(k))
    47  			require.Equal(t, value, []byte(v))
    48  		}
    49  	}
    50  
    51  	tree := newTestTree(t, false, 100, "test")
    52  
    53  	//_, _, err = tree.SaveVersion()
    54  	//require.NoError(t, err)
    55  	for k, v := range originData {
    56  		tree.Set([]byte(k), []byte(v))
    57  	}
    58  	_, _, _, err := tree.SaveVersion(false)
    59  	require.NoError(t, err)
    60  	oldVersion := tree.version
    61  	tree.Set([]byte(k1), []byte(modifiedValue))
    62  	tree.Set([]byte(k2), []byte(modifiedValue))
    63  	tree.Set([]byte(k3), []byte(modifiedValue))
    64  	tree.Remove([]byte(k1))
    65  	delete(modifiedData, k1)
    66  
    67  	_, _, _, err = tree.SaveVersion(false)
    68  	require.NoError(t, err)
    69  
    70  	oldTree, err := tree.GetImmutable(oldVersion)
    71  	require.NoError(t, err)
    72  
    73  	newTree, err := tree.GetImmutable(tree.version)
    74  	//require.Equal(t, oldTree.nodeSize(), newTree.nodeSize())
    75  	testTree(originData, oldTree)
    76  	testTree(modifiedData, newTree)
    77  
    78  	for i := 0; i < 10; i++ {
    79  		_, _, _, err = tree.SaveVersion(false)
    80  		require.NoError(t, err)
    81  	}
    82  	for i := 0; i < 200; i++ {
    83  		_, _, _, err = tree.SaveVersion(false)
    84  		require.NoError(t, err)
    85  		for j := 0; j < 8; j++ {
    86  			tree, err := tree.GetImmutable(tree.version - int64(j))
    87  			require.NoError(t, err)
    88  			testTree(modifiedData, tree)
    89  		}
    90  
    91  	}
    92  
    93  }
    94  
    95  func TestSaveVersionCommitIntervalHeight(t *testing.T) {
    96  	EnableAsyncCommit = true
    97  	defer func() {
    98  		EnableAsyncCommit = false
    99  		treeMap.resetMap()
   100  	}()
   101  	tree := newTestTree(t, false, 10000, "test")
   102  
   103  	_, _, _, err := tree.SaveVersion(false)
   104  	require.NoError(t, err)
   105  	keys, _ := initSetTree(tree)
   106  	_, k2, _ := keys[0], keys[1], keys[2]
   107  
   108  	_, _, _, err = tree.SaveVersion(false)
   109  	require.NoError(t, err)
   110  	tree.Set([]byte(k2), []byte("k22"))
   111  	_, _, _, err = tree.SaveVersion(false)
   112  
   113  	tree.ndb.sanityCheckHandleOrphansResult(tree.version + 1)
   114  	tree.ndb.oi.enqueueResult(tree.version)
   115  
   116  	require.Equal(t, 5, tree.ndb.prePersistNodeCacheLen()+tree.ndb.nc.nodeCacheLen())
   117  	require.Equal(t, 3, tree.ndb.oi.orphanNodeCacheLen())
   118  
   119  	_, _, _, err = tree.SaveVersion(false)
   120  	require.NoError(t, err)
   121  	require.NoError(t, err)
   122  	for i := 0; i < 96; i++ {
   123  		_, _, _, err = tree.SaveVersion(false)
   124  		require.NoError(t, err)
   125  	}
   126  
   127  	_, _, _, err = tree.SaveVersion(false)
   128  	require.NoError(t, err)
   129  	require.Equal(t, 0, tree.ndb.prePersistNodeCacheLen())
   130  	require.Equal(t, 0, tree.ndb.oi.orphanNodeCacheLen())
   131  
   132  	//require.Equal(t, 5, len(tree.ndb.nodeCache)+len(tree.ndb.tempPrePersistNodeCache))
   133  	tree.Set([]byte("k5"), []byte("5555555555"))
   134  	for i := 0; i < 98; i++ {
   135  		_, _, _, err = tree.SaveVersion(false)
   136  		require.NoError(t, err)
   137  	}
   138  
   139  	_, _, _, err = tree.SaveVersion(false)
   140  	require.NoError(t, err)
   141  
   142  }
   143  
   144  func TestConcurrentGetNode(t *testing.T) {
   145  	EnableAsyncCommit = true
   146  	defer func() {
   147  		EnableAsyncCommit = false
   148  		treeMap.resetMap()
   149  	}()
   150  	originData := make(map[string]string)
   151  	var dataKey []string
   152  	var dataLock sync.RWMutex
   153  	for i := 0; i < 10000; i++ {
   154  		key := randstr(5)
   155  		value := randstr(5)
   156  		originData[key] = value
   157  		dataKey = append(dataKey, key)
   158  	}
   159  
   160  	tree := newTestTree(t, false, 10000, "test")
   161  
   162  	//_, _, err = tree.SaveVersion()
   163  	//require.NoError(t, err)
   164  	for k, v := range originData {
   165  		tree.Set([]byte(k), []byte(v))
   166  	}
   167  	_, _, _, err := tree.SaveVersion(false)
   168  	require.NoError(t, err)
   169  	wg := sync.WaitGroup{}
   170  	const num = 50
   171  	wg.Add(num)
   172  	go func() {
   173  		for i := 0; i < num; i++ {
   174  			go func() {
   175  				queryTree, newErr := tree.GetImmutable(tree.version)
   176  				require.Nil(t, newErr)
   177  				idx := rand.Int() % len(dataKey)
   178  				_, value := queryTree.GetWithIndex([]byte(dataKey[idx]))
   179  				dataLock.RLock()
   180  				if originData[string(dataKey[idx])] != string(value) {
   181  					//fmt.Println("not equal", originData[string(dataKey[idx])], string(value))
   182  					time.Sleep(time.Millisecond * 10)
   183  				}
   184  				dataLock.RUnlock()
   185  				_, value = queryTree.GetWithIndex([]byte(dataKey[idx]))
   186  				dataLock.RLock()
   187  				require.Equal(t, originData[string(dataKey[idx])], string(value))
   188  				dataLock.RUnlock()
   189  				wg.Done()
   190  			}()
   191  		}
   192  	}()
   193  	for i := 0; i < 10; i++ {
   194  		time.Sleep(time.Millisecond * 10)
   195  		for j := 0; j < 100; j++ {
   196  			key := randstr(5)
   197  			value := randstr(5)
   198  			dataLock.Lock()
   199  			originData[key] = value
   200  			dataLock.Unlock()
   201  			tree.Set([]byte(key), []byte(value))
   202  
   203  		}
   204  		_, _, _, err = tree.SaveVersion(false)
   205  		require.NoError(t, err)
   206  
   207  	}
   208  	wg.Wait()
   209  }
   210  
   211  func TestShareNode(t *testing.T) {
   212  	EnableAsyncCommit = true
   213  	CommitIntervalHeight = 10
   214  	defer func() {
   215  		EnableAsyncCommit = false
   216  		CommitIntervalHeight = 100
   217  		treeMap.resetMap()
   218  	}()
   219  
   220  	tree := newTestTree(t, false, 10000, "test")
   221  
   222  	_, _, _, err := tree.SaveVersion(false)
   223  	require.NoError(t, err)
   224  
   225  	keys, _ := initSetTree(tree)
   226  	_, k2, _ := keys[0], keys[1], keys[2]
   227  
   228  	_, oldVersion, _, err := tree.SaveVersion(false)
   229  	require.NoError(t, err)
   230  	tree.Set([]byte(k2), []byte("k2new"))
   231  	_, _, _, err = tree.SaveVersion(false)
   232  
   233  	oldTree, err := tree.GetImmutable(oldVersion)
   234  	require.NoError(t, err)
   235  
   236  	oldK1Node := oldTree.root.getLeftNode(oldTree)
   237  	newK1Node := tree.root.getLeftNode(tree.ImmutableTree)
   238  	require.Equal(t, oldK1Node, newK1Node)
   239  	nodeDBK1Node := tree.ndb.GetNode(oldK1Node.hash)
   240  	require.Equal(t, oldK1Node, nodeDBK1Node)
   241  
   242  	for i := 0; i < 10; i++ {
   243  		_, _, _, err = tree.SaveVersion(false)
   244  		require.NoError(t, err)
   245  	}
   246  	oldK1Node = oldTree.root.getLeftNode(oldTree)
   247  	newK1Node = tree.root.getLeftNode(tree.ImmutableTree)
   248  	require.Equal(t, oldK1Node, newK1Node)
   249  	nodeDBK1Node = tree.ndb.GetNode(oldK1Node.hash)
   250  	require.Equal(t, oldK1Node, nodeDBK1Node)
   251  }
   252  
   253  func TestParseDBName(t *testing.T) {
   254  	str := "staking"
   255  	memDB := db.NewMemDB()
   256  	prefixDB := db.NewPrefixDB(memDB, []byte(str))
   257  
   258  	result := ParseDBName(prefixDB)
   259  	require.Equal(t, str, result)
   260  
   261  	result2 := ParseDBName(memDB)
   262  	require.Equal(t, "", result2)
   263  }
   264  
   265  func TestPruningHistoryState(t *testing.T) {
   266  	EnableAsyncCommit = true
   267  	EnablePruningHistoryState = true
   268  	defer func() {
   269  		EnableAsyncCommit = false
   270  		EnablePruningHistoryState = false
   271  		treeMap.resetMap()
   272  	}()
   273  	tree := newTestTree(t, false, 10000, "test")
   274  	keys, _ := initSetTree(tree)
   275  	_, k2, _ := keys[0], keys[1], keys[2]
   276  
   277  	_, _, _, err := tree.SaveVersion(false)
   278  	require.NoError(t, err)
   279  
   280  	batchSaveVersion(t, tree, int(CommitIntervalHeight))
   281  
   282  	v2New := []byte("v22")
   283  	tree.Set(k2, v2New)
   284  	_, _, _, err = tree.SaveVersion(false)
   285  	require.NoError(t, err)
   286  
   287  	batchSaveVersion(t, tree, minHistoryStateNum*int(CommitIntervalHeight)-2)
   288  
   289  	tree.commitCh <- commitEvent{-1, nil, nil, nil, nil, 0, nil, nil, false}
   290  	tree.waitCurrentPruningScheduleDone()
   291  
   292  	iTree, err := tree.GetImmutable(CommitIntervalHeight * (minHistoryStateNum - 1))
   293  	require.NoError(t, err)
   294  	require.NotNil(t, iTree)
   295  	_, v := iTree.GetWithIndex(k2)
   296  	require.Equal(t, v2New, v)
   297  
   298  	iTree, err = tree.GetImmutable(CommitIntervalHeight * 1)
   299  	require.Error(t, err)
   300  	require.Nil(t, iTree)
   301  
   302  	nodeCount := 0
   303  	tree.ndb.traverseNodes(func(hash []byte, node *Node) {
   304  		nodeCount++
   305  	})
   306  	require.Equal(t, 5, nodeCount)
   307  
   308  	orphansCount := 0
   309  	tree.ndb.traverseOrphans(func(k, v []byte) {
   310  		orphansCount++
   311  	})
   312  	require.Equal(t, 0, orphansCount)
   313  }
   314  
   315  func batchSaveVersion(t *testing.T, tree *MutableTree, n int) {
   316  	for i := 0; i < n; i++ {
   317  		_, _, _, err := tree.SaveVersion(false)
   318  		require.NoError(t, err)
   319  	}
   320  }
   321  
   322  func openLog(moduleName string) {
   323  	SetLogFunc(func(level int, format string, args ...interface{}) {
   324  		if level == IavlInfo {
   325  			fmt.Printf(format, args...)
   326  			fmt.Printf("\n")
   327  		}
   328  	})
   329  	OutputModules = make(map[string]int)
   330  	OutputModules[moduleName] = 1
   331  }
   332  
   333  func TestPruningHistoryStateRandom(t *testing.T) {
   334  	EnableAsyncCommit = true
   335  	EnablePruningHistoryState = true
   336  	defer func() {
   337  		EnableAsyncCommit = false
   338  		EnablePruningHistoryState = false
   339  		treeMap.resetMap()
   340  	}()
   341  	tree := newTestTree(t, false, 10000, "test")
   342  	keys, _ := initSetTree(tree)
   343  	k1, k2, k3 := keys[0], keys[1], keys[2]
   344  
   345  	_, _, _, err := tree.SaveVersion(false)
   346  	require.NoError(t, err)
   347  
   348  	for i := 0; i < 10000; i++ {
   349  		tree.Set(k2, randBytes(i%64+1))
   350  		_, _, _, err := tree.SaveVersion(false)
   351  		require.NoError(t, err)
   352  	}
   353  
   354  	tree.commitCh <- commitEvent{-1, nil, nil, nil, nil, 0, nil, nil, false}
   355  	tree.waitCurrentPruningScheduleDone()
   356  
   357  	nodeCount := 0
   358  	tree.ndb.traverseNodes(func(hash []byte, node *Node) {
   359  		nodeCount++
   360  	})
   361  	require.Equal(t, (minHistoryStateNum-1)*3+5, nodeCount)
   362  
   363  	orphansCount := 0
   364  	tree.ndb.traverseOrphans(func(k, v []byte) {
   365  		orphansCount++
   366  	})
   367  	require.Equal(t, (minHistoryStateNum-1)*3, orphansCount)
   368  
   369  	for i := 0; i < 10000; i++ {
   370  		tree.Set(k1, randBytes(i%64+1))
   371  		tree.Set(k2, randBytes(i%64+1))
   372  		tree.Set(k3, randBytes(i%64+1))
   373  		_, _, _, err := tree.SaveVersion(false)
   374  		require.NoError(t, err)
   375  	}
   376  
   377  	tree.waitCurrentPruningScheduleDone()
   378  
   379  	nodeCount = 0
   380  	tree.ndb.traverseNodes(func(hash []byte, node *Node) {
   381  		nodeCount++
   382  	})
   383  	require.Equal(t, minHistoryStateNum*5, nodeCount)
   384  
   385  	orphansCount = 0
   386  	tree.ndb.traverseOrphans(func(k, v []byte) {
   387  		orphansCount++
   388  	})
   389  	require.Equal(t, (minHistoryStateNum-1)*5, orphansCount)
   390  }
   391  
   392  func TestConcurrentQuery(t *testing.T) {
   393  	EnableAsyncCommit = true
   394  	EnablePruningHistoryState = true
   395  	CommitIntervalHeight = 5
   396  	defer func() {
   397  		EnableAsyncCommit = false
   398  		EnablePruningHistoryState = false
   399  		CommitIntervalHeight = 100
   400  		treeMap.resetMap()
   401  	}()
   402  	originData := make(map[string]string)
   403  	var dataKey []string
   404  	for i := 0; i < 100000; i++ {
   405  		key := randstr(5)
   406  		value := randstr(5)
   407  		originData[key] = value
   408  		dataKey = append(dataKey, key)
   409  	}
   410  
   411  	tree := newTestTree(t, false, 10000, "test")
   412  
   413  	for k, v := range originData {
   414  		tree.Set([]byte(k), []byte(v))
   415  	}
   416  	_, _, _, err := tree.SaveVersion(false)
   417  	require.NoError(t, err)
   418  	const num = 1000000
   419  	queryEnd := false
   420  	endCh := make(chan struct{})
   421  	go func() {
   422  		ch := make(chan struct{}, 20)
   423  		wg := sync.WaitGroup{}
   424  		wg.Add(num)
   425  		for i := 0; i < num; i++ {
   426  			ch <- struct{}{}
   427  			go func() {
   428  				queryVersion := tree.version
   429  				//fmt.Println(time.Now().String(),"query version:", queryVersion)
   430  				queryTree, newErr := tree.GetImmutable(queryVersion)
   431  				require.Nil(t, newErr, "query:%d current:%d\n", queryVersion, tree.version)
   432  				idx := rand.Int() % len(dataKey)
   433  				_, value := queryTree.GetWithIndex([]byte(dataKey[idx]))
   434  				require.NotNil(t, value)
   435  				require.NotEqual(t, []byte{}, value)
   436  				wg.Done()
   437  				<-ch
   438  			}()
   439  		}
   440  		wg.Wait()
   441  		queryEnd = true
   442  	}()
   443  	go func() {
   444  		for i := 0; ; i++ {
   445  			fmt.Println(time.Now().String(), "current version:", tree.version)
   446  			for j := 0; j < 100; j++ {
   447  				key := dataKey[rand.Intn(len(dataKey))]
   448  				value := randstr(5)
   449  				originData[key] = value
   450  				tree.Set([]byte(key), []byte(value))
   451  			}
   452  			_, _, _, err = tree.SaveVersion(false)
   453  			require.NoError(t, err)
   454  			if queryEnd {
   455  				break
   456  			}
   457  		}
   458  		endCh <- struct{}{}
   459  	}()
   460  	<-endCh
   461  }
   462  
   463  func TestStopTree(t *testing.T) {
   464  	EnableAsyncCommit = true
   465  	EnablePruningHistoryState = true
   466  	defer func() {
   467  		EnableAsyncCommit = false
   468  		EnablePruningHistoryState = false
   469  		treeMap.resetMap()
   470  	}()
   471  	tree := newTestTree(t, false, 10000, "test")
   472  	initSetTree(tree)
   473  
   474  	_, _, _, err := tree.SaveVersion(false)
   475  	require.NoError(t, err)
   476  	tree.StopTree()
   477  	require.Equal(t, 5, tree.ndb.nc.nodeCacheLen())
   478  }
   479  
   480  func TestLog(t *testing.T) {
   481  	defer func() {
   482  		treeMap.resetMap()
   483  	}()
   484  	tree := newTestTree(t, false, 10000, "test")
   485  	dbRCount := tree.GetDBReadCount()
   486  	dbWCount := tree.GetDBWriteCount()
   487  	nodeRCount := tree.GetNodeReadCount()
   488  	require.Zero(t, dbRCount)
   489  	require.Zero(t, dbWCount)
   490  	require.Zero(t, nodeRCount)
   491  	tree.ResetCount()
   492  }
   493  
   494  func initSetTree(tree *MutableTree) ([][]byte, [][]byte) {
   495  	keys := [][]byte{
   496  		[]byte("k1"),
   497  		[]byte("k2"),
   498  		[]byte("k3"),
   499  	}
   500  	values := [][]byte{
   501  		[]byte("v1"),
   502  		[]byte("v2"),
   503  		[]byte("v3"),
   504  	}
   505  	for i, key := range keys {
   506  		tree.Set(key, values[i])
   507  	}
   508  	//    k1
   509  	//  k1   k2
   510  	//      k2 k3
   511  	return keys, values
   512  }
   513  
   514  func newTestTree(t *testing.T, openLogFlag bool, cacheSize int, moduleName string) *MutableTree {
   515  	if openLogFlag {
   516  		openLog(moduleName)
   517  	}
   518  
   519  	memDB := db.NewPrefixDB(db.NewMemDB(), []byte(moduleName))
   520  	tree, err := NewMutableTree(memDB, cacheSize)
   521  	require.NoError(t, err)
   522  	return tree
   523  }
   524  
   525  func TestCommitSchedule(t *testing.T) {
   526  	EnableAsyncCommit = true
   527  	EnablePruningHistoryState = true
   528  	defer func() {
   529  		EnableAsyncCommit = false
   530  		EnablePruningHistoryState = false
   531  		treeMap.resetMap()
   532  	}()
   533  	tree := newTestTree(t, false, 10000, "test")
   534  	initSetTree(tree)
   535  
   536  	for i := 0; i < int(CommitIntervalHeight); i++ {
   537  		_, _, _, err := tree.SaveVersion(false)
   538  		require.NoError(t, err)
   539  	}
   540  
   541  	var wg sync.WaitGroup
   542  	wg.Add(1)
   543  	versions := tree.deepCopyVersions()
   544  	batch := tree.NewBatch()
   545  	tree.commitCh <- commitEvent{CommitIntervalHeight, versions, batch, nil, nil, 0, nil, nil, false}
   546  
   547  	tree.commitCh <- commitEvent{CommitIntervalHeight, versions, batch, nil, &wg, 0, nil, nil, false}
   548  	wg.Wait()
   549  
   550  }