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 }