github.com/ledgerwatch/erigon-lib@v1.0.0/state/aggregator_test.go (about)

     1  package state
     2  
     3  import (
     4  	"context"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"math/rand"
     8  	"os"
     9  	"path"
    10  	"path/filepath"
    11  	"sync/atomic"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/holiman/uint256"
    16  	"github.com/ledgerwatch/erigon-lib/common/background"
    17  	"github.com/ledgerwatch/log/v3"
    18  	"github.com/stretchr/testify/require"
    19  
    20  	"github.com/ledgerwatch/erigon-lib/commitment"
    21  	"github.com/ledgerwatch/erigon-lib/common"
    22  	"github.com/ledgerwatch/erigon-lib/common/length"
    23  	"github.com/ledgerwatch/erigon-lib/compress"
    24  	"github.com/ledgerwatch/erigon-lib/kv"
    25  	"github.com/ledgerwatch/erigon-lib/kv/mdbx"
    26  )
    27  
    28  func testDbAndAggregator(t *testing.T, aggStep uint64) (string, kv.RwDB, *Aggregator) {
    29  	t.Helper()
    30  	path := t.TempDir()
    31  	logger := log.New()
    32  	db := mdbx.NewMDBX(logger).InMem(filepath.Join(path, "db4")).WithTableCfg(func(defaultBuckets kv.TableCfg) kv.TableCfg {
    33  		return kv.ChaindataTablesCfg
    34  	}).MustOpen()
    35  	t.Cleanup(db.Close)
    36  	agg, err := NewAggregator(filepath.Join(path, "e4"), filepath.Join(path, "e4tmp"), aggStep, CommitmentModeDirect, commitment.VariantHexPatriciaTrie, logger)
    37  	require.NoError(t, err)
    38  	return path, db, agg
    39  }
    40  
    41  func TestAggregator_WinAccess(t *testing.T) {
    42  	_, db, agg := testDbAndAggregator(t, 100)
    43  	defer agg.Close()
    44  
    45  	tx, err := db.BeginRwNosync(context.Background())
    46  	require.NoError(t, err)
    47  	defer func() {
    48  		if tx != nil {
    49  			tx.Rollback()
    50  		}
    51  	}()
    52  	agg.SetTx(tx)
    53  
    54  	agg.StartWrites()
    55  
    56  	rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
    57  	for txNum := uint64(1); txNum <= 100; txNum++ {
    58  		agg.SetTxNum(txNum)
    59  
    60  		addr := make([]byte, length.Addr)
    61  		n, err := rnd.Read(addr)
    62  		require.NoError(t, err)
    63  		require.EqualValues(t, length.Addr, n)
    64  
    65  		buf := EncodeAccountBytes(1, uint256.NewInt(uint64(rand.Intn(10e9))), nil, 0)
    66  		err = agg.UpdateAccountData(addr, buf)
    67  		require.NoError(t, err)
    68  
    69  		var v [8]byte
    70  		binary.BigEndian.PutUint64(v[:], txNum)
    71  		require.NoError(t, err)
    72  		require.NoError(t, agg.FinishTx())
    73  	}
    74  	agg.FinishWrites()
    75  
    76  	require.NoError(t, err)
    77  	err = tx.Commit()
    78  	require.NoError(t, err)
    79  	tx = nil
    80  }
    81  
    82  func TestAggregator_Merge(t *testing.T) {
    83  	_, db, agg := testDbAndAggregator(t, 1000)
    84  	defer agg.Close()
    85  
    86  	tx, err := db.BeginRwNosync(context.Background())
    87  	require.NoError(t, err)
    88  	defer func() {
    89  		if tx != nil {
    90  			tx.Rollback()
    91  		}
    92  	}()
    93  	agg.SetTx(tx)
    94  
    95  	agg.StartWrites()
    96  
    97  	txs := uint64(10000)
    98  	rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
    99  
   100  	// keys are encodings of numbers 1..31
   101  	// each key changes value on every txNum which is multiple of the key
   102  	var maxWrite, otherMaxWrite uint64
   103  	for txNum := uint64(1); txNum <= txs; txNum++ {
   104  		agg.SetTxNum(txNum)
   105  
   106  		addr, loc := make([]byte, length.Addr), make([]byte, length.Hash)
   107  
   108  		n, err := rnd.Read(addr)
   109  		require.NoError(t, err)
   110  		require.EqualValues(t, length.Addr, n)
   111  
   112  		n, err = rnd.Read(loc)
   113  		require.NoError(t, err)
   114  		require.EqualValues(t, length.Hash, n)
   115  		//keys[txNum-1] = append(addr, loc...)
   116  
   117  		buf := EncodeAccountBytes(1, uint256.NewInt(0), nil, 0)
   118  		err = agg.UpdateAccountData(addr, buf)
   119  		require.NoError(t, err)
   120  
   121  		err = agg.WriteAccountStorage(addr, loc, []byte{addr[0], loc[0]})
   122  		require.NoError(t, err)
   123  
   124  		var v [8]byte
   125  		binary.BigEndian.PutUint64(v[:], txNum)
   126  		if txNum%135 == 0 {
   127  			err = agg.UpdateCommitmentData([]byte("otherroothash"), v[:])
   128  			otherMaxWrite = txNum
   129  		} else {
   130  			err = agg.UpdateCommitmentData([]byte("roothash"), v[:])
   131  			maxWrite = txNum
   132  		}
   133  		require.NoError(t, err)
   134  		require.NoError(t, agg.FinishTx())
   135  	}
   136  	agg.FinishWrites()
   137  	require.NoError(t, err)
   138  	err = tx.Commit()
   139  	require.NoError(t, err)
   140  	tx = nil
   141  
   142  	// Check the history
   143  	roTx, err := db.BeginRo(context.Background())
   144  	require.NoError(t, err)
   145  	defer roTx.Rollback()
   146  
   147  	dc := agg.MakeContext()
   148  
   149  	v, err := dc.ReadCommitment([]byte("roothash"), roTx)
   150  	require.NoError(t, err)
   151  
   152  	require.EqualValues(t, maxWrite, binary.BigEndian.Uint64(v[:]))
   153  
   154  	v, err = dc.ReadCommitment([]byte("otherroothash"), roTx)
   155  	require.NoError(t, err)
   156  	dc.Close()
   157  
   158  	require.EqualValues(t, otherMaxWrite, binary.BigEndian.Uint64(v[:]))
   159  }
   160  
   161  // here we create a bunch of updates for further aggregation.
   162  // FinishTx should merge underlying files several times
   163  // Expected that:
   164  // - we could close first aggregator and open another with previous data still available
   165  // - new aggregator SeekCommitment must return txNum equal to amount of total txns
   166  func TestAggregator_RestartOnDatadir(t *testing.T) {
   167  	logger := log.New()
   168  	aggStep := uint64(50)
   169  	path, db, agg := testDbAndAggregator(t, aggStep)
   170  
   171  	tx, err := db.BeginRw(context.Background())
   172  	require.NoError(t, err)
   173  	defer func() {
   174  		if tx != nil {
   175  			tx.Rollback()
   176  		}
   177  	}()
   178  	agg.SetTx(tx)
   179  	agg.StartWrites()
   180  
   181  	var latestCommitTxNum uint64
   182  
   183  	rnd := rand.New(rand.NewSource(time.Now().Unix()))
   184  
   185  	txs := (aggStep / 2) * 19
   186  	t.Logf("step=%d tx_count=%d", aggStep, txs)
   187  	var aux [8]byte
   188  	// keys are encodings of numbers 1..31
   189  	// each key changes value on every txNum which is multiple of the key
   190  	var maxWrite uint64
   191  	for txNum := uint64(1); txNum <= txs; txNum++ {
   192  		agg.SetTxNum(txNum)
   193  		binary.BigEndian.PutUint64(aux[:], txNum)
   194  
   195  		addr, loc := make([]byte, length.Addr), make([]byte, length.Hash)
   196  		n, err := rnd.Read(addr)
   197  		require.NoError(t, err)
   198  		require.EqualValues(t, length.Addr, n)
   199  
   200  		n, err = rnd.Read(loc)
   201  		require.NoError(t, err)
   202  		require.EqualValues(t, length.Hash, n)
   203  		//keys[txNum-1] = append(addr, loc...)
   204  
   205  		buf := EncodeAccountBytes(1, uint256.NewInt(0), nil, 0)
   206  		err = agg.UpdateAccountData(addr, buf)
   207  		require.NoError(t, err)
   208  
   209  		err = agg.WriteAccountStorage(addr, loc, []byte{addr[0], loc[0]})
   210  		require.NoError(t, err)
   211  
   212  		err = agg.UpdateCommitmentData([]byte("key"), aux[:])
   213  		require.NoError(t, err)
   214  		maxWrite = txNum
   215  
   216  		require.NoError(t, agg.FinishTx())
   217  	}
   218  	agg.FinishWrites()
   219  	agg.Close()
   220  
   221  	err = tx.Commit()
   222  	require.NoError(t, err)
   223  	tx = nil
   224  
   225  	// Start another aggregator on same datadir
   226  	anotherAgg, err := NewAggregator(filepath.Join(path, "e4"), filepath.Join(path, "e4tmp"), aggStep, CommitmentModeDirect, commitment.VariantHexPatriciaTrie, logger)
   227  	require.NoError(t, err)
   228  	require.NoError(t, anotherAgg.ReopenFolder())
   229  
   230  	defer anotherAgg.Close()
   231  
   232  	rwTx, err := db.BeginRw(context.Background())
   233  	require.NoError(t, err)
   234  	defer func() {
   235  		if rwTx != nil {
   236  			rwTx.Rollback()
   237  		}
   238  	}()
   239  
   240  	anotherAgg.SetTx(rwTx)
   241  	startTx := anotherAgg.EndTxNumMinimax()
   242  	_, sstartTx, err := anotherAgg.SeekCommitment()
   243  	require.NoError(t, err)
   244  	require.GreaterOrEqual(t, sstartTx, startTx)
   245  	require.GreaterOrEqual(t, sstartTx, latestCommitTxNum)
   246  	_ = sstartTx
   247  	rwTx.Rollback()
   248  	rwTx = nil
   249  
   250  	// Check the history
   251  	roTx, err := db.BeginRo(context.Background())
   252  	require.NoError(t, err)
   253  	defer roTx.Rollback()
   254  
   255  	dc := anotherAgg.MakeContext()
   256  	v, err := dc.ReadCommitment([]byte("key"), roTx)
   257  	require.NoError(t, err)
   258  	dc.Close()
   259  
   260  	require.EqualValues(t, maxWrite, binary.BigEndian.Uint64(v[:]))
   261  }
   262  
   263  func TestAggregator_RestartOnFiles(t *testing.T) {
   264  	logger := log.New()
   265  	aggStep := uint64(100)
   266  
   267  	path, db, agg := testDbAndAggregator(t, aggStep)
   268  
   269  	tx, err := db.BeginRw(context.Background())
   270  	require.NoError(t, err)
   271  	defer func() {
   272  		if tx != nil {
   273  			tx.Rollback()
   274  		}
   275  	}()
   276  	agg.SetTx(tx)
   277  	agg.StartWrites()
   278  
   279  	txs := aggStep * 5
   280  	t.Logf("step=%d tx_count=%d\n", aggStep, txs)
   281  
   282  	rnd := rand.New(rand.NewSource(0))
   283  	keys := make([][]byte, txs)
   284  
   285  	for txNum := uint64(1); txNum <= txs; txNum++ {
   286  		agg.SetTxNum(txNum)
   287  
   288  		addr, loc := make([]byte, length.Addr), make([]byte, length.Hash)
   289  		n, err := rnd.Read(addr)
   290  		require.NoError(t, err)
   291  		require.EqualValues(t, length.Addr, n)
   292  
   293  		n, err = rnd.Read(loc)
   294  		require.NoError(t, err)
   295  		require.EqualValues(t, length.Hash, n)
   296  
   297  		buf := EncodeAccountBytes(txNum, uint256.NewInt(1000000000000), nil, 0)
   298  		err = agg.UpdateAccountData(addr, buf[:])
   299  		require.NoError(t, err)
   300  
   301  		err = agg.WriteAccountStorage(addr, loc, []byte{addr[0], loc[0]})
   302  		require.NoError(t, err)
   303  
   304  		keys[txNum-1] = append(addr, loc...)
   305  
   306  		err = agg.FinishTx()
   307  		require.NoError(t, err)
   308  	}
   309  	agg.FinishWrites()
   310  
   311  	err = tx.Commit()
   312  	require.NoError(t, err)
   313  	tx = nil
   314  	db.Close()
   315  	agg.Close()
   316  
   317  	require.NoError(t, os.RemoveAll(filepath.Join(path, "db4")))
   318  
   319  	newDb, err := mdbx.NewMDBX(logger).InMem(filepath.Join(path, "db4")).WithTableCfg(func(defaultBuckets kv.TableCfg) kv.TableCfg {
   320  		return kv.ChaindataTablesCfg
   321  	}).Open()
   322  	require.NoError(t, err)
   323  	t.Cleanup(newDb.Close)
   324  
   325  	newTx, err := newDb.BeginRw(context.Background())
   326  	require.NoError(t, err)
   327  	defer newTx.Rollback()
   328  
   329  	newAgg, err := NewAggregator(path, path, aggStep, CommitmentModeDirect, commitment.VariantHexPatriciaTrie, logger)
   330  	require.NoError(t, err)
   331  	require.NoError(t, newAgg.ReopenFolder())
   332  
   333  	newAgg.SetTx(newTx)
   334  	newAgg.StartWrites()
   335  
   336  	_, latestTx, err := newAgg.SeekCommitment()
   337  	require.NoError(t, err)
   338  	t.Logf("seek to latest_tx=%d", latestTx)
   339  
   340  	ctx := newAgg.defaultCtx
   341  	miss := uint64(0)
   342  	for i, key := range keys {
   343  		if uint64(i+1) >= txs-aggStep {
   344  			continue // finishtx always stores last agg step in db which we deleted, so missing  values which were not aggregated is expected
   345  		}
   346  		stored, err := ctx.ReadAccountData(key[:length.Addr], newTx)
   347  		require.NoError(t, err)
   348  		if len(stored) == 0 {
   349  			miss++
   350  			fmt.Printf("%x [%d/%d]", key, miss, i+1) // txnum starts from 1
   351  			continue
   352  		}
   353  
   354  		nonce, _, _ := DecodeAccountBytes(stored)
   355  		require.EqualValues(t, i+1, nonce)
   356  
   357  		storedV, err := ctx.ReadAccountStorage(key[:length.Addr], key[length.Addr:], newTx)
   358  		require.NoError(t, err)
   359  		require.EqualValues(t, key[0], storedV[0])
   360  		require.EqualValues(t, key[length.Addr], storedV[1])
   361  	}
   362  	newAgg.FinishWrites()
   363  	ctx.Close()
   364  	newAgg.Close()
   365  
   366  	require.NoError(t, err)
   367  }
   368  
   369  func TestAggregator_ReplaceCommittedKeys(t *testing.T) {
   370  	aggStep := uint64(500)
   371  
   372  	_, db, agg := testDbAndAggregator(t, aggStep)
   373  	t.Cleanup(agg.Close)
   374  
   375  	tx, err := db.BeginRw(context.Background())
   376  	require.NoError(t, err)
   377  	defer func() {
   378  		if tx != nil {
   379  			tx.Rollback()
   380  		}
   381  	}()
   382  	agg.SetTx(tx)
   383  	defer agg.StartWrites().FinishWrites()
   384  
   385  	var latestCommitTxNum uint64
   386  	commit := func(txn uint64) error {
   387  		err = tx.Commit()
   388  		require.NoError(t, err)
   389  		tx, err = db.BeginRw(context.Background())
   390  		require.NoError(t, err)
   391  		t.Logf("commit to db txn=%d", txn)
   392  
   393  		atomic.StoreUint64(&latestCommitTxNum, txn)
   394  		agg.SetTx(tx)
   395  		return nil
   396  	}
   397  
   398  	roots := agg.AggregatedRoots()
   399  	txs := (aggStep) * StepsInBiggestFile
   400  	t.Logf("step=%d tx_count=%d", aggStep, txs)
   401  
   402  	rnd := rand.New(rand.NewSource(0))
   403  	keys := make([][]byte, txs/2)
   404  
   405  	for txNum := uint64(1); txNum <= txs/2; txNum++ {
   406  		agg.SetTxNum(txNum)
   407  
   408  		addr, loc := make([]byte, length.Addr), make([]byte, length.Hash)
   409  		n, err := rnd.Read(addr)
   410  		require.NoError(t, err)
   411  		require.EqualValues(t, length.Addr, n)
   412  
   413  		n, err = rnd.Read(loc)
   414  		require.NoError(t, err)
   415  		require.EqualValues(t, length.Hash, n)
   416  		keys[txNum-1] = append(addr, loc...)
   417  
   418  		buf := EncodeAccountBytes(1, uint256.NewInt(0), nil, 0)
   419  		err = agg.UpdateAccountData(addr, buf)
   420  		require.NoError(t, err)
   421  
   422  		err = agg.WriteAccountStorage(addr, loc, []byte{addr[0], loc[0]})
   423  		require.NoError(t, err)
   424  
   425  		err = agg.FinishTx()
   426  		require.NoError(t, err)
   427  		select {
   428  		case <-roots:
   429  			require.NoError(t, commit(txNum))
   430  		default:
   431  			continue
   432  		}
   433  	}
   434  
   435  	half := txs / 2
   436  	for txNum := txs/2 + 1; txNum <= txs; txNum++ {
   437  		agg.SetTxNum(txNum)
   438  
   439  		addr, loc := keys[txNum-1-half][:length.Addr], keys[txNum-1-half][length.Addr:]
   440  
   441  		err = agg.WriteAccountStorage(addr, loc, []byte{addr[0], loc[0]})
   442  		require.NoError(t, err)
   443  
   444  		err = agg.FinishTx()
   445  		require.NoError(t, err)
   446  	}
   447  
   448  	err = tx.Commit()
   449  	tx = nil
   450  
   451  	tx, err = db.BeginRw(context.Background())
   452  	require.NoError(t, err)
   453  
   454  	ctx := agg.defaultCtx
   455  	for _, key := range keys {
   456  		storedV, err := ctx.ReadAccountStorage(key[:length.Addr], key[length.Addr:], tx)
   457  		require.NoError(t, err)
   458  		require.EqualValues(t, key[0], storedV[0])
   459  		require.EqualValues(t, key[length.Addr], storedV[1])
   460  	}
   461  	require.NoError(t, err)
   462  }
   463  
   464  func Test_EncodeCommitmentState(t *testing.T) {
   465  	cs := commitmentState{
   466  		txNum:     rand.Uint64(),
   467  		trieState: make([]byte, 1024),
   468  	}
   469  	n, err := rand.Read(cs.trieState)
   470  	require.NoError(t, err)
   471  	require.EqualValues(t, len(cs.trieState), n)
   472  
   473  	buf, err := cs.Encode()
   474  	require.NoError(t, err)
   475  	require.NotEmpty(t, buf)
   476  
   477  	var dec commitmentState
   478  	err = dec.Decode(buf)
   479  	require.NoError(t, err)
   480  	require.EqualValues(t, cs.txNum, dec.txNum)
   481  	require.EqualValues(t, cs.trieState, dec.trieState)
   482  }
   483  
   484  func Test_BtreeIndex_Seek(t *testing.T) {
   485  	tmp := t.TempDir()
   486  	logger := log.New()
   487  
   488  	keyCount, M := 120000, 1024
   489  	dataPath := generateCompressedKV(t, tmp, 52, 180 /*val size*/, keyCount, logger)
   490  	defer os.RemoveAll(tmp)
   491  
   492  	indexPath := path.Join(tmp, filepath.Base(dataPath)+".bti")
   493  	err := BuildBtreeIndex(dataPath, indexPath, logger)
   494  	require.NoError(t, err)
   495  
   496  	bt, err := OpenBtreeIndex(indexPath, dataPath, uint64(M))
   497  	require.NoError(t, err)
   498  	require.EqualValues(t, bt.KeyCount(), keyCount)
   499  
   500  	keys, err := pivotKeysFromKV(dataPath)
   501  	require.NoError(t, err)
   502  
   503  	for i := 0; i < len(keys); i++ {
   504  		cur, err := bt.Seek(keys[i])
   505  		require.NoErrorf(t, err, "i=%d", i)
   506  		require.EqualValues(t, keys[i], cur.key)
   507  		require.NotEmptyf(t, cur.Value(), "i=%d", i)
   508  		// require.EqualValues(t, uint64(i), cur.Value())
   509  	}
   510  	for i := 1; i < len(keys); i++ {
   511  		alt := common.Copy(keys[i])
   512  		for j := len(alt) - 1; j >= 0; j-- {
   513  			if alt[j] > 0 {
   514  				alt[j] -= 1
   515  				break
   516  			}
   517  		}
   518  		cur, err := bt.Seek(keys[i])
   519  		require.NoError(t, err)
   520  		require.EqualValues(t, keys[i], cur.Key())
   521  	}
   522  
   523  	bt.Close()
   524  }
   525  
   526  func pivotKeysFromKV(dataPath string) ([][]byte, error) {
   527  	decomp, err := compress.NewDecompressor(dataPath)
   528  	if err != nil {
   529  		return nil, err
   530  	}
   531  
   532  	getter := decomp.MakeGetter()
   533  	getter.Reset(0)
   534  
   535  	key := make([]byte, 0, 64)
   536  
   537  	listing := make([][]byte, 0, 1000)
   538  
   539  	for getter.HasNext() {
   540  		if len(listing) > 100000 {
   541  			break
   542  		}
   543  		key, _ := getter.Next(key[:0])
   544  		listing = append(listing, common.Copy(key))
   545  		getter.Skip()
   546  	}
   547  	decomp.Close()
   548  
   549  	return listing, nil
   550  }
   551  
   552  func generateCompressedKV(tb testing.TB, tmp string, keySize, valueSize, keyCount int, logger log.Logger) string {
   553  	tb.Helper()
   554  
   555  	args := BtIndexWriterArgs{
   556  		IndexFile: path.Join(tmp, fmt.Sprintf("%dk.bt", keyCount/1000)),
   557  		TmpDir:    tmp,
   558  		KeyCount:  12,
   559  	}
   560  
   561  	iw, err := NewBtIndexWriter(args, logger)
   562  	require.NoError(tb, err)
   563  
   564  	defer iw.Close()
   565  	rnd := rand.New(rand.NewSource(0))
   566  	values := make([]byte, valueSize)
   567  
   568  	dataPath := path.Join(tmp, fmt.Sprintf("%dk.kv", keyCount/1000))
   569  	comp, err := compress.NewCompressor(context.Background(), "cmp", dataPath, tmp, compress.MinPatternScore, 1, log.LvlDebug, logger)
   570  	require.NoError(tb, err)
   571  
   572  	for i := 0; i < keyCount; i++ {
   573  		key := make([]byte, keySize)
   574  		n, err := rnd.Read(key[:])
   575  		require.EqualValues(tb, keySize, n)
   576  		binary.BigEndian.PutUint64(key[keySize-8:], uint64(i))
   577  		require.NoError(tb, err)
   578  		err = comp.AddWord(key[:])
   579  		require.NoError(tb, err)
   580  
   581  		n, err = rnd.Read(values[:rnd.Intn(valueSize)+1])
   582  		require.NoError(tb, err)
   583  
   584  		err = comp.AddWord(values[:n])
   585  		require.NoError(tb, err)
   586  	}
   587  
   588  	err = comp.Compress()
   589  	require.NoError(tb, err)
   590  	comp.Close()
   591  
   592  	decomp, err := compress.NewDecompressor(dataPath)
   593  	require.NoError(tb, err)
   594  
   595  	getter := decomp.MakeGetter()
   596  	getter.Reset(0)
   597  
   598  	var pos uint64
   599  	key := make([]byte, keySize)
   600  	for i := 0; i < keyCount; i++ {
   601  		if !getter.HasNext() {
   602  			tb.Fatalf("not enough values at %d", i)
   603  			break
   604  		}
   605  
   606  		keys, _ := getter.Next(key[:0])
   607  		err = iw.AddKey(keys[:], pos)
   608  
   609  		pos, _ = getter.Skip()
   610  		require.NoError(tb, err)
   611  	}
   612  	decomp.Close()
   613  
   614  	require.NoError(tb, iw.Build())
   615  	iw.Close()
   616  
   617  	return decomp.FilePath()
   618  }
   619  
   620  func Test_InitBtreeIndex(t *testing.T) {
   621  	logger := log.New()
   622  	tmp := t.TempDir()
   623  
   624  	keyCount, M := 100, uint64(4)
   625  	compPath := generateCompressedKV(t, tmp, 52, 300, keyCount, logger)
   626  	decomp, err := compress.NewDecompressor(compPath)
   627  	require.NoError(t, err)
   628  	defer decomp.Close()
   629  
   630  	err = BuildBtreeIndexWithDecompressor(tmp+".bt", decomp, &background.Progress{}, tmp, logger)
   631  	require.NoError(t, err)
   632  
   633  	bt, err := OpenBtreeIndexWithDecompressor(tmp+".bt", M, decomp)
   634  	require.NoError(t, err)
   635  	require.EqualValues(t, bt.KeyCount(), keyCount)
   636  	bt.Close()
   637  }