github.com/ethereum/go-ethereum@v1.16.1/ethdb/dbtest/testsuite.go (about)

     1  // Copyright 2019 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package dbtest
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/rand"
    22  	"slices"
    23  	"sort"
    24  	"strconv"
    25  	"testing"
    26  
    27  	"github.com/ethereum/go-ethereum/ethdb"
    28  )
    29  
    30  // TestDatabaseSuite runs a suite of tests against a KeyValueStore database
    31  // implementation.
    32  func TestDatabaseSuite(t *testing.T, New func() ethdb.KeyValueStore) {
    33  	t.Run("Iterator", func(t *testing.T) {
    34  		tests := []struct {
    35  			content map[string]string
    36  			prefix  string
    37  			start   string
    38  			order   []string
    39  		}{
    40  			// Empty databases should be iterable
    41  			{map[string]string{}, "", "", nil},
    42  			{map[string]string{}, "non-existent-prefix", "", nil},
    43  
    44  			// Single-item databases should be iterable
    45  			{map[string]string{"key": "val"}, "", "", []string{"key"}},
    46  			{map[string]string{"key": "val"}, "k", "", []string{"key"}},
    47  			{map[string]string{"key": "val"}, "l", "", nil},
    48  
    49  			// Multi-item databases should be fully iterable
    50  			{
    51  				map[string]string{"k1": "v1", "k5": "v5", "k2": "v2", "k4": "v4", "k3": "v3"},
    52  				"", "",
    53  				[]string{"k1", "k2", "k3", "k4", "k5"},
    54  			},
    55  			{
    56  				map[string]string{"k1": "v1", "k5": "v5", "k2": "v2", "k4": "v4", "k3": "v3"},
    57  				"k", "",
    58  				[]string{"k1", "k2", "k3", "k4", "k5"},
    59  			},
    60  			{
    61  				map[string]string{"k1": "v1", "k5": "v5", "k2": "v2", "k4": "v4", "k3": "v3"},
    62  				"l", "",
    63  				nil,
    64  			},
    65  			// Multi-item databases should be prefix-iterable
    66  			{
    67  				map[string]string{
    68  					"ka1": "va1", "ka5": "va5", "ka2": "va2", "ka4": "va4", "ka3": "va3",
    69  					"kb1": "vb1", "kb5": "vb5", "kb2": "vb2", "kb4": "vb4", "kb3": "vb3",
    70  				},
    71  				"ka", "",
    72  				[]string{"ka1", "ka2", "ka3", "ka4", "ka5"},
    73  			},
    74  			{
    75  				map[string]string{
    76  					"ka1": "va1", "ka5": "va5", "ka2": "va2", "ka4": "va4", "ka3": "va3",
    77  					"kb1": "vb1", "kb5": "vb5", "kb2": "vb2", "kb4": "vb4", "kb3": "vb3",
    78  				},
    79  				"kc", "",
    80  				nil,
    81  			},
    82  			// Multi-item databases should be prefix-iterable with start position
    83  			{
    84  				map[string]string{
    85  					"ka1": "va1", "ka5": "va5", "ka2": "va2", "ka4": "va4", "ka3": "va3",
    86  					"kb1": "vb1", "kb5": "vb5", "kb2": "vb2", "kb4": "vb4", "kb3": "vb3",
    87  				},
    88  				"ka", "3",
    89  				[]string{"ka3", "ka4", "ka5"},
    90  			},
    91  			{
    92  				map[string]string{
    93  					"ka1": "va1", "ka5": "va5", "ka2": "va2", "ka4": "va4", "ka3": "va3",
    94  					"kb1": "vb1", "kb5": "vb5", "kb2": "vb2", "kb4": "vb4", "kb3": "vb3",
    95  				},
    96  				"ka", "8",
    97  				nil,
    98  			},
    99  		}
   100  		for i, tt := range tests {
   101  			// Create the key-value data store
   102  			db := New()
   103  			for key, val := range tt.content {
   104  				if err := db.Put([]byte(key), []byte(val)); err != nil {
   105  					t.Fatalf("test %d: failed to insert item %s:%s into database: %v", i, key, val, err)
   106  				}
   107  			}
   108  			// Iterate over the database with the given configs and verify the results
   109  			it, idx := db.NewIterator([]byte(tt.prefix), []byte(tt.start)), 0
   110  			for it.Next() {
   111  				if len(tt.order) <= idx {
   112  					t.Errorf("test %d: prefix=%q more items than expected: checking idx=%d (key %q), expecting len=%d", i, tt.prefix, idx, it.Key(), len(tt.order))
   113  					break
   114  				}
   115  				if !bytes.Equal(it.Key(), []byte(tt.order[idx])) {
   116  					t.Errorf("test %d: item %d: key mismatch: have %s, want %s", i, idx, string(it.Key()), tt.order[idx])
   117  				}
   118  				if !bytes.Equal(it.Value(), []byte(tt.content[tt.order[idx]])) {
   119  					t.Errorf("test %d: item %d: value mismatch: have %s, want %s", i, idx, string(it.Value()), tt.content[tt.order[idx]])
   120  				}
   121  				idx++
   122  			}
   123  			if err := it.Error(); err != nil {
   124  				t.Errorf("test %d: iteration failed: %v", i, err)
   125  			}
   126  			if idx != len(tt.order) {
   127  				t.Errorf("test %d: iteration terminated prematurely: have %d, want %d", i, idx, len(tt.order))
   128  			}
   129  			db.Close()
   130  		}
   131  	})
   132  
   133  	t.Run("IteratorWith", func(t *testing.T) {
   134  		db := New()
   135  		defer db.Close()
   136  
   137  		keys := []string{"1", "2", "3", "4", "6", "10", "11", "12", "20", "21", "22"}
   138  		sort.Strings(keys) // 1, 10, 11, etc
   139  
   140  		for _, k := range keys {
   141  			if err := db.Put([]byte(k), nil); err != nil {
   142  				t.Fatal(err)
   143  			}
   144  		}
   145  
   146  		{
   147  			it := db.NewIterator(nil, nil)
   148  			got, want := iterateKeys(it), keys
   149  			if err := it.Error(); err != nil {
   150  				t.Fatal(err)
   151  			}
   152  			if !slices.Equal(got, want) {
   153  				t.Errorf("Iterator: got: %s; want: %s", got, want)
   154  			}
   155  		}
   156  
   157  		{
   158  			it := db.NewIterator([]byte("1"), nil)
   159  			got, want := iterateKeys(it), []string{"1", "10", "11", "12"}
   160  			if err := it.Error(); err != nil {
   161  				t.Fatal(err)
   162  			}
   163  			if !slices.Equal(got, want) {
   164  				t.Errorf("IteratorWith(1,nil): got: %s; want: %s", got, want)
   165  			}
   166  		}
   167  
   168  		{
   169  			it := db.NewIterator([]byte("5"), nil)
   170  			got, want := iterateKeys(it), []string{}
   171  			if err := it.Error(); err != nil {
   172  				t.Fatal(err)
   173  			}
   174  			if !slices.Equal(got, want) {
   175  				t.Errorf("IteratorWith(5,nil): got: %s; want: %s", got, want)
   176  			}
   177  		}
   178  
   179  		{
   180  			it := db.NewIterator(nil, []byte("2"))
   181  			got, want := iterateKeys(it), []string{"2", "20", "21", "22", "3", "4", "6"}
   182  			if err := it.Error(); err != nil {
   183  				t.Fatal(err)
   184  			}
   185  			if !slices.Equal(got, want) {
   186  				t.Errorf("IteratorWith(nil,2): got: %s; want: %s", got, want)
   187  			}
   188  		}
   189  
   190  		{
   191  			it := db.NewIterator(nil, []byte("5"))
   192  			got, want := iterateKeys(it), []string{"6"}
   193  			if err := it.Error(); err != nil {
   194  				t.Fatal(err)
   195  			}
   196  			if !slices.Equal(got, want) {
   197  				t.Errorf("IteratorWith(nil,5): got: %s; want: %s", got, want)
   198  			}
   199  		}
   200  	})
   201  
   202  	t.Run("KeyValueOperations", func(t *testing.T) {
   203  		db := New()
   204  		defer db.Close()
   205  
   206  		key := []byte("foo")
   207  
   208  		if got, err := db.Has(key); err != nil {
   209  			t.Error(err)
   210  		} else if got {
   211  			t.Errorf("wrong value: %t", got)
   212  		}
   213  
   214  		value := []byte("hello world")
   215  		if err := db.Put(key, value); err != nil {
   216  			t.Error(err)
   217  		}
   218  
   219  		if got, err := db.Has(key); err != nil {
   220  			t.Error(err)
   221  		} else if !got {
   222  			t.Errorf("wrong value: %t", got)
   223  		}
   224  
   225  		if got, err := db.Get(key); err != nil {
   226  			t.Error(err)
   227  		} else if !bytes.Equal(got, value) {
   228  			t.Errorf("wrong value: %q", got)
   229  		}
   230  
   231  		if err := db.Delete(key); err != nil {
   232  			t.Error(err)
   233  		}
   234  
   235  		if got, err := db.Has(key); err != nil {
   236  			t.Error(err)
   237  		} else if got {
   238  			t.Errorf("wrong value: %t", got)
   239  		}
   240  	})
   241  
   242  	t.Run("Batch", func(t *testing.T) {
   243  		db := New()
   244  		defer db.Close()
   245  
   246  		b := db.NewBatch()
   247  		for _, k := range []string{"1", "2", "3", "4"} {
   248  			if err := b.Put([]byte(k), nil); err != nil {
   249  				t.Fatal(err)
   250  			}
   251  		}
   252  
   253  		if has, err := db.Has([]byte("1")); err != nil {
   254  			t.Fatal(err)
   255  		} else if has {
   256  			t.Error("db contains element before batch write")
   257  		}
   258  
   259  		if err := b.Write(); err != nil {
   260  			t.Fatal(err)
   261  		}
   262  
   263  		{
   264  			it := db.NewIterator(nil, nil)
   265  			if got, want := iterateKeys(it), []string{"1", "2", "3", "4"}; !slices.Equal(got, want) {
   266  				t.Errorf("got: %s; want: %s", got, want)
   267  			}
   268  		}
   269  
   270  		b.Reset()
   271  
   272  		// Mix writes and deletes in batch
   273  		b.Put([]byte("5"), nil)
   274  		b.Delete([]byte("1"))
   275  		b.Put([]byte("6"), nil)
   276  
   277  		b.Delete([]byte("3")) // delete then put
   278  		b.Put([]byte("3"), nil)
   279  
   280  		b.Put([]byte("7"), nil) // put then delete
   281  		b.Delete([]byte("7"))
   282  
   283  		if err := b.Write(); err != nil {
   284  			t.Fatal(err)
   285  		}
   286  
   287  		{
   288  			it := db.NewIterator(nil, nil)
   289  			if got, want := iterateKeys(it), []string{"2", "3", "4", "5", "6"}; !slices.Equal(got, want) {
   290  				t.Errorf("got: %s; want: %s", got, want)
   291  			}
   292  		}
   293  	})
   294  
   295  	t.Run("BatchReplay", func(t *testing.T) {
   296  		db := New()
   297  		defer db.Close()
   298  
   299  		want := []string{"1", "2", "3", "4"}
   300  		b := db.NewBatch()
   301  		for _, k := range want {
   302  			if err := b.Put([]byte(k), nil); err != nil {
   303  				t.Fatal(err)
   304  			}
   305  		}
   306  
   307  		b2 := db.NewBatch()
   308  		if err := b.Replay(b2); err != nil {
   309  			t.Fatal(err)
   310  		}
   311  
   312  		if err := b2.Replay(db); err != nil {
   313  			t.Fatal(err)
   314  		}
   315  
   316  		it := db.NewIterator(nil, nil)
   317  		if got := iterateKeys(it); !slices.Equal(got, want) {
   318  			t.Errorf("got: %s; want: %s", got, want)
   319  		}
   320  	})
   321  
   322  	t.Run("OperationsAfterClose", func(t *testing.T) {
   323  		db := New()
   324  		db.Put([]byte("key"), []byte("value"))
   325  		db.Close()
   326  		if _, err := db.Get([]byte("key")); err == nil {
   327  			t.Fatalf("expected error on Get after Close")
   328  		}
   329  		if _, err := db.Has([]byte("key")); err == nil {
   330  			t.Fatalf("expected error on Get after Close")
   331  		}
   332  		if err := db.Put([]byte("key2"), []byte("value2")); err == nil {
   333  			t.Fatalf("expected error on Put after Close")
   334  		}
   335  		if err := db.Delete([]byte("key")); err == nil {
   336  			t.Fatalf("expected error on Delete after Close")
   337  		}
   338  
   339  		b := db.NewBatch()
   340  		if err := b.Put([]byte("batchkey"), []byte("batchval")); err != nil {
   341  			t.Fatalf("expected no error on batch.Put after Close, got %v", err)
   342  		}
   343  		if err := b.Write(); err == nil {
   344  			t.Fatalf("expected error on batch.Write after Close")
   345  		}
   346  	})
   347  
   348  	t.Run("DeleteRange", func(t *testing.T) {
   349  		db := New()
   350  		defer db.Close()
   351  
   352  		addRange := func(start, stop int) {
   353  			for i := start; i <= stop; i++ {
   354  				db.Put([]byte(strconv.Itoa(i)), nil)
   355  			}
   356  		}
   357  
   358  		checkRange := func(start, stop int, exp bool) {
   359  			for i := start; i <= stop; i++ {
   360  				has, _ := db.Has([]byte(strconv.Itoa(i)))
   361  				if has && !exp {
   362  					t.Fatalf("unexpected key %d", i)
   363  				}
   364  				if !has && exp {
   365  					t.Fatalf("missing expected key %d", i)
   366  				}
   367  			}
   368  		}
   369  
   370  		addRange(1, 9)
   371  		db.DeleteRange([]byte("9"), []byte("1"))
   372  		checkRange(1, 9, true)
   373  		db.DeleteRange([]byte("5"), []byte("5"))
   374  		checkRange(1, 9, true)
   375  		db.DeleteRange([]byte("5"), []byte("50"))
   376  		checkRange(1, 4, true)
   377  		checkRange(5, 5, false)
   378  		checkRange(6, 9, true)
   379  		db.DeleteRange([]byte(""), []byte("a"))
   380  		checkRange(1, 9, false)
   381  
   382  		addRange(1, 999)
   383  		db.DeleteRange([]byte("12345"), []byte("54321"))
   384  		checkRange(1, 1, true)
   385  		checkRange(2, 5, false)
   386  		checkRange(6, 12, true)
   387  		checkRange(13, 54, false)
   388  		checkRange(55, 123, true)
   389  		checkRange(124, 543, false)
   390  		checkRange(544, 999, true)
   391  
   392  		addRange(1, 999)
   393  		db.DeleteRange([]byte("3"), []byte("7"))
   394  		checkRange(1, 2, true)
   395  		checkRange(3, 6, false)
   396  		checkRange(7, 29, true)
   397  		checkRange(30, 69, false)
   398  		checkRange(70, 299, true)
   399  		checkRange(300, 699, false)
   400  		checkRange(700, 999, true)
   401  
   402  		db.DeleteRange([]byte(""), []byte("a"))
   403  		checkRange(1, 999, false)
   404  
   405  		addRange(1, 999)
   406  		db.DeleteRange(nil, nil)
   407  		checkRange(1, 999, false)
   408  	})
   409  
   410  	t.Run("BatchDeleteRange", func(t *testing.T) {
   411  		db := New()
   412  		defer db.Close()
   413  
   414  		// Helper to add keys
   415  		addKeys := func(start, stop int) {
   416  			for i := start; i <= stop; i++ {
   417  				if err := db.Put([]byte(strconv.Itoa(i)), []byte("val-"+strconv.Itoa(i))); err != nil {
   418  					t.Fatal(err)
   419  				}
   420  			}
   421  		}
   422  
   423  		// Helper to check if keys exist
   424  		checkKeys := func(start, stop int, shouldExist bool) {
   425  			for i := start; i <= stop; i++ {
   426  				key := []byte(strconv.Itoa(i))
   427  				has, err := db.Has(key)
   428  				if err != nil {
   429  					t.Fatal(err)
   430  				}
   431  				if has != shouldExist {
   432  					if shouldExist {
   433  						t.Fatalf("key %s should exist but doesn't", key)
   434  					} else {
   435  						t.Fatalf("key %s shouldn't exist but does", key)
   436  					}
   437  				}
   438  			}
   439  		}
   440  
   441  		// Test 1: Basic range deletion in batch
   442  		addKeys(1, 10)
   443  		checkKeys(1, 10, true)
   444  
   445  		batch := db.NewBatch()
   446  		if err := batch.DeleteRange([]byte("3"), []byte("8")); err != nil {
   447  			t.Fatal(err)
   448  		}
   449  		// Keys shouldn't be deleted until Write is called
   450  		checkKeys(1, 10, true)
   451  
   452  		if err := batch.Write(); err != nil {
   453  			t.Fatal(err)
   454  		}
   455  		// After Write, keys in range should be deleted
   456  		// Range is [start, end) - inclusive of start, exclusive of end
   457  		checkKeys(1, 2, true)  // These should still exist
   458  		checkKeys(3, 7, false) // These should be deleted (3 to 7 inclusive)
   459  		checkKeys(8, 10, true) // These should still exist (8 is the end boundary, exclusive)
   460  
   461  		// Test 2: Delete range with special markers
   462  		addKeys(3, 7)
   463  		batch = db.NewBatch()
   464  		if err := batch.DeleteRange(nil, nil); err != nil {
   465  			t.Fatal(err)
   466  		}
   467  		if err := batch.Write(); err != nil {
   468  			t.Fatal(err)
   469  		}
   470  		checkKeys(1, 10, false)
   471  
   472  		// Test 3: Mix Put, Delete, and DeleteRange in a batch
   473  		// Reset database for next test by adding back deleted keys
   474  		addKeys(1, 10)
   475  		checkKeys(1, 10, true)
   476  
   477  		// Create a new batch with multiple operations
   478  		batch = db.NewBatch()
   479  		if err := batch.Put([]byte("5"), []byte("new-val-5")); err != nil {
   480  			t.Fatal(err)
   481  		}
   482  		if err := batch.Delete([]byte("9")); err != nil {
   483  			t.Fatal(err)
   484  		}
   485  		if err := batch.DeleteRange([]byte("1"), []byte("3")); err != nil {
   486  			t.Fatal(err)
   487  		}
   488  		if err := batch.Write(); err != nil {
   489  			t.Fatal(err)
   490  		}
   491  		// Check results after batch operations
   492  		// Keys 1-2 should be deleted by DeleteRange
   493  		checkKeys(1, 2, false)
   494  
   495  		// Key 3 should exist (exclusive of end)
   496  		has, err := db.Has([]byte("3"))
   497  		if err != nil {
   498  			t.Fatal(err)
   499  		}
   500  		if !has {
   501  			t.Fatalf("key 3 should exist after DeleteRange(1,3)")
   502  		}
   503  
   504  		// Key 5 should have a new value
   505  		val, err := db.Get([]byte("5"))
   506  		if err != nil {
   507  			t.Fatal(err)
   508  		}
   509  		if !bytes.Equal(val, []byte("new-val-5")) {
   510  			t.Fatalf("key 5 has wrong value: got %s, want %s", val, "new-val-5")
   511  		}
   512  
   513  		// Key 9 should be deleted
   514  		has, err = db.Has([]byte("9"))
   515  		if err != nil {
   516  			t.Fatal(err)
   517  		}
   518  		if has {
   519  			t.Fatalf("key 9 should be deleted")
   520  		}
   521  
   522  		// Test 4: Reset batch
   523  		batch.Reset()
   524  		// Individual deletes work better with both string and numeric comparisons
   525  		if err := batch.Delete([]byte("8")); err != nil {
   526  			t.Fatal(err)
   527  		}
   528  		if err := batch.Delete([]byte("10")); err != nil {
   529  			t.Fatal(err)
   530  		}
   531  		if err := batch.Delete([]byte("11")); err != nil {
   532  			t.Fatal(err)
   533  		}
   534  		if err := batch.Write(); err != nil {
   535  			t.Fatal(err)
   536  		}
   537  
   538  		// Key 8 should be deleted
   539  		has, err = db.Has([]byte("8"))
   540  		if err != nil {
   541  			t.Fatal(err)
   542  		}
   543  		if has {
   544  			t.Fatalf("key 8 should be deleted")
   545  		}
   546  
   547  		// Keys 3-7 should still exist
   548  		checkKeys(3, 7, true)
   549  
   550  		// Key 10 should be deleted
   551  		has, err = db.Has([]byte("10"))
   552  		if err != nil {
   553  			t.Fatal(err)
   554  		}
   555  		if has {
   556  			t.Fatalf("key 10 should be deleted")
   557  		}
   558  
   559  		// Test 5: Empty range
   560  		batch = db.NewBatch()
   561  		if err := batch.DeleteRange([]byte("100"), []byte("100")); err != nil {
   562  			t.Fatal(err)
   563  		}
   564  		if err := batch.Write(); err != nil {
   565  			t.Fatal(err)
   566  		}
   567  		// No existing keys should be affected
   568  		checkKeys(3, 7, true)
   569  
   570  		// Test 6: Test entire keyspace deletion
   571  		// First clear any existing keys
   572  		for i := 1; i <= 100; i++ {
   573  			db.Delete([]byte(strconv.Itoa(i)))
   574  		}
   575  
   576  		// Then add some fresh test keys
   577  		addKeys(50, 60)
   578  
   579  		// Verify keys exist before deletion
   580  		checkKeys(50, 60, true)
   581  
   582  		batch = db.NewBatch()
   583  		if err := batch.DeleteRange([]byte(""), []byte("z")); err != nil {
   584  			t.Fatal(err)
   585  		}
   586  		if err := batch.Write(); err != nil {
   587  			t.Fatal(err)
   588  		}
   589  		// All keys should be deleted
   590  		checkKeys(50, 60, false)
   591  
   592  		// Test 7: overlapping range deletion
   593  		addKeys(50, 60)
   594  		batch = db.NewBatch()
   595  		if err := batch.DeleteRange([]byte("50"), []byte("55")); err != nil {
   596  			t.Fatal(err)
   597  		}
   598  		if err := batch.DeleteRange([]byte("52"), []byte("58")); err != nil {
   599  			t.Fatal(err)
   600  		}
   601  		if err := batch.Write(); err != nil {
   602  			t.Fatal(err)
   603  		}
   604  		checkKeys(50, 57, false)
   605  		checkKeys(58, 60, true)
   606  	})
   607  
   608  	t.Run("BatchReplayWithDeleteRange", func(t *testing.T) {
   609  		db := New()
   610  		defer db.Close()
   611  
   612  		// Setup some initial data
   613  		for i := 1; i <= 10; i++ {
   614  			if err := db.Put([]byte(strconv.Itoa(i)), []byte("val-"+strconv.Itoa(i))); err != nil {
   615  				t.Fatal(err)
   616  			}
   617  		}
   618  
   619  		// Create batch with multiple operations including DeleteRange
   620  		batch1 := db.NewBatch()
   621  		batch1.Put([]byte("new-key-1"), []byte("new-val-1"))
   622  		batch1.DeleteRange([]byte("3"), []byte("7")) // Should delete keys 3-6 but not 7
   623  		batch1.Delete([]byte("8"))
   624  		batch1.Put([]byte("new-key-2"), []byte("new-val-2"))
   625  
   626  		// Create a second batch to replay into
   627  		batch2 := db.NewBatch()
   628  		if err := batch1.Replay(batch2); err != nil {
   629  			t.Fatal(err)
   630  		}
   631  
   632  		// Write the second batch
   633  		if err := batch2.Write(); err != nil {
   634  			t.Fatal(err)
   635  		}
   636  
   637  		// Verify results
   638  		// Original keys 3-6 should be deleted (inclusive of start, exclusive of end)
   639  		for i := 3; i <= 6; i++ {
   640  			has, err := db.Has([]byte(strconv.Itoa(i)))
   641  			if err != nil {
   642  				t.Fatal(err)
   643  			}
   644  			if has {
   645  				t.Fatalf("key %d should be deleted", i)
   646  			}
   647  		}
   648  
   649  		// Key 7 should NOT be deleted (exclusive of end)
   650  		has, err := db.Has([]byte("7"))
   651  		if err != nil {
   652  			t.Fatal(err)
   653  		}
   654  		if !has {
   655  			t.Fatalf("key 7 should NOT be deleted (exclusive of end)")
   656  		}
   657  
   658  		// Key 8 should be deleted
   659  		has, err = db.Has([]byte("8"))
   660  		if err != nil {
   661  			t.Fatal(err)
   662  		}
   663  		if has {
   664  			t.Fatalf("key 8 should be deleted")
   665  		}
   666  
   667  		// New keys should be added
   668  		for _, key := range []string{"new-key-1", "new-key-2"} {
   669  			has, err := db.Has([]byte(key))
   670  			if err != nil {
   671  				t.Fatal(err)
   672  			}
   673  			if !has {
   674  				t.Fatalf("key %s should exist", key)
   675  			}
   676  		}
   677  
   678  		// Create a third batch for direct replay to database
   679  		batch3 := db.NewBatch()
   680  		batch3.DeleteRange([]byte("1"), []byte("3")) // Should delete keys 1-2 but not 3
   681  
   682  		// Replay directly to the database
   683  		if err := batch3.Replay(db); err != nil {
   684  			t.Fatal(err)
   685  		}
   686  
   687  		// Verify keys 1-2 are now deleted
   688  		for i := 1; i <= 2; i++ {
   689  			has, err := db.Has([]byte(strconv.Itoa(i)))
   690  			if err != nil {
   691  				t.Fatal(err)
   692  			}
   693  			if has {
   694  				t.Fatalf("key %d should be deleted after direct replay", i)
   695  			}
   696  		}
   697  
   698  		// Verify key 3 is NOT deleted (since it's exclusive of end)
   699  		has, err = db.Has([]byte("3"))
   700  		if err != nil {
   701  			t.Fatal(err)
   702  		}
   703  		if has {
   704  			t.Fatalf("key 3 should still be deleted from previous operation")
   705  		}
   706  	})
   707  }
   708  
   709  // BenchDatabaseSuite runs a suite of benchmarks against a KeyValueStore database
   710  // implementation.
   711  func BenchDatabaseSuite(b *testing.B, New func() ethdb.KeyValueStore) {
   712  	var (
   713  		keys, vals   = makeDataset(1_000_000, 32, 32, false)
   714  		sKeys, sVals = makeDataset(1_000_000, 32, 32, true)
   715  	)
   716  	// Run benchmarks sequentially
   717  	b.Run("Write", func(b *testing.B) {
   718  		benchWrite := func(b *testing.B, keys, vals [][]byte) {
   719  			b.ResetTimer()
   720  			b.ReportAllocs()
   721  
   722  			db := New()
   723  			defer db.Close()
   724  
   725  			for i := 0; i < len(keys); i++ {
   726  				db.Put(keys[i], vals[i])
   727  			}
   728  		}
   729  		b.Run("WriteSorted", func(b *testing.B) {
   730  			benchWrite(b, sKeys, sVals)
   731  		})
   732  		b.Run("WriteRandom", func(b *testing.B) {
   733  			benchWrite(b, keys, vals)
   734  		})
   735  	})
   736  	b.Run("Read", func(b *testing.B) {
   737  		benchRead := func(b *testing.B, keys, vals [][]byte) {
   738  			db := New()
   739  			defer db.Close()
   740  
   741  			for i := 0; i < len(keys); i++ {
   742  				db.Put(keys[i], vals[i])
   743  			}
   744  			b.ResetTimer()
   745  			b.ReportAllocs()
   746  
   747  			for i := 0; i < len(keys); i++ {
   748  				db.Get(keys[i])
   749  			}
   750  		}
   751  		b.Run("ReadSorted", func(b *testing.B) {
   752  			benchRead(b, sKeys, sVals)
   753  		})
   754  		b.Run("ReadRandom", func(b *testing.B) {
   755  			benchRead(b, keys, vals)
   756  		})
   757  	})
   758  	b.Run("Iteration", func(b *testing.B) {
   759  		benchIteration := func(b *testing.B, keys, vals [][]byte) {
   760  			db := New()
   761  			defer db.Close()
   762  
   763  			for i := 0; i < len(keys); i++ {
   764  				db.Put(keys[i], vals[i])
   765  			}
   766  			b.ResetTimer()
   767  			b.ReportAllocs()
   768  
   769  			it := db.NewIterator(nil, nil)
   770  			for it.Next() {
   771  			}
   772  			it.Release()
   773  		}
   774  		b.Run("IterationSorted", func(b *testing.B) {
   775  			benchIteration(b, sKeys, sVals)
   776  		})
   777  		b.Run("IterationRandom", func(b *testing.B) {
   778  			benchIteration(b, keys, vals)
   779  		})
   780  	})
   781  	b.Run("BatchWrite", func(b *testing.B) {
   782  		benchBatchWrite := func(b *testing.B, keys, vals [][]byte) {
   783  			b.ResetTimer()
   784  			b.ReportAllocs()
   785  
   786  			db := New()
   787  			defer db.Close()
   788  
   789  			batch := db.NewBatch()
   790  			for i := 0; i < len(keys); i++ {
   791  				batch.Put(keys[i], vals[i])
   792  			}
   793  			batch.Write()
   794  		}
   795  		b.Run("BenchWriteSorted", func(b *testing.B) {
   796  			benchBatchWrite(b, sKeys, sVals)
   797  		})
   798  		b.Run("BenchWriteRandom", func(b *testing.B) {
   799  			benchBatchWrite(b, keys, vals)
   800  		})
   801  	})
   802  	b.Run("DeleteRange", func(b *testing.B) {
   803  		benchDeleteRange := func(b *testing.B, count int) {
   804  			db := New()
   805  			defer db.Close()
   806  
   807  			for i := 0; i < count; i++ {
   808  				db.Put([]byte(strconv.Itoa(i)), nil)
   809  			}
   810  			b.ResetTimer()
   811  			b.ReportAllocs()
   812  
   813  			db.DeleteRange([]byte("0"), []byte("999999999"))
   814  		}
   815  		b.Run("DeleteRange100", func(b *testing.B) {
   816  			benchDeleteRange(b, 100)
   817  		})
   818  		b.Run("DeleteRange1k", func(b *testing.B) {
   819  			benchDeleteRange(b, 1000)
   820  		})
   821  		b.Run("DeleteRange10k", func(b *testing.B) {
   822  			benchDeleteRange(b, 10000)
   823  		})
   824  	})
   825  	b.Run("BatchDeleteRange", func(b *testing.B) {
   826  		benchBatchDeleteRange := func(b *testing.B, count int) {
   827  			db := New()
   828  			defer db.Close()
   829  
   830  			// Prepare data
   831  			for i := 0; i < count; i++ {
   832  				db.Put([]byte(strconv.Itoa(i)), nil)
   833  			}
   834  
   835  			b.ResetTimer()
   836  			b.ReportAllocs()
   837  
   838  			// Create batch and delete range
   839  			batch := db.NewBatch()
   840  			batch.DeleteRange([]byte("0"), []byte("999999999"))
   841  			batch.Write()
   842  		}
   843  
   844  		b.Run("BatchDeleteRange100", func(b *testing.B) {
   845  			benchBatchDeleteRange(b, 100)
   846  		})
   847  		b.Run("BatchDeleteRange1k", func(b *testing.B) {
   848  			benchBatchDeleteRange(b, 1000)
   849  		})
   850  		b.Run("BatchDeleteRange10k", func(b *testing.B) {
   851  			benchBatchDeleteRange(b, 10000)
   852  		})
   853  	})
   854  
   855  	b.Run("BatchMixedOps", func(b *testing.B) {
   856  		benchBatchMixedOps := func(b *testing.B, count int) {
   857  			db := New()
   858  			defer db.Close()
   859  
   860  			// Prepare initial data
   861  			for i := 0; i < count; i++ {
   862  				db.Put([]byte(strconv.Itoa(i)), []byte("val"))
   863  			}
   864  
   865  			b.ResetTimer()
   866  			b.ReportAllocs()
   867  
   868  			// Create batch with mixed operations
   869  			batch := db.NewBatch()
   870  
   871  			// Add some new keys
   872  			for i := 0; i < count/10; i++ {
   873  				batch.Put([]byte(strconv.Itoa(count+i)), []byte("new-val"))
   874  			}
   875  
   876  			// Delete some individual keys
   877  			for i := 0; i < count/20; i++ {
   878  				batch.Delete([]byte(strconv.Itoa(i * 2)))
   879  			}
   880  
   881  			// Delete range of keys
   882  			rangeStart := count / 2
   883  			rangeEnd := count * 3 / 4
   884  			batch.DeleteRange([]byte(strconv.Itoa(rangeStart)), []byte(strconv.Itoa(rangeEnd)))
   885  
   886  			// Write the batch
   887  			batch.Write()
   888  		}
   889  
   890  		b.Run("BatchMixedOps100", func(b *testing.B) {
   891  			benchBatchMixedOps(b, 100)
   892  		})
   893  		b.Run("BatchMixedOps1k", func(b *testing.B) {
   894  			benchBatchMixedOps(b, 1000)
   895  		})
   896  		b.Run("BatchMixedOps10k", func(b *testing.B) {
   897  			benchBatchMixedOps(b, 10000)
   898  		})
   899  	})
   900  }
   901  
   902  func iterateKeys(it ethdb.Iterator) []string {
   903  	keys := []string{}
   904  	for it.Next() {
   905  		keys = append(keys, string(it.Key()))
   906  	}
   907  	sort.Strings(keys)
   908  	it.Release()
   909  	return keys
   910  }
   911  
   912  // randBytes generates a random blob of data.
   913  func randBytes(len int) []byte {
   914  	buf := make([]byte, len)
   915  	if n, err := rand.Read(buf); n != len || err != nil {
   916  		panic(err)
   917  	}
   918  	return buf
   919  }
   920  
   921  func makeDataset(size, ksize, vsize int, order bool) ([][]byte, [][]byte) {
   922  	var keys [][]byte
   923  	var vals [][]byte
   924  	for i := 0; i < size; i += 1 {
   925  		keys = append(keys, randBytes(ksize))
   926  		vals = append(vals, randBytes(vsize))
   927  	}
   928  	if order {
   929  		slices.SortFunc(keys, bytes.Compare)
   930  	}
   931  	return keys, vals
   932  }