github.com/ledgerwatch/erigon-lib@v1.0.0/kv/mdbx/kv_abstract_test.go (about) 1 /* 2 Copyright 2021 Erigon contributors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package mdbx_test 18 19 import ( 20 "context" 21 "fmt" 22 "net" 23 "runtime" 24 "testing" 25 26 "github.com/ledgerwatch/erigon-lib/gointerfaces" 27 "github.com/ledgerwatch/erigon-lib/gointerfaces/remote" 28 "github.com/ledgerwatch/erigon-lib/kv" 29 "github.com/ledgerwatch/erigon-lib/kv/mdbx" 30 "github.com/ledgerwatch/erigon-lib/kv/memdb" 31 "github.com/ledgerwatch/erigon-lib/kv/remotedb" 32 "github.com/ledgerwatch/erigon-lib/kv/remotedbserver" 33 "github.com/ledgerwatch/log/v3" 34 "github.com/stretchr/testify/assert" 35 "github.com/stretchr/testify/require" 36 "google.golang.org/grpc" 37 "google.golang.org/grpc/test/bufconn" 38 ) 39 40 func TestSequence(t *testing.T) { 41 if runtime.GOOS == "windows" { 42 t.Skip("fix me on win please") 43 } 44 45 writeDBs, _ := setupDatabases(t, log.New(), func(defaultBuckets kv.TableCfg) kv.TableCfg { 46 return defaultBuckets 47 }) 48 ctx := context.Background() 49 50 for _, db := range writeDBs { 51 db := db 52 tx, err := db.BeginRw(ctx) 53 require.NoError(t, err) 54 defer tx.Rollback() 55 56 i, err := tx.ReadSequence(kv.ChaindataTables[0]) 57 require.NoError(t, err) 58 require.Equal(t, uint64(0), i) 59 i, err = tx.IncrementSequence(kv.ChaindataTables[0], 1) 60 require.NoError(t, err) 61 require.Equal(t, uint64(0), i) 62 i, err = tx.IncrementSequence(kv.ChaindataTables[0], 6) 63 require.NoError(t, err) 64 require.Equal(t, uint64(1), i) 65 i, err = tx.IncrementSequence(kv.ChaindataTables[0], 1) 66 require.NoError(t, err) 67 require.Equal(t, uint64(7), i) 68 69 i, err = tx.ReadSequence(kv.ChaindataTables[1]) 70 require.NoError(t, err) 71 require.Equal(t, uint64(0), i) 72 i, err = tx.IncrementSequence(kv.ChaindataTables[1], 1) 73 require.NoError(t, err) 74 require.Equal(t, uint64(0), i) 75 i, err = tx.IncrementSequence(kv.ChaindataTables[1], 6) 76 require.NoError(t, err) 77 require.Equal(t, uint64(1), i) 78 i, err = tx.IncrementSequence(kv.ChaindataTables[1], 1) 79 require.NoError(t, err) 80 require.Equal(t, uint64(7), i) 81 tx.Rollback() 82 } 83 } 84 85 func TestManagedTx(t *testing.T) { 86 if runtime.GOOS == "windows" { 87 t.Skip("fix me on win please") 88 } 89 90 logger := log.New() 91 defaultConfig := kv.ChaindataTablesCfg 92 defer func() { 93 kv.ChaindataTablesCfg = defaultConfig 94 }() 95 96 bucketID := 0 97 bucket1 := kv.ChaindataTables[bucketID] 98 bucket2 := kv.ChaindataTables[bucketID+1] 99 writeDBs, readDBs := setupDatabases(t, logger, func(defaultBuckets kv.TableCfg) kv.TableCfg { 100 return map[string]kv.TableCfgItem{ 101 bucket1: { 102 Flags: kv.DupSort, 103 AutoDupSortKeysConversion: true, 104 DupToLen: 4, 105 DupFromLen: 6, 106 }, 107 bucket2: { 108 Flags: 0, 109 }, 110 } 111 }) 112 113 ctx := context.Background() 114 115 for _, db := range writeDBs { 116 db := db 117 tx, err := db.BeginRw(ctx) 118 require.NoError(t, err) 119 defer tx.Rollback() 120 121 c, err := tx.RwCursor(bucket1) 122 require.NoError(t, err) 123 c1, err := tx.RwCursor(bucket2) 124 require.NoError(t, err) 125 require.NoError(t, c.Append([]byte{0}, []byte{1})) 126 require.NoError(t, c1.Append([]byte{0}, []byte{1})) 127 require.NoError(t, c.Append([]byte{0, 0, 0, 0, 0, 1}, []byte{1})) // prefixes of len=FromLen for DupSort test (other keys must be <ToLen) 128 require.NoError(t, c1.Append([]byte{0, 0, 0, 0, 0, 1}, []byte{1})) 129 require.NoError(t, c.Append([]byte{0, 0, 0, 0, 0, 2}, []byte{1})) 130 require.NoError(t, c1.Append([]byte{0, 0, 0, 0, 0, 2}, []byte{1})) 131 require.NoError(t, c.Append([]byte{0, 0, 1}, []byte{1})) 132 require.NoError(t, c1.Append([]byte{0, 0, 1}, []byte{1})) 133 for i := uint8(1); i < 10; i++ { 134 require.NoError(t, c.Append([]byte{i}, []byte{1})) 135 require.NoError(t, c1.Append([]byte{i}, []byte{1})) 136 } 137 require.NoError(t, c.Put([]byte{0, 0, 0, 0, 0, 1}, []byte{2})) 138 require.NoError(t, c1.Put([]byte{0, 0, 0, 0, 0, 1}, []byte{2})) 139 err = tx.Commit() 140 require.NoError(t, err) 141 } 142 143 for _, db := range readDBs { 144 db := db 145 msg := fmt.Sprintf("%T", db) 146 switch db.(type) { 147 case *remotedb.DB: 148 default: 149 continue 150 } 151 152 t.Run("filter "+msg, func(t *testing.T) { 153 //testPrefixFilter(t, db, bucket1) 154 }) 155 t.Run("multiple cursors "+msg, func(t *testing.T) { 156 testMultiCursor(t, db, bucket1, bucket2) 157 }) 158 } 159 } 160 161 func TestRemoteKvVersion(t *testing.T) { 162 if runtime.GOOS == "windows" { 163 t.Skip("fix me on win please") 164 } 165 ctx := context.Background() 166 logger := log.New() 167 writeDB := mdbx.NewMDBX(logger).InMem("").MustOpen() 168 defer writeDB.Close() 169 conn := bufconn.Listen(1024 * 1024) 170 grpcServer := grpc.NewServer() 171 go func() { 172 remote.RegisterKVServer(grpcServer, remotedbserver.NewKvServer(ctx, writeDB, nil, nil, logger)) 173 if err := grpcServer.Serve(conn); err != nil { 174 log.Error("private RPC server fail", "err", err) 175 } 176 }() 177 v := gointerfaces.VersionFromProto(remotedbserver.KvServiceAPIVersion) 178 // Different Major versions 179 v1 := v 180 v1.Major++ 181 182 cc, err := grpc.Dial("", grpc.WithInsecure(), grpc.WithContextDialer(func(ctx context.Context, url string) (net.Conn, error) { return conn.Dial() })) 183 assert.NoError(t, err) 184 a, err := remotedb.NewRemote(v1, logger, remote.NewKVClient(cc)).Open() 185 if err != nil { 186 t.Fatalf("%v", err) 187 } 188 require.False(t, a.EnsureVersionCompatibility()) 189 // Different Minor versions 190 v2 := v 191 v2.Minor++ 192 a, err = remotedb.NewRemote(v2, logger, remote.NewKVClient(cc)).Open() 193 if err != nil { 194 t.Fatalf("%v", err) 195 } 196 require.False(t, a.EnsureVersionCompatibility()) 197 // Different Patch versions 198 v3 := v 199 v3.Patch++ 200 a, err = remotedb.NewRemote(v3, logger, remote.NewKVClient(cc)).Open() 201 require.NoError(t, err) 202 require.True(t, a.EnsureVersionCompatibility()) 203 } 204 205 func TestRemoteKvRange(t *testing.T) { 206 if runtime.GOOS == "windows" { 207 t.Skip("fix me on win please") 208 } 209 logger := log.New() 210 ctx, writeDB := context.Background(), memdb.NewTestDB(t) 211 grpcServer, conn := grpc.NewServer(), bufconn.Listen(1024*1024) 212 go func() { 213 kvServer := remotedbserver.NewKvServer(ctx, writeDB, nil, nil, logger) 214 remote.RegisterKVServer(grpcServer, kvServer) 215 if err := grpcServer.Serve(conn); err != nil { 216 log.Error("private RPC server fail", "err", err) 217 } 218 }() 219 220 cc, err := grpc.Dial("", grpc.WithInsecure(), grpc.WithContextDialer(func(ctx context.Context, url string) (net.Conn, error) { return conn.Dial() })) 221 require.NoError(t, err) 222 db, err := remotedb.NewRemote(gointerfaces.VersionFromProto(remotedbserver.KvServiceAPIVersion), logger, remote.NewKVClient(cc)).Open() 223 require.NoError(t, err) 224 require.True(t, db.EnsureVersionCompatibility()) 225 226 require := require.New(t) 227 require.NoError(writeDB.Update(ctx, func(tx kv.RwTx) error { 228 wc, err := tx.RwCursorDupSort(kv.PlainState) 229 require.NoError(err) 230 require.NoError(wc.Append([]byte{1}, []byte{1})) 231 require.NoError(wc.Append([]byte{1}, []byte{2})) 232 require.NoError(wc.Append([]byte{2}, []byte{1})) 233 require.NoError(wc.Append([]byte{3}, []byte{1})) 234 return nil 235 })) 236 237 require.NoError(db.View(ctx, func(tx kv.Tx) error { 238 c, err := tx.Cursor(kv.PlainState) 239 require.NoError(err) 240 241 k, v, err := c.First() 242 require.NoError(err) 243 require.Equal([]byte{1}, k) 244 require.Equal([]byte{1}, v) 245 246 // it must be possible to Stream and manipulate cursors in same time 247 cnt := 0 248 require.NoError(tx.ForEach(kv.PlainState, nil, func(_, _ []byte) error { 249 if cnt == 0 { 250 k, v, err = c.Next() 251 require.NoError(err) 252 require.Equal([]byte{1}, k) 253 require.Equal([]byte{2}, v) 254 } 255 cnt++ 256 return nil 257 })) 258 require.Equal(4, cnt) 259 260 // remote Tx must provide Snapshots-Isolation-Level: new updates are not visible for old readers 261 require.NoError(writeDB.Update(ctx, func(tx kv.RwTx) error { 262 require.NoError(tx.Put(kv.PlainState, []byte{4}, []byte{1})) 263 return nil 264 })) 265 266 k, v, err = c.Last() 267 require.NoError(err) 268 require.Equal([]byte{3}, k) 269 require.Equal([]byte{1}, v) 270 return nil 271 })) 272 273 err = db.View(ctx, func(tx kv.Tx) error { 274 cntRange := func(from, to []byte) (i int) { 275 it, err := tx.Range(kv.PlainState, from, to) 276 require.NoError(err) 277 for it.HasNext() { 278 _, _, err = it.Next() 279 require.NoError(err) 280 i++ 281 } 282 return i 283 } 284 285 require.Equal(2, cntRange([]byte{2}, []byte{4})) 286 require.Equal(4, cntRange(nil, []byte{4})) 287 require.Equal(3, cntRange([]byte{2}, nil)) 288 require.Equal(5, cntRange(nil, nil)) 289 return nil 290 }) 291 require.NoError(err) 292 293 // Limit 294 err = db.View(ctx, func(tx kv.Tx) error { 295 cntRange := func(from, to []byte) (i int) { 296 it, err := tx.RangeAscend(kv.PlainState, from, to, 2) 297 require.NoError(err) 298 for it.HasNext() { 299 _, _, err := it.Next() 300 require.NoError(err) 301 i++ 302 } 303 return i 304 } 305 306 require.Equal(2, cntRange([]byte{2}, []byte{4})) 307 require.Equal(2, cntRange(nil, []byte{4})) 308 require.Equal(2, cntRange([]byte{2}, nil)) 309 require.Equal(2, cntRange(nil, nil)) 310 return nil 311 }) 312 require.NoError(err) 313 314 err = db.View(ctx, func(tx kv.Tx) error { 315 cntRange := func(from, to []byte) (i int) { 316 it, err := tx.RangeDescend(kv.PlainState, from, to, 2) 317 require.NoError(err) 318 for it.HasNext() { 319 _, _, err := it.Next() 320 require.NoError(err) 321 i++ 322 } 323 return i 324 } 325 326 require.Equal(2, cntRange([]byte{4}, []byte{2})) 327 require.Equal(0, cntRange(nil, []byte{4})) 328 require.Equal(2, cntRange([]byte{2}, nil)) 329 require.Equal(2, cntRange(nil, nil)) 330 return nil 331 }) 332 require.NoError(err) 333 } 334 335 func setupDatabases(t *testing.T, logger log.Logger, f mdbx.TableCfgFunc) (writeDBs []kv.RwDB, readDBs []kv.RwDB) { 336 t.Helper() 337 ctx := context.Background() 338 writeDBs = []kv.RwDB{ 339 mdbx.NewMDBX(logger).InMem("").WithTableCfg(f).MustOpen(), 340 mdbx.NewMDBX(logger).InMem("").WithTableCfg(f).MustOpen(), // for remote db 341 } 342 343 conn := bufconn.Listen(1024 * 1024) 344 345 grpcServer := grpc.NewServer() 346 f2 := func() { 347 remote.RegisterKVServer(grpcServer, remotedbserver.NewKvServer(ctx, writeDBs[1], nil, nil, logger)) 348 if err := grpcServer.Serve(conn); err != nil { 349 logger.Error("private RPC server fail", "err", err) 350 } 351 } 352 go f2() 353 v := gointerfaces.VersionFromProto(remotedbserver.KvServiceAPIVersion) 354 cc, err := grpc.Dial("", grpc.WithInsecure(), grpc.WithContextDialer(func(ctx context.Context, url string) (net.Conn, error) { return conn.Dial() })) 355 assert.NoError(t, err) 356 rdb, err := remotedb.NewRemote(v, logger, remote.NewKVClient(cc)).Open() 357 assert.NoError(t, err) 358 readDBs = []kv.RwDB{ 359 writeDBs[0], 360 writeDBs[1], 361 rdb, 362 } 363 364 t.Cleanup(func() { 365 grpcServer.Stop() 366 367 if err := conn.Close(); err != nil { 368 panic(err) 369 } 370 371 for _, db := range readDBs { 372 db.Close() 373 } 374 375 for _, db := range writeDBs { 376 db.Close() 377 } 378 379 }) 380 return writeDBs, readDBs 381 } 382 383 func testMultiCursor(t *testing.T, db kv.RwDB, bucket1, bucket2 string) { 384 t.Helper() 385 assert, ctx := assert.New(t), context.Background() 386 387 if err := db.View(ctx, func(tx kv.Tx) error { 388 c1, err := tx.Cursor(bucket1) 389 if err != nil { 390 return err 391 } 392 c2, err := tx.Cursor(bucket2) 393 if err != nil { 394 return err 395 } 396 397 k1, v1, err := c1.First() 398 assert.NoError(err) 399 k2, v2, err := c2.First() 400 assert.NoError(err) 401 assert.Equal(k1, k2) 402 assert.Equal(v1, v2) 403 404 k1, v1, err = c1.Next() 405 assert.NoError(err) 406 k2, v2, err = c2.Next() 407 assert.NoError(err) 408 assert.Equal(k1, k2) 409 assert.Equal(v1, v2) 410 411 k1, v1, err = c1.Seek([]byte{0}) 412 assert.NoError(err) 413 k2, v2, err = c2.Seek([]byte{0}) 414 assert.NoError(err) 415 assert.Equal(k1, k2) 416 assert.Equal(v1, v2) 417 418 k1, v1, err = c1.Seek([]byte{0, 0}) 419 assert.NoError(err) 420 k2, v2, err = c2.Seek([]byte{0, 0}) 421 assert.NoError(err) 422 assert.Equal(k1, k2) 423 assert.Equal(v1, v2) 424 425 k1, v1, err = c1.Seek([]byte{0, 0, 0, 0}) 426 assert.NoError(err) 427 k2, v2, err = c2.Seek([]byte{0, 0, 0, 0}) 428 assert.NoError(err) 429 assert.Equal(k1, k2) 430 assert.Equal(v1, v2) 431 432 k1, v1, err = c1.Next() 433 assert.NoError(err) 434 k2, v2, err = c2.Next() 435 assert.NoError(err) 436 assert.Equal(k1, k2) 437 assert.Equal(v1, v2) 438 439 k1, v1, err = c1.Seek([]byte{0}) 440 assert.NoError(err) 441 k2, v2, err = c2.Seek([]byte{0}) 442 assert.NoError(err) 443 assert.Equal(k1, k2) 444 assert.Equal(v1, v2) 445 446 k1, v1, err = c1.Seek([]byte{0, 0}) 447 assert.NoError(err) 448 k2, v2, err = c2.Seek([]byte{0, 0}) 449 assert.NoError(err) 450 assert.Equal(k1, k2) 451 assert.Equal(v1, v2) 452 453 k1, v1, err = c1.Seek([]byte{0, 0, 0, 0}) 454 assert.NoError(err) 455 k2, v2, err = c2.Seek([]byte{0, 0, 0, 0}) 456 assert.NoError(err) 457 assert.Equal(k1, k2) 458 assert.Equal(v1, v2) 459 460 k1, v1, err = c1.Next() 461 assert.NoError(err) 462 k2, v2, err = c2.Next() 463 assert.NoError(err) 464 assert.Equal(k1, k2) 465 assert.Equal(v1, v2) 466 k1, v1, err = c1.Seek([]byte{2}) 467 assert.NoError(err) 468 k2, v2, err = c2.Seek([]byte{2}) 469 assert.NoError(err) 470 assert.Equal(k1, k2) 471 assert.Equal(v1, v2) 472 473 return nil 474 }); err != nil { 475 assert.NoError(err) 476 } 477 } 478 479 //func TestMultipleBuckets(t *testing.T) { 480 // writeDBs, readDBs, closeAll := setupDatabases(ethdb.WithChaindataTables) 481 // defer closeAll() 482 // 483 // ctx := context.Background() 484 // 485 // for _, db := range writeDBs { 486 // db := db 487 // msg := fmt.Sprintf("%T", db) 488 // t.Run("FillBuckets "+msg, func(t *testing.T) { 489 // if err := db.Update(ctx, func(tx ethdb.Tx) error { 490 // c := tx.Cursor(dbutils.ChaindataTables[0]) 491 // for i := uint8(0); i < 10; i++ { 492 // require.NoError(t, c.Put([]byte{i}, []byte{i})) 493 // } 494 // c2 := tx.Cursor(dbutils.ChaindataTables[1]) 495 // for i := uint8(0); i < 12; i++ { 496 // require.NoError(t, c2.Put([]byte{i}, []byte{i})) 497 // } 498 // 499 // // delete from first bucket key 5, then will seek on it and expect to see key 6 500 // if err := c.Delete([]byte{5}, nil); err != nil { 501 // return err 502 // } 503 // // delete non-existing key 504 // if err := c.Delete([]byte{6, 1}, nil); err != nil { 505 // return err 506 // } 507 // 508 // return nil 509 // }); err != nil { 510 // require.NoError(t, err) 511 // } 512 // }) 513 // } 514 // 515 // for _, db := range readDBs { 516 // db := db 517 // msg := fmt.Sprintf("%T", db) 518 // t.Run("MultipleBuckets "+msg, func(t *testing.T) { 519 // counter2, counter := 0, 0 520 // var key, value []byte 521 // err := db.View(ctx, func(tx ethdb.Tx) error { 522 // c := tx.Cursor(dbutils.ChaindataTables[0]) 523 // for k, _, err := c.First(); k != nil; k, _, err = c.Next() { 524 // if err != nil { 525 // return err 526 // } 527 // counter++ 528 // } 529 // 530 // c2 := tx.Cursor(dbutils.ChaindataTables[1]) 531 // for k, _, err := c2.First(); k != nil; k, _, err = c2.Next() { 532 // if err != nil { 533 // return err 534 // } 535 // counter2++ 536 // } 537 // 538 // c3 := tx.Cursor(dbutils.ChaindataTables[0]) 539 // k, v, err := c3.Seek([]byte{5}) 540 // if err != nil { 541 // return err 542 // } 543 // key = common.CopyBytes(k) 544 // value = common.CopyBytes(v) 545 // 546 // return nil 547 // }) 548 // require.NoError(t, err) 549 // assert.Equal(t, 9, counter) 550 // assert.Equal(t, 12, counter2) 551 // assert.Equal(t, []byte{6}, key) 552 // assert.Equal(t, []byte{6}, value) 553 // }) 554 // } 555 //} 556 557 //func TestReadAfterPut(t *testing.T) { 558 // writeDBs, _, closeAll := setupDatabases(ethdb.WithChaindataTables) 559 // defer closeAll() 560 // 561 // ctx := context.Background() 562 // 563 // for _, db := range writeDBs { 564 // db := db 565 // msg := fmt.Sprintf("%T", db) 566 // t.Run("GetAfterPut "+msg, func(t *testing.T) { 567 // if err := db.Update(ctx, func(tx ethdb.Tx) error { 568 // c := tx.Cursor(dbutils.ChaindataTables[0]) 569 // for i := uint8(0); i < 10; i++ { // don't read in same loop to check that writes don't affect each other (for example by sharing bucket.prefix buffer) 570 // require.NoError(t, c.Put([]byte{i}, []byte{i})) 571 // } 572 // 573 // for i := uint8(0); i < 10; i++ { 574 // v, err := c.SeekExact([]byte{i}) 575 // require.NoError(t, err) 576 // require.Equal(t, []byte{i}, v) 577 // } 578 // 579 // c2 := tx.Cursor(dbutils.ChaindataTables[1]) 580 // for i := uint8(0); i < 12; i++ { 581 // require.NoError(t, c2.Put([]byte{i}, []byte{i})) 582 // } 583 // 584 // for i := uint8(0); i < 12; i++ { 585 // v, err := c2.SeekExact([]byte{i}) 586 // require.NoError(t, err) 587 // require.Equal(t, []byte{i}, v) 588 // } 589 // 590 // { 591 // require.NoError(t, c2.Delete([]byte{5}, nil)) 592 // v, err := c2.SeekExact([]byte{5}) 593 // require.NoError(t, err) 594 // require.Nil(t, v) 595 // 596 // require.NoError(t, c2.Delete([]byte{255}, nil)) // delete non-existing key 597 // } 598 // 599 // return nil 600 // }); err != nil { 601 // require.NoError(t, err) 602 // } 603 // }) 604 // 605 // t.Run("cursor put and delete"+msg, func(t *testing.T) { 606 // if err := db.Update(ctx, func(tx ethdb.Tx) error { 607 // c3 := tx.Cursor(dbutils.ChaindataTables[2]) 608 // for i := uint8(0); i < 10; i++ { // don't read in same loop to check that writes don't affect each other (for example by sharing bucket.prefix buffer) 609 // require.NoError(t, c3.Put([]byte{i}, []byte{i})) 610 // } 611 // for i := uint8(0); i < 10; i++ { 612 // v, err := tx.GetOne(dbutils.ChaindataTables[2], []byte{i}) 613 // require.NoError(t, err) 614 // require.Equal(t, []byte{i}, v) 615 // } 616 // 617 // require.NoError(t, c3.Delete([]byte{255}, nil)) // delete non-existing key 618 // return nil 619 // }); err != nil { 620 // t.Error(err) 621 // } 622 // 623 // if err := db.Update(ctx, func(tx ethdb.Tx) error { 624 // c3 := tx.Cursor(dbutils.ChaindataTables[2]) 625 // require.NoError(t, c3.Delete([]byte{5}, nil)) 626 // v, err := tx.GetOne(dbutils.ChaindataTables[2], []byte{5}) 627 // require.NoError(t, err) 628 // require.Nil(t, v) 629 // return nil 630 // }); err != nil { 631 // t.Error(err) 632 // } 633 // }) 634 // } 635 //}