github.com/shrimpyuk/bor@v0.2.15-0.20220224151350-fb4ec6020bae/core/rawdb/freezer_test.go (about)

     1  // Copyright 2021 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 rawdb
    18  
    19  import (
    20  	"bytes"
    21  	"errors"
    22  	"fmt"
    23  	"io/ioutil"
    24  	"math/big"
    25  	"math/rand"
    26  	"os"
    27  	"sync"
    28  	"testing"
    29  
    30  	"github.com/ethereum/go-ethereum/ethdb"
    31  	"github.com/ethereum/go-ethereum/rlp"
    32  	"github.com/stretchr/testify/require"
    33  )
    34  
    35  var freezerTestTableDef = map[string]bool{"test": true}
    36  
    37  func TestFreezerModify(t *testing.T) {
    38  	t.Parallel()
    39  
    40  	// Create test data.
    41  	var valuesRaw [][]byte
    42  	var valuesRLP []*big.Int
    43  	for x := 0; x < 100; x++ {
    44  		v := getChunk(256, x)
    45  		valuesRaw = append(valuesRaw, v)
    46  		iv := big.NewInt(int64(x))
    47  		iv = iv.Exp(iv, iv, nil)
    48  		valuesRLP = append(valuesRLP, iv)
    49  	}
    50  
    51  	tables := map[string]bool{"raw": true, "rlp": false}
    52  	f, dir := newFreezerForTesting(t, tables)
    53  	defer os.RemoveAll(dir)
    54  	defer f.Close()
    55  
    56  	// Commit test data.
    57  	_, err := f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
    58  		for i := range valuesRaw {
    59  			if err := op.AppendRaw("raw", uint64(i), valuesRaw[i]); err != nil {
    60  				return err
    61  			}
    62  			if err := op.Append("rlp", uint64(i), valuesRLP[i]); err != nil {
    63  				return err
    64  			}
    65  		}
    66  		return nil
    67  	})
    68  	if err != nil {
    69  		t.Fatal("ModifyAncients failed:", err)
    70  	}
    71  
    72  	// Dump indexes.
    73  	for _, table := range f.tables {
    74  		t.Log(table.name, "index:", table.dumpIndexString(0, int64(len(valuesRaw))))
    75  	}
    76  
    77  	// Read back test data.
    78  	checkAncientCount(t, f, "raw", uint64(len(valuesRaw)))
    79  	checkAncientCount(t, f, "rlp", uint64(len(valuesRLP)))
    80  	for i := range valuesRaw {
    81  		v, _ := f.Ancient("raw", uint64(i))
    82  		if !bytes.Equal(v, valuesRaw[i]) {
    83  			t.Fatalf("wrong raw value at %d: %x", i, v)
    84  		}
    85  		ivEnc, _ := f.Ancient("rlp", uint64(i))
    86  		want, _ := rlp.EncodeToBytes(valuesRLP[i])
    87  		if !bytes.Equal(ivEnc, want) {
    88  			t.Fatalf("wrong RLP value at %d: %x", i, ivEnc)
    89  		}
    90  	}
    91  }
    92  
    93  // This checks that ModifyAncients rolls back freezer updates
    94  // when the function passed to it returns an error.
    95  func TestFreezerModifyRollback(t *testing.T) {
    96  	t.Parallel()
    97  
    98  	f, dir := newFreezerForTesting(t, freezerTestTableDef)
    99  	defer os.RemoveAll(dir)
   100  
   101  	theError := errors.New("oops")
   102  	_, err := f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
   103  		// Append three items. This creates two files immediately,
   104  		// because the table size limit of the test freezer is 2048.
   105  		require.NoError(t, op.AppendRaw("test", 0, make([]byte, 2048)))
   106  		require.NoError(t, op.AppendRaw("test", 1, make([]byte, 2048)))
   107  		require.NoError(t, op.AppendRaw("test", 2, make([]byte, 2048)))
   108  		return theError
   109  	})
   110  	if err != theError {
   111  		t.Errorf("ModifyAncients returned wrong error %q", err)
   112  	}
   113  	checkAncientCount(t, f, "test", 0)
   114  	f.Close()
   115  
   116  	// Reopen and check that the rolled-back data doesn't reappear.
   117  	tables := map[string]bool{"test": true}
   118  	f2, err := newFreezer(dir, "", false, 2049, tables)
   119  	if err != nil {
   120  		t.Fatalf("can't reopen freezer after failed ModifyAncients: %v", err)
   121  	}
   122  	defer f2.Close()
   123  	checkAncientCount(t, f2, "test", 0)
   124  }
   125  
   126  // This test runs ModifyAncients and Ancient concurrently with each other.
   127  func TestFreezerConcurrentModifyRetrieve(t *testing.T) {
   128  	t.Parallel()
   129  
   130  	f, dir := newFreezerForTesting(t, freezerTestTableDef)
   131  	defer os.RemoveAll(dir)
   132  	defer f.Close()
   133  
   134  	var (
   135  		numReaders     = 5
   136  		writeBatchSize = uint64(50)
   137  		written        = make(chan uint64, numReaders*6)
   138  		wg             sync.WaitGroup
   139  	)
   140  	wg.Add(numReaders + 1)
   141  
   142  	// Launch the writer. It appends 10000 items in batches.
   143  	go func() {
   144  		defer wg.Done()
   145  		defer close(written)
   146  		for item := uint64(0); item < 10000; item += writeBatchSize {
   147  			_, err := f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
   148  				for i := uint64(0); i < writeBatchSize; i++ {
   149  					item := item + i
   150  					value := getChunk(32, int(item))
   151  					if err := op.AppendRaw("test", item, value); err != nil {
   152  						return err
   153  					}
   154  				}
   155  				return nil
   156  			})
   157  			if err != nil {
   158  				panic(err)
   159  			}
   160  			for i := 0; i < numReaders; i++ {
   161  				written <- item + writeBatchSize
   162  			}
   163  		}
   164  	}()
   165  
   166  	// Launch the readers. They read random items from the freezer up to the
   167  	// current frozen item count.
   168  	for i := 0; i < numReaders; i++ {
   169  		go func() {
   170  			defer wg.Done()
   171  			for frozen := range written {
   172  				for rc := 0; rc < 80; rc++ {
   173  					num := uint64(rand.Intn(int(frozen)))
   174  					value, err := f.Ancient("test", num)
   175  					if err != nil {
   176  						panic(fmt.Errorf("error reading %d (frozen %d): %v", num, frozen, err))
   177  					}
   178  					if !bytes.Equal(value, getChunk(32, int(num))) {
   179  						panic(fmt.Errorf("wrong value at %d", num))
   180  					}
   181  				}
   182  			}
   183  		}()
   184  	}
   185  
   186  	wg.Wait()
   187  }
   188  
   189  // This test runs ModifyAncients and TruncateAncients concurrently with each other.
   190  func TestFreezerConcurrentModifyTruncate(t *testing.T) {
   191  	f, dir := newFreezerForTesting(t, freezerTestTableDef)
   192  	defer os.RemoveAll(dir)
   193  	defer f.Close()
   194  
   195  	var item = make([]byte, 256)
   196  
   197  	for i := 0; i < 1000; i++ {
   198  		// First reset and write 100 items.
   199  		if err := f.TruncateAncients(0); err != nil {
   200  			t.Fatal("truncate failed:", err)
   201  		}
   202  		_, err := f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
   203  			for i := uint64(0); i < 100; i++ {
   204  				if err := op.AppendRaw("test", i, item); err != nil {
   205  					return err
   206  				}
   207  			}
   208  			return nil
   209  		})
   210  		if err != nil {
   211  			t.Fatal("modify failed:", err)
   212  		}
   213  		checkAncientCount(t, f, "test", 100)
   214  
   215  		// Now append 100 more items and truncate concurrently.
   216  		var (
   217  			wg          sync.WaitGroup
   218  			truncateErr error
   219  			modifyErr   error
   220  		)
   221  		wg.Add(3)
   222  		go func() {
   223  			_, modifyErr = f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
   224  				for i := uint64(100); i < 200; i++ {
   225  					if err := op.AppendRaw("test", i, item); err != nil {
   226  						return err
   227  					}
   228  				}
   229  				return nil
   230  			})
   231  			wg.Done()
   232  		}()
   233  		go func() {
   234  			truncateErr = f.TruncateAncients(10)
   235  			wg.Done()
   236  		}()
   237  		go func() {
   238  			f.AncientSize("test")
   239  			wg.Done()
   240  		}()
   241  		wg.Wait()
   242  
   243  		// Now check the outcome. If the truncate operation went through first, the append
   244  		// fails, otherwise it succeeds. In either case, the freezer should be positioned
   245  		// at 10 after both operations are done.
   246  		if truncateErr != nil {
   247  			t.Fatal("concurrent truncate failed:", err)
   248  		}
   249  		if !(modifyErr == nil || modifyErr == errOutOrderInsertion) {
   250  			t.Fatal("wrong error from concurrent modify:", modifyErr)
   251  		}
   252  		checkAncientCount(t, f, "test", 10)
   253  	}
   254  }
   255  
   256  func newFreezerForTesting(t *testing.T, tables map[string]bool) (*freezer, string) {
   257  	t.Helper()
   258  
   259  	dir, err := ioutil.TempDir("", "freezer")
   260  	if err != nil {
   261  		t.Fatal(err)
   262  	}
   263  	// note: using low max table size here to ensure the tests actually
   264  	// switch between multiple files.
   265  	f, err := newFreezer(dir, "", false, 2049, tables)
   266  	if err != nil {
   267  		t.Fatal("can't open freezer", err)
   268  	}
   269  	return f, dir
   270  }
   271  
   272  // checkAncientCount verifies that the freezer contains n items.
   273  func checkAncientCount(t *testing.T, f *freezer, kind string, n uint64) {
   274  	t.Helper()
   275  
   276  	if frozen, _ := f.Ancients(); frozen != n {
   277  		t.Fatalf("Ancients() returned %d, want %d", frozen, n)
   278  	}
   279  
   280  	// Check at index n-1.
   281  	if n > 0 {
   282  		index := n - 1
   283  		if ok, _ := f.HasAncient(kind, index); !ok {
   284  			t.Errorf("HasAncient(%q, %d) returned false unexpectedly", kind, index)
   285  		}
   286  		if _, err := f.Ancient(kind, index); err != nil {
   287  			t.Errorf("Ancient(%q, %d) returned unexpected error %q", kind, index, err)
   288  		}
   289  	}
   290  
   291  	// Check at index n.
   292  	index := n
   293  	if ok, _ := f.HasAncient(kind, index); ok {
   294  		t.Errorf("HasAncient(%q, %d) returned true unexpectedly", kind, index)
   295  	}
   296  	if _, err := f.Ancient(kind, index); err == nil {
   297  		t.Errorf("Ancient(%q, %d) didn't return expected error", kind, index)
   298  	} else if err != errOutOfBounds {
   299  		t.Errorf("Ancient(%q, %d) returned unexpected error %q", kind, index, err)
   300  	}
   301  }