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

     1  /*
     2     Copyright 2022 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 state
    18  
    19  import (
    20  	"bytes"
    21  	"container/heap"
    22  	"context"
    23  	"encoding/binary"
    24  	"fmt"
    25  	"math"
    26  	"path/filepath"
    27  	"regexp"
    28  	"strconv"
    29  	"sync/atomic"
    30  	"time"
    31  
    32  	"github.com/RoaringBitmap/roaring/roaring64"
    33  	"github.com/ledgerwatch/log/v3"
    34  	btree2 "github.com/tidwall/btree"
    35  	"golang.org/x/exp/slices"
    36  	"golang.org/x/sync/errgroup"
    37  
    38  	"github.com/ledgerwatch/erigon-lib/common"
    39  	"github.com/ledgerwatch/erigon-lib/common/background"
    40  	"github.com/ledgerwatch/erigon-lib/common/cmp"
    41  	"github.com/ledgerwatch/erigon-lib/common/dir"
    42  	"github.com/ledgerwatch/erigon-lib/compress"
    43  	"github.com/ledgerwatch/erigon-lib/etl"
    44  	"github.com/ledgerwatch/erigon-lib/kv"
    45  	"github.com/ledgerwatch/erigon-lib/kv/bitmapdb"
    46  	"github.com/ledgerwatch/erigon-lib/kv/iter"
    47  	"github.com/ledgerwatch/erigon-lib/kv/order"
    48  	"github.com/ledgerwatch/erigon-lib/recsplit"
    49  	"github.com/ledgerwatch/erigon-lib/recsplit/eliasfano32"
    50  )
    51  
    52  type History struct {
    53  	*InvertedIndex
    54  
    55  	// Files:
    56  	//  .v - list of values
    57  	//  .vi - txNum+key -> offset in .v
    58  	files *btree2.BTreeG[*filesItem] // thread-safe, but maybe need 1 RWLock for all trees in AggregatorV3
    59  
    60  	// roFiles derivative from field `file`, but without garbage (canDelete=true, overlaps, etc...)
    61  	// MakeContext() using this field in zero-copy way
    62  	roFiles atomic.Pointer[[]ctxItem]
    63  
    64  	historyValsTable        string // key1+key2+txnNum -> oldValue , stores values BEFORE change
    65  	compressWorkers         int
    66  	compressVals            bool
    67  	integrityFileExtensions []string
    68  
    69  	// not large:
    70  	//   keys: txNum -> key1+key2
    71  	//   vals: key1+key2 -> txNum + value (DupSort)
    72  	// large:
    73  	//   keys: txNum -> key1+key2
    74  	//   vals: key1+key2+txNum -> value (not DupSort)
    75  	largeValues bool // can't use DupSort optimization (aka. prefix-compression) if values size > 4kb
    76  
    77  	garbageFiles []*filesItem // files that exist on disk, but ignored on opening folder - because they are garbage
    78  
    79  	wal    *historyWAL
    80  	logger log.Logger
    81  }
    82  
    83  func NewHistory(dir, tmpdir string, aggregationStep uint64,
    84  	filenameBase, indexKeysTable, indexTable, historyValsTable string,
    85  	compressVals bool, integrityFileExtensions []string, largeValues bool, logger log.Logger) (*History, error) {
    86  	h := History{
    87  		files:                   btree2.NewBTreeGOptions[*filesItem](filesItemLess, btree2.Options{Degree: 128, NoLocks: false}),
    88  		historyValsTable:        historyValsTable,
    89  		compressVals:            compressVals,
    90  		compressWorkers:         1,
    91  		integrityFileExtensions: integrityFileExtensions,
    92  		largeValues:             largeValues,
    93  		logger:                  logger,
    94  	}
    95  	h.roFiles.Store(&[]ctxItem{})
    96  	var err error
    97  	h.InvertedIndex, err = NewInvertedIndex(dir, tmpdir, aggregationStep, filenameBase, indexKeysTable, indexTable, true, append(slices.Clone(h.integrityFileExtensions), "v"), logger)
    98  	if err != nil {
    99  		return nil, fmt.Errorf("NewHistory: %s, %w", filenameBase, err)
   100  	}
   101  
   102  	return &h, nil
   103  }
   104  
   105  // OpenList - main method to open list of files.
   106  // It's ok if some files was open earlier.
   107  // If some file already open: noop.
   108  // If some file already open but not in provided list: close and remove from `files` field.
   109  func (h *History) OpenList(fNames []string) error {
   110  	if err := h.InvertedIndex.OpenList(fNames); err != nil {
   111  		return err
   112  	}
   113  	return h.openList(fNames)
   114  
   115  }
   116  func (h *History) openList(fNames []string) error {
   117  	h.closeWhatNotInList(fNames)
   118  	h.garbageFiles = h.scanStateFiles(fNames)
   119  	if err := h.openFiles(); err != nil {
   120  		return fmt.Errorf("History.OpenList: %s, %w", h.filenameBase, err)
   121  	}
   122  	return nil
   123  }
   124  
   125  func (h *History) OpenFolder() error {
   126  	files, err := h.fileNamesOnDisk()
   127  	if err != nil {
   128  		return err
   129  	}
   130  	return h.OpenList(files)
   131  }
   132  
   133  // scanStateFiles
   134  // returns `uselessFiles` where file "is useless" means: it's subset of frozen file. such files can be safely deleted. subset of non-frozen file may be useful
   135  func (h *History) scanStateFiles(fNames []string) (garbageFiles []*filesItem) {
   136  	re := regexp.MustCompile("^" + h.filenameBase + ".([0-9]+)-([0-9]+).v$")
   137  	var err error
   138  Loop:
   139  	for _, name := range fNames {
   140  		subs := re.FindStringSubmatch(name)
   141  		if len(subs) != 3 {
   142  			if len(subs) != 0 {
   143  				h.logger.Warn("[snapshots] file ignored by inverted index scan, more than 3 submatches", "name", name, "submatches", len(subs))
   144  			}
   145  			continue
   146  		}
   147  		var startStep, endStep uint64
   148  		if startStep, err = strconv.ParseUint(subs[1], 10, 64); err != nil {
   149  			h.logger.Warn("[snapshots] file ignored by inverted index scan, parsing startTxNum", "error", err, "name", name)
   150  			continue
   151  		}
   152  		if endStep, err = strconv.ParseUint(subs[2], 10, 64); err != nil {
   153  			h.logger.Warn("[snapshots] file ignored by inverted index scan, parsing endTxNum", "error", err, "name", name)
   154  			continue
   155  		}
   156  		if startStep > endStep {
   157  			h.logger.Warn("[snapshots] file ignored by inverted index scan, startTxNum > endTxNum", "name", name)
   158  			continue
   159  		}
   160  
   161  		startTxNum, endTxNum := startStep*h.aggregationStep, endStep*h.aggregationStep
   162  		var newFile = newFilesItem(startTxNum, endTxNum, h.aggregationStep)
   163  
   164  		for _, ext := range h.integrityFileExtensions {
   165  			requiredFile := fmt.Sprintf("%s.%d-%d.%s", h.filenameBase, startStep, endStep, ext)
   166  			if !dir.FileExist(filepath.Join(h.dir, requiredFile)) {
   167  				h.logger.Debug(fmt.Sprintf("[snapshots] skip %s because %s doesn't exists", name, requiredFile))
   168  				garbageFiles = append(garbageFiles, newFile)
   169  				continue Loop
   170  			}
   171  		}
   172  
   173  		if _, has := h.files.Get(newFile); has {
   174  			continue
   175  		}
   176  
   177  		addNewFile := true
   178  		var subSets []*filesItem
   179  		h.files.Walk(func(items []*filesItem) bool {
   180  			for _, item := range items {
   181  				if item.isSubsetOf(newFile) {
   182  					subSets = append(subSets, item)
   183  					continue
   184  				}
   185  
   186  				if newFile.isSubsetOf(item) {
   187  					if item.frozen {
   188  						addNewFile = false
   189  						garbageFiles = append(garbageFiles, newFile)
   190  					}
   191  					continue
   192  				}
   193  			}
   194  			return true
   195  		})
   196  		if addNewFile {
   197  			h.files.Set(newFile)
   198  		}
   199  	}
   200  	return garbageFiles
   201  }
   202  
   203  func (h *History) openFiles() error {
   204  	var totalKeys uint64
   205  	var err error
   206  	invalidFileItems := make([]*filesItem, 0)
   207  	h.files.Walk(func(items []*filesItem) bool {
   208  		for _, item := range items {
   209  			if item.decompressor != nil {
   210  				continue
   211  			}
   212  			fromStep, toStep := item.startTxNum/h.aggregationStep, item.endTxNum/h.aggregationStep
   213  			datPath := filepath.Join(h.dir, fmt.Sprintf("%s.%d-%d.v", h.filenameBase, fromStep, toStep))
   214  			if !dir.FileExist(datPath) {
   215  				invalidFileItems = append(invalidFileItems, item)
   216  				continue
   217  			}
   218  			if item.decompressor, err = compress.NewDecompressor(datPath); err != nil {
   219  				h.logger.Debug("Hisrory.openFiles: %w, %s", err, datPath)
   220  				return false
   221  			}
   222  
   223  			if item.index != nil {
   224  				continue
   225  			}
   226  			idxPath := filepath.Join(h.dir, fmt.Sprintf("%s.%d-%d.vi", h.filenameBase, fromStep, toStep))
   227  			if dir.FileExist(idxPath) {
   228  				if item.index, err = recsplit.OpenIndex(idxPath); err != nil {
   229  					h.logger.Debug(fmt.Errorf("Hisrory.openFiles: %w, %s", err, idxPath).Error())
   230  					return false
   231  				}
   232  				totalKeys += item.index.KeyCount()
   233  			}
   234  		}
   235  		return true
   236  	})
   237  	if err != nil {
   238  		return err
   239  	}
   240  	for _, item := range invalidFileItems {
   241  		h.files.Delete(item)
   242  	}
   243  
   244  	h.reCalcRoFiles()
   245  	return nil
   246  }
   247  
   248  func (h *History) closeWhatNotInList(fNames []string) {
   249  	var toDelete []*filesItem
   250  	h.files.Walk(func(items []*filesItem) bool {
   251  	Loop1:
   252  		for _, item := range items {
   253  			for _, protectName := range fNames {
   254  				if item.decompressor != nil && item.decompressor.FileName() == protectName {
   255  					continue Loop1
   256  				}
   257  			}
   258  			toDelete = append(toDelete, item)
   259  		}
   260  		return true
   261  	})
   262  	for _, item := range toDelete {
   263  		if item.decompressor != nil {
   264  			item.decompressor.Close()
   265  			item.decompressor = nil
   266  		}
   267  		if item.index != nil {
   268  			item.index.Close()
   269  			item.index = nil
   270  		}
   271  		h.files.Delete(item)
   272  	}
   273  }
   274  
   275  func (h *History) Close() {
   276  	h.InvertedIndex.Close()
   277  	h.closeWhatNotInList([]string{})
   278  	h.reCalcRoFiles()
   279  }
   280  
   281  func (h *History) Files() (res []string) {
   282  	h.files.Walk(func(items []*filesItem) bool {
   283  		for _, item := range items {
   284  			if item.decompressor != nil {
   285  				res = append(res, item.decompressor.FileName())
   286  			}
   287  		}
   288  		return true
   289  	})
   290  	res = append(res, h.InvertedIndex.Files()...)
   291  	return res
   292  }
   293  
   294  func (h *History) missedIdxFiles() (l []*filesItem) {
   295  	h.files.Walk(func(items []*filesItem) bool { // don't run slow logic while iterating on btree
   296  		for _, item := range items {
   297  			fromStep, toStep := item.startTxNum/h.aggregationStep, item.endTxNum/h.aggregationStep
   298  			if !dir.FileExist(filepath.Join(h.dir, fmt.Sprintf("%s.%d-%d.vi", h.filenameBase, fromStep, toStep))) {
   299  				l = append(l, item)
   300  			}
   301  		}
   302  		return true
   303  	})
   304  	return l
   305  }
   306  
   307  // BuildMissedIndices - produce .efi/.vi/.kvi from .ef/.v/.kv
   308  func (hc *HistoryContext) BuildOptionalMissedIndices(ctx context.Context) (err error) {
   309  	return hc.h.localityIndex.BuildMissedIndices(ctx, hc.ic)
   310  }
   311  
   312  func (h *History) buildVi(ctx context.Context, item *filesItem, p *background.Progress) (err error) {
   313  	search := &filesItem{startTxNum: item.startTxNum, endTxNum: item.endTxNum}
   314  	iiItem, ok := h.InvertedIndex.files.Get(search)
   315  	if !ok {
   316  		return nil
   317  	}
   318  
   319  	fromStep, toStep := item.startTxNum/h.aggregationStep, item.endTxNum/h.aggregationStep
   320  	fName := fmt.Sprintf("%s.%d-%d.vi", h.filenameBase, fromStep, toStep)
   321  	idxPath := filepath.Join(h.dir, fName)
   322  
   323  	//h.logger.Info("[snapshots] build idx", "file", fName)
   324  
   325  	p.Name.Store(&fName)
   326  	p.Total.Store(uint64(iiItem.decompressor.Count()) * 2)
   327  
   328  	count, err := iterateForVi(item, iiItem, p, h.compressVals, func(v []byte) error { return nil })
   329  	if err != nil {
   330  		return err
   331  	}
   332  	return buildVi(ctx, item, iiItem, idxPath, h.tmpdir, count, p, h.compressVals, h.logger)
   333  }
   334  
   335  func (h *History) BuildMissedIndices(ctx context.Context, g *errgroup.Group, ps *background.ProgressSet) {
   336  	h.InvertedIndex.BuildMissedIndices(ctx, g, ps)
   337  	missedFiles := h.missedIdxFiles()
   338  	for _, item := range missedFiles {
   339  		item := item
   340  		g.Go(func() error {
   341  			p := &background.Progress{}
   342  			ps.Add(p)
   343  			defer ps.Delete(p)
   344  			return h.buildVi(ctx, item, p)
   345  		})
   346  	}
   347  }
   348  
   349  func iterateForVi(historyItem, iiItem *filesItem, p *background.Progress, compressVals bool, f func(v []byte) error) (count int, err error) {
   350  	var cp CursorHeap
   351  	heap.Init(&cp)
   352  	g := iiItem.decompressor.MakeGetter()
   353  	g.Reset(0)
   354  	if g.HasNext() {
   355  		g2 := historyItem.decompressor.MakeGetter()
   356  		key, _ := g.NextUncompressed()
   357  		val, _ := g.NextUncompressed()
   358  		heap.Push(&cp, &CursorItem{
   359  			t:        FILE_CURSOR,
   360  			dg:       g,
   361  			dg2:      g2,
   362  			key:      key,
   363  			val:      val,
   364  			endTxNum: iiItem.endTxNum,
   365  			reverse:  false,
   366  		})
   367  	}
   368  
   369  	// In the loop below, the pair `keyBuf=>valBuf` is always 1 item behind `lastKey=>lastVal`.
   370  	// `lastKey` and `lastVal` are taken from the top of the multi-way merge (assisted by the CursorHeap cp), but not processed right away
   371  	// instead, the pair from the previous iteration is processed first - `keyBuf=>valBuf`. After that, `keyBuf` and `valBuf` are assigned
   372  	// to `lastKey` and `lastVal` correspondingly, and the next step of multi-way merge happens. Therefore, after the multi-way merge loop
   373  	// (when CursorHeap cp is empty), there is a need to process the last pair `keyBuf=>valBuf`, because it was one step behind
   374  	var valBuf []byte
   375  	for cp.Len() > 0 {
   376  		lastKey := common.Copy(cp[0].key)
   377  		// Advance all the items that have this key (including the top)
   378  		//var mergeOnce bool
   379  		for cp.Len() > 0 && bytes.Equal(cp[0].key, lastKey) {
   380  			ci1 := cp[0]
   381  			keysCount := eliasfano32.Count(ci1.val)
   382  			for i := uint64(0); i < keysCount; i++ {
   383  				if compressVals {
   384  					valBuf, _ = ci1.dg2.Next(valBuf[:0])
   385  				} else {
   386  					valBuf, _ = ci1.dg2.NextUncompressed()
   387  				}
   388  				if err = f(valBuf); err != nil {
   389  					return count, err
   390  				}
   391  			}
   392  			count += int(keysCount)
   393  			if ci1.dg.HasNext() {
   394  				ci1.key, _ = ci1.dg.NextUncompressed()
   395  				ci1.val, _ = ci1.dg.NextUncompressed()
   396  				heap.Fix(&cp, 0)
   397  			} else {
   398  				heap.Remove(&cp, 0)
   399  			}
   400  
   401  			p.Processed.Add(1)
   402  		}
   403  	}
   404  	return count, nil
   405  }
   406  
   407  func buildVi(ctx context.Context, historyItem, iiItem *filesItem, historyIdxPath, tmpdir string, count int, p *background.Progress, compressVals bool, logger log.Logger) error {
   408  	rs, err := recsplit.NewRecSplit(recsplit.RecSplitArgs{
   409  		KeyCount:    count,
   410  		Enums:       false,
   411  		BucketSize:  2000,
   412  		LeafSize:    8,
   413  		TmpDir:      tmpdir,
   414  		IndexFile:   historyIdxPath,
   415  		EtlBufLimit: etl.BufferOptimalSize / 2,
   416  	}, logger)
   417  	if err != nil {
   418  		return fmt.Errorf("create recsplit: %w", err)
   419  	}
   420  	rs.LogLvl(log.LvlTrace)
   421  	defer rs.Close()
   422  	var historyKey []byte
   423  	var txKey [8]byte
   424  	var valOffset uint64
   425  
   426  	defer iiItem.decompressor.EnableMadvNormal().DisableReadAhead()
   427  	defer historyItem.decompressor.EnableMadvNormal().DisableReadAhead()
   428  
   429  	g := iiItem.decompressor.MakeGetter()
   430  	g2 := historyItem.decompressor.MakeGetter()
   431  	var keyBuf, valBuf []byte
   432  	for {
   433  		g.Reset(0)
   434  		g2.Reset(0)
   435  		valOffset = 0
   436  		for g.HasNext() {
   437  			select {
   438  			case <-ctx.Done():
   439  				return ctx.Err()
   440  			default:
   441  			}
   442  
   443  			keyBuf, _ = g.NextUncompressed()
   444  			valBuf, _ = g.NextUncompressed()
   445  			ef, _ := eliasfano32.ReadEliasFano(valBuf)
   446  			efIt := ef.Iterator()
   447  			for efIt.HasNext() {
   448  				txNum, _ := efIt.Next()
   449  				binary.BigEndian.PutUint64(txKey[:], txNum)
   450  				historyKey = append(append(historyKey[:0], txKey[:]...), keyBuf...)
   451  				if err = rs.AddKey(historyKey, valOffset); err != nil {
   452  					return err
   453  				}
   454  				if compressVals {
   455  					valOffset, _ = g2.Skip()
   456  				} else {
   457  					valOffset, _ = g2.SkipUncompressed()
   458  				}
   459  			}
   460  
   461  			p.Processed.Add(1)
   462  		}
   463  		if err = rs.Build(ctx); err != nil {
   464  			if rs.Collision() {
   465  				logger.Info("Building recsplit. Collision happened. It's ok. Restarting...")
   466  				rs.ResetNextSalt()
   467  			} else {
   468  				return fmt.Errorf("build %s idx: %w", historyIdxPath, err)
   469  			}
   470  		} else {
   471  			break
   472  		}
   473  	}
   474  	return nil
   475  }
   476  
   477  func (h *History) AddPrevValue(key1, key2, original []byte) (err error) {
   478  	if original == nil {
   479  		original = []byte{}
   480  	}
   481  	return h.wal.addPrevValue(key1, key2, original)
   482  }
   483  
   484  func (h *History) DiscardHistory() {
   485  	h.InvertedIndex.StartWrites()
   486  	h.wal = h.newWriter(h.tmpdir, false, true)
   487  }
   488  func (h *History) StartUnbufferedWrites() {
   489  	h.InvertedIndex.StartUnbufferedWrites()
   490  	h.wal = h.newWriter(h.tmpdir, false, false)
   491  }
   492  func (h *History) StartWrites() {
   493  	h.InvertedIndex.StartWrites()
   494  	h.wal = h.newWriter(h.tmpdir, true, false)
   495  }
   496  func (h *History) FinishWrites() {
   497  	h.InvertedIndex.FinishWrites()
   498  	h.wal.close()
   499  	h.wal = nil
   500  }
   501  
   502  func (h *History) Rotate() historyFlusher {
   503  	w := h.wal
   504  	h.wal = h.newWriter(h.wal.tmpdir, h.wal.buffered, h.wal.discard)
   505  	return historyFlusher{h: w, i: h.InvertedIndex.Rotate()}
   506  }
   507  
   508  type historyFlusher struct {
   509  	h *historyWAL
   510  	i *invertedIndexWAL
   511  }
   512  
   513  func (f historyFlusher) Flush(ctx context.Context, tx kv.RwTx) error {
   514  	if err := f.i.Flush(ctx, tx); err != nil {
   515  		return err
   516  	}
   517  	if err := f.h.flush(ctx, tx); err != nil {
   518  		return err
   519  	}
   520  	return nil
   521  }
   522  
   523  type historyWAL struct {
   524  	h                *History
   525  	historyVals      *etl.Collector
   526  	tmpdir           string
   527  	autoIncrementBuf []byte
   528  	historyKey       []byte
   529  	buffered         bool
   530  	discard          bool
   531  
   532  	// not large:
   533  	//   keys: txNum -> key1+key2
   534  	//   vals: key1+key2 -> txNum + value (DupSort)
   535  	// large:
   536  	//   keys: txNum -> key1+key2
   537  	//   vals: key1+key2+txNum -> value (not DupSort)
   538  	largeValues bool
   539  }
   540  
   541  func (h *historyWAL) close() {
   542  	if h == nil { // allow dobule-close
   543  		return
   544  	}
   545  	if h.historyVals != nil {
   546  		h.historyVals.Close()
   547  	}
   548  }
   549  
   550  func (h *History) newWriter(tmpdir string, buffered, discard bool) *historyWAL {
   551  	w := &historyWAL{h: h,
   552  		tmpdir:   tmpdir,
   553  		buffered: buffered,
   554  		discard:  discard,
   555  
   556  		autoIncrementBuf: make([]byte, 8),
   557  		historyKey:       make([]byte, 0, 128),
   558  		largeValues:      h.largeValues,
   559  	}
   560  	if buffered {
   561  		w.historyVals = etl.NewCollector(h.historyValsTable, tmpdir, etl.NewSortableBuffer(WALCollectorRAM), h.logger)
   562  		w.historyVals.LogLvl(log.LvlTrace)
   563  	}
   564  	return w
   565  }
   566  
   567  func (h *historyWAL) flush(ctx context.Context, tx kv.RwTx) error {
   568  	if h.discard || !h.buffered {
   569  		return nil
   570  	}
   571  	if err := h.historyVals.Load(tx, h.h.historyValsTable, loadFunc, etl.TransformArgs{Quit: ctx.Done()}); err != nil {
   572  		return err
   573  	}
   574  	h.close()
   575  	return nil
   576  }
   577  
   578  func (h *historyWAL) addPrevValue(key1, key2, original []byte) error {
   579  	if h.discard {
   580  		return nil
   581  	}
   582  
   583  	ii := h.h.InvertedIndex
   584  	if h.largeValues {
   585  		lk := len(key1) + len(key2)
   586  		historyKey := h.historyKey[:lk+8]
   587  		copy(historyKey, key1)
   588  		if len(key2) > 0 {
   589  			copy(historyKey[len(key1):], key2)
   590  		}
   591  		copy(historyKey[lk:], h.h.InvertedIndex.txNumBytes[:])
   592  
   593  		if !h.buffered {
   594  			if err := h.h.tx.Put(h.h.historyValsTable, historyKey, original); err != nil {
   595  				return err
   596  			}
   597  			if err := ii.tx.Put(ii.indexKeysTable, ii.txNumBytes[:], historyKey[:lk]); err != nil {
   598  				return err
   599  			}
   600  			return nil
   601  		}
   602  		if err := h.historyVals.Collect(historyKey, original); err != nil {
   603  			return err
   604  		}
   605  		if err := ii.wal.indexKeys.Collect(ii.txNumBytes[:], historyKey[:lk]); err != nil {
   606  			return err
   607  		}
   608  		return nil
   609  	}
   610  
   611  	lk := len(key1) + len(key2)
   612  	historyKey := h.historyKey[:lk+8+len(original)]
   613  	copy(historyKey, key1)
   614  	copy(historyKey[len(key1):], key2)
   615  	copy(historyKey[lk:], h.h.InvertedIndex.txNumBytes[:])
   616  	copy(historyKey[lk+8:], original)
   617  	historyKey1 := historyKey[:lk]
   618  	historyVal := historyKey[lk:]
   619  	invIdxVal := historyKey[:lk]
   620  
   621  	if !h.buffered {
   622  		if err := h.h.tx.Put(h.h.historyValsTable, historyKey1, historyVal); err != nil {
   623  			return err
   624  		}
   625  		if err := ii.tx.Put(ii.indexKeysTable, ii.txNumBytes[:], invIdxVal); err != nil {
   626  			return err
   627  		}
   628  		return nil
   629  	}
   630  	if err := h.historyVals.Collect(historyKey1, historyVal); err != nil {
   631  		return err
   632  	}
   633  	if err := ii.wal.indexKeys.Collect(ii.txNumBytes[:], invIdxVal); err != nil {
   634  		return err
   635  	}
   636  	return nil
   637  }
   638  
   639  type HistoryCollation struct {
   640  	historyComp  *compress.Compressor
   641  	indexBitmaps map[string]*roaring64.Bitmap
   642  	historyPath  string
   643  	historyCount int
   644  }
   645  
   646  func (c HistoryCollation) Close() {
   647  	if c.historyComp != nil {
   648  		c.historyComp.Close()
   649  	}
   650  	for _, b := range c.indexBitmaps {
   651  		bitmapdb.ReturnToPool64(b)
   652  	}
   653  }
   654  
   655  func (h *History) collate(step, txFrom, txTo uint64, roTx kv.Tx) (HistoryCollation, error) {
   656  	var historyComp *compress.Compressor
   657  	var err error
   658  	closeComp := true
   659  	defer func() {
   660  		if closeComp {
   661  			if historyComp != nil {
   662  				historyComp.Close()
   663  			}
   664  		}
   665  	}()
   666  	historyPath := filepath.Join(h.dir, fmt.Sprintf("%s.%d-%d.v", h.filenameBase, step, step+1))
   667  	if historyComp, err = compress.NewCompressor(context.Background(), "collate history", historyPath, h.tmpdir, compress.MinPatternScore, h.compressWorkers, log.LvlTrace, h.logger); err != nil {
   668  		return HistoryCollation{}, fmt.Errorf("create %s history compressor: %w", h.filenameBase, err)
   669  	}
   670  	keysCursor, err := roTx.CursorDupSort(h.indexKeysTable)
   671  	if err != nil {
   672  		return HistoryCollation{}, fmt.Errorf("create %s history cursor: %w", h.filenameBase, err)
   673  	}
   674  	defer keysCursor.Close()
   675  	indexBitmaps := map[string]*roaring64.Bitmap{}
   676  	var txKey [8]byte
   677  	binary.BigEndian.PutUint64(txKey[:], txFrom)
   678  	var k, v []byte
   679  	for k, v, err = keysCursor.Seek(txKey[:]); err == nil && k != nil; k, v, err = keysCursor.Next() {
   680  		txNum := binary.BigEndian.Uint64(k)
   681  		if txNum >= txTo {
   682  			break
   683  		}
   684  		var bitmap *roaring64.Bitmap
   685  		var ok bool
   686  		if bitmap, ok = indexBitmaps[string(v)]; !ok {
   687  			bitmap = bitmapdb.NewBitmap64()
   688  			indexBitmaps[string(v)] = bitmap
   689  		}
   690  		bitmap.Add(txNum)
   691  	}
   692  	if err != nil {
   693  		return HistoryCollation{}, fmt.Errorf("iterate over %s history cursor: %w", h.filenameBase, err)
   694  	}
   695  	keys := make([]string, 0, len(indexBitmaps))
   696  	for key := range indexBitmaps {
   697  		keys = append(keys, key)
   698  	}
   699  	slices.Sort(keys)
   700  	historyCount := 0
   701  	keyBuf := make([]byte, 256)
   702  
   703  	var c kv.Cursor
   704  	var cd kv.CursorDupSort
   705  	if h.largeValues {
   706  		c, err = roTx.Cursor(h.historyValsTable)
   707  		if err != nil {
   708  			return HistoryCollation{}, err
   709  		}
   710  		defer c.Close()
   711  	} else {
   712  		cd, err = roTx.CursorDupSort(h.historyValsTable)
   713  		if err != nil {
   714  			return HistoryCollation{}, err
   715  		}
   716  		defer cd.Close()
   717  	}
   718  	for _, key := range keys {
   719  		bitmap := indexBitmaps[key]
   720  		it := bitmap.Iterator()
   721  		copy(keyBuf, key)
   722  		keyBuf = keyBuf[:len(key)+8]
   723  		for it.HasNext() {
   724  			txNum := it.Next()
   725  			binary.BigEndian.PutUint64(keyBuf[len(key):], txNum)
   726  			//TODO: use cursor range
   727  			if h.largeValues {
   728  				val, err := roTx.GetOne(h.historyValsTable, keyBuf)
   729  				if err != nil {
   730  					return HistoryCollation{}, fmt.Errorf("get %s history val [%x]: %w", h.filenameBase, k, err)
   731  				}
   732  				if len(val) == 0 {
   733  					val = nil
   734  				}
   735  				if err = historyComp.AddUncompressedWord(val); err != nil {
   736  					return HistoryCollation{}, fmt.Errorf("add %s history val [%x]=>[%x]: %w", h.filenameBase, k, val, err)
   737  				}
   738  			} else {
   739  				val, err := cd.SeekBothRange(keyBuf[:len(key)], keyBuf[len(key):])
   740  				if err != nil {
   741  					return HistoryCollation{}, err
   742  				}
   743  				if val != nil && binary.BigEndian.Uint64(val) == txNum {
   744  					val = val[8:]
   745  				} else {
   746  					val = nil
   747  				}
   748  				if err = historyComp.AddUncompressedWord(val); err != nil {
   749  					return HistoryCollation{}, fmt.Errorf("add %s history val [%x]=>[%x]: %w", h.filenameBase, k, val, err)
   750  				}
   751  			}
   752  			historyCount++
   753  		}
   754  	}
   755  	closeComp = false
   756  	return HistoryCollation{
   757  		historyPath:  historyPath,
   758  		historyComp:  historyComp,
   759  		historyCount: historyCount,
   760  		indexBitmaps: indexBitmaps,
   761  	}, nil
   762  }
   763  
   764  type HistoryFiles struct {
   765  	historyDecomp   *compress.Decompressor
   766  	historyIdx      *recsplit.Index
   767  	efHistoryDecomp *compress.Decompressor
   768  	efHistoryIdx    *recsplit.Index
   769  }
   770  
   771  func (sf HistoryFiles) Close() {
   772  	if sf.historyDecomp != nil {
   773  		sf.historyDecomp.Close()
   774  	}
   775  	if sf.historyIdx != nil {
   776  		sf.historyIdx.Close()
   777  	}
   778  	if sf.efHistoryDecomp != nil {
   779  		sf.efHistoryDecomp.Close()
   780  	}
   781  	if sf.efHistoryIdx != nil {
   782  		sf.efHistoryIdx.Close()
   783  	}
   784  }
   785  func (h *History) reCalcRoFiles() {
   786  	roFiles := ctxFiles(h.files)
   787  	h.roFiles.Store(&roFiles)
   788  }
   789  
   790  // buildFiles performs potentially resource intensive operations of creating
   791  // static files and their indices
   792  func (h *History) buildFiles(ctx context.Context, step uint64, collation HistoryCollation, ps *background.ProgressSet) (HistoryFiles, error) {
   793  	historyComp := collation.historyComp
   794  	if h.noFsync {
   795  		historyComp.DisableFsync()
   796  	}
   797  	var historyDecomp, efHistoryDecomp *compress.Decompressor
   798  	var historyIdx, efHistoryIdx *recsplit.Index
   799  	var efHistoryComp *compress.Compressor
   800  	var rs *recsplit.RecSplit
   801  	closeComp := true
   802  	defer func() {
   803  		if closeComp {
   804  			if historyComp != nil {
   805  				historyComp.Close()
   806  			}
   807  			if historyDecomp != nil {
   808  				historyDecomp.Close()
   809  			}
   810  			if historyIdx != nil {
   811  				historyIdx.Close()
   812  			}
   813  			if efHistoryComp != nil {
   814  				efHistoryComp.Close()
   815  			}
   816  			if efHistoryDecomp != nil {
   817  				efHistoryDecomp.Close()
   818  			}
   819  			if efHistoryIdx != nil {
   820  				efHistoryIdx.Close()
   821  			}
   822  			if rs != nil {
   823  				rs.Close()
   824  			}
   825  		}
   826  	}()
   827  
   828  	var historyIdxPath, efHistoryPath string
   829  
   830  	{
   831  		historyIdxFileName := fmt.Sprintf("%s.%d-%d.vi", h.filenameBase, step, step+1)
   832  		p := ps.AddNew(historyIdxFileName, 1)
   833  		defer ps.Delete(p)
   834  		historyIdxPath = filepath.Join(h.dir, historyIdxFileName)
   835  		if err := historyComp.Compress(); err != nil {
   836  			return HistoryFiles{}, fmt.Errorf("compress %s history: %w", h.filenameBase, err)
   837  		}
   838  		historyComp.Close()
   839  		historyComp = nil
   840  		ps.Delete(p)
   841  	}
   842  
   843  	keys := make([]string, 0, len(collation.indexBitmaps))
   844  	for key := range collation.indexBitmaps {
   845  		keys = append(keys, key)
   846  	}
   847  	slices.Sort(keys)
   848  
   849  	{
   850  		var err error
   851  		if historyDecomp, err = compress.NewDecompressor(collation.historyPath); err != nil {
   852  			return HistoryFiles{}, fmt.Errorf("open %s history decompressor: %w", h.filenameBase, err)
   853  		}
   854  
   855  		// Build history ef
   856  		efHistoryFileName := fmt.Sprintf("%s.%d-%d.ef", h.filenameBase, step, step+1)
   857  
   858  		p := ps.AddNew(efHistoryFileName, 1)
   859  		defer ps.Delete(p)
   860  		efHistoryPath = filepath.Join(h.dir, efHistoryFileName)
   861  		efHistoryComp, err = compress.NewCompressor(ctx, "ef history", efHistoryPath, h.tmpdir, compress.MinPatternScore, h.compressWorkers, log.LvlTrace, h.logger)
   862  		if err != nil {
   863  			return HistoryFiles{}, fmt.Errorf("create %s ef history compressor: %w", h.filenameBase, err)
   864  		}
   865  		if h.noFsync {
   866  			efHistoryComp.DisableFsync()
   867  		}
   868  		var buf []byte
   869  		for _, key := range keys {
   870  			if err = efHistoryComp.AddUncompressedWord([]byte(key)); err != nil {
   871  				return HistoryFiles{}, fmt.Errorf("add %s ef history key [%x]: %w", h.InvertedIndex.filenameBase, key, err)
   872  			}
   873  			bitmap := collation.indexBitmaps[key]
   874  			ef := eliasfano32.NewEliasFano(bitmap.GetCardinality(), bitmap.Maximum())
   875  			it := bitmap.Iterator()
   876  			for it.HasNext() {
   877  				txNum := it.Next()
   878  				ef.AddOffset(txNum)
   879  			}
   880  			ef.Build()
   881  			buf = ef.AppendBytes(buf[:0])
   882  			if err = efHistoryComp.AddUncompressedWord(buf); err != nil {
   883  				return HistoryFiles{}, fmt.Errorf("add %s ef history val: %w", h.filenameBase, err)
   884  			}
   885  		}
   886  		if err = efHistoryComp.Compress(); err != nil {
   887  			return HistoryFiles{}, fmt.Errorf("compress %s ef history: %w", h.filenameBase, err)
   888  		}
   889  		efHistoryComp.Close()
   890  		efHistoryComp = nil
   891  		ps.Delete(p)
   892  	}
   893  
   894  	var err error
   895  	if efHistoryDecomp, err = compress.NewDecompressor(efHistoryPath); err != nil {
   896  		return HistoryFiles{}, fmt.Errorf("open %s ef history decompressor: %w", h.filenameBase, err)
   897  	}
   898  	efHistoryIdxFileName := fmt.Sprintf("%s.%d-%d.efi", h.filenameBase, step, step+1)
   899  	efHistoryIdxPath := filepath.Join(h.dir, efHistoryIdxFileName)
   900  	p := ps.AddNew(efHistoryIdxFileName, uint64(len(keys)*2))
   901  	defer ps.Delete(p)
   902  	if efHistoryIdx, err = buildIndexThenOpen(ctx, efHistoryDecomp, efHistoryIdxPath, h.tmpdir, len(keys), false /* values */, p, h.logger, h.noFsync); err != nil {
   903  		return HistoryFiles{}, fmt.Errorf("build %s ef history idx: %w", h.filenameBase, err)
   904  	}
   905  	if rs, err = recsplit.NewRecSplit(recsplit.RecSplitArgs{
   906  		KeyCount:   collation.historyCount,
   907  		Enums:      false,
   908  		BucketSize: 2000,
   909  		LeafSize:   8,
   910  		TmpDir:     h.tmpdir,
   911  		IndexFile:  historyIdxPath,
   912  	}, h.logger); err != nil {
   913  		return HistoryFiles{}, fmt.Errorf("create recsplit: %w", err)
   914  	}
   915  	rs.LogLvl(log.LvlTrace)
   916  	if h.noFsync {
   917  		rs.DisableFsync()
   918  	}
   919  	var historyKey []byte
   920  	var txKey [8]byte
   921  	var valOffset uint64
   922  	g := historyDecomp.MakeGetter()
   923  	for {
   924  		g.Reset(0)
   925  		valOffset = 0
   926  		for _, key := range keys {
   927  			bitmap := collation.indexBitmaps[key]
   928  			it := bitmap.Iterator()
   929  			for it.HasNext() {
   930  				txNum := it.Next()
   931  				binary.BigEndian.PutUint64(txKey[:], txNum)
   932  				historyKey = append(append(historyKey[:0], txKey[:]...), key...)
   933  				if err = rs.AddKey(historyKey, valOffset); err != nil {
   934  					return HistoryFiles{}, fmt.Errorf("add %s history idx [%x]: %w", h.filenameBase, historyKey, err)
   935  				}
   936  				valOffset, _ = g.Skip()
   937  			}
   938  		}
   939  		if err = rs.Build(ctx); err != nil {
   940  			if rs.Collision() {
   941  				log.Info("Building recsplit. Collision happened. It's ok. Restarting...")
   942  				rs.ResetNextSalt()
   943  			} else {
   944  				return HistoryFiles{}, fmt.Errorf("build idx: %w", err)
   945  			}
   946  		} else {
   947  			break
   948  		}
   949  	}
   950  	rs.Close()
   951  	rs = nil
   952  	if historyIdx, err = recsplit.OpenIndex(historyIdxPath); err != nil {
   953  		return HistoryFiles{}, fmt.Errorf("open idx: %w", err)
   954  	}
   955  	closeComp = false
   956  	return HistoryFiles{
   957  		historyDecomp:   historyDecomp,
   958  		historyIdx:      historyIdx,
   959  		efHistoryDecomp: efHistoryDecomp,
   960  		efHistoryIdx:    efHistoryIdx,
   961  	}, nil
   962  }
   963  
   964  func (h *History) integrateFiles(sf HistoryFiles, txNumFrom, txNumTo uint64) {
   965  	h.InvertedIndex.integrateFiles(InvertedFiles{
   966  		decomp: sf.efHistoryDecomp,
   967  		index:  sf.efHistoryIdx,
   968  	}, txNumFrom, txNumTo)
   969  
   970  	fi := newFilesItem(txNumFrom, txNumTo, h.aggregationStep)
   971  	fi.decompressor = sf.historyDecomp
   972  	fi.index = sf.historyIdx
   973  	h.files.Set(fi)
   974  
   975  	h.reCalcRoFiles()
   976  }
   977  
   978  func (h *History) warmup(ctx context.Context, txFrom, limit uint64, tx kv.Tx) error {
   979  	historyKeysCursor, err := tx.CursorDupSort(h.indexKeysTable)
   980  	if err != nil {
   981  		return fmt.Errorf("create %s history cursor: %w", h.filenameBase, err)
   982  	}
   983  	defer historyKeysCursor.Close()
   984  	var txKey [8]byte
   985  	binary.BigEndian.PutUint64(txKey[:], txFrom)
   986  	valsC, err := tx.Cursor(h.historyValsTable)
   987  	if err != nil {
   988  		return err
   989  	}
   990  	defer valsC.Close()
   991  	k, v, err := historyKeysCursor.Seek(txKey[:])
   992  	if err != nil {
   993  		return err
   994  	}
   995  	if k == nil {
   996  		return nil
   997  	}
   998  	txFrom = binary.BigEndian.Uint64(k)
   999  	txTo := txFrom + h.aggregationStep
  1000  	if limit != math.MaxUint64 && limit != 0 {
  1001  		txTo = txFrom + limit
  1002  	}
  1003  	keyBuf := make([]byte, 256)
  1004  	for ; err == nil && k != nil; k, v, err = historyKeysCursor.Next() {
  1005  		if err != nil {
  1006  			return err
  1007  		}
  1008  		txNum := binary.BigEndian.Uint64(k)
  1009  		if txNum >= txTo {
  1010  			break
  1011  		}
  1012  		copy(keyBuf, v)
  1013  		binary.BigEndian.PutUint64(keyBuf[len(v):], txNum)
  1014  		_, _, _ = valsC.Seek(keyBuf)
  1015  
  1016  		select {
  1017  		case <-ctx.Done():
  1018  			return ctx.Err()
  1019  		default:
  1020  		}
  1021  	}
  1022  	if err != nil {
  1023  		return fmt.Errorf("iterate over %s history keys: %w", h.filenameBase, err)
  1024  	}
  1025  
  1026  	return nil
  1027  }
  1028  
  1029  func (h *History) isEmpty(tx kv.Tx) (bool, error) {
  1030  	if h.largeValues {
  1031  		k, err := kv.FirstKey(tx, h.historyValsTable)
  1032  		if err != nil {
  1033  			return false, err
  1034  		}
  1035  		k2, err := kv.FirstKey(tx, h.indexKeysTable)
  1036  		if err != nil {
  1037  			return false, err
  1038  		}
  1039  		return k == nil && k2 == nil, nil
  1040  	}
  1041  	k, err := kv.FirstKey(tx, h.historyValsTable)
  1042  	if err != nil {
  1043  		return false, err
  1044  	}
  1045  	k2, err := kv.FirstKey(tx, h.indexKeysTable)
  1046  	if err != nil {
  1047  		return false, err
  1048  	}
  1049  	return k == nil && k2 == nil, nil
  1050  }
  1051  
  1052  func (h *History) prune(ctx context.Context, txFrom, txTo, limit uint64, logEvery *time.Ticker) error {
  1053  	historyKeysCursorForDeletes, err := h.tx.RwCursorDupSort(h.indexKeysTable)
  1054  	if err != nil {
  1055  		return fmt.Errorf("create %s history cursor: %w", h.filenameBase, err)
  1056  	}
  1057  	defer historyKeysCursorForDeletes.Close()
  1058  	historyKeysCursor, err := h.tx.RwCursorDupSort(h.indexKeysTable)
  1059  	if err != nil {
  1060  		return fmt.Errorf("create %s history cursor: %w", h.filenameBase, err)
  1061  	}
  1062  	defer historyKeysCursor.Close()
  1063  	var txKey [8]byte
  1064  	binary.BigEndian.PutUint64(txKey[:], txFrom)
  1065  	var k, v []byte
  1066  	var valsC kv.RwCursor
  1067  	var valsCDup kv.RwCursorDupSort
  1068  	if h.largeValues {
  1069  		valsC, err = h.tx.RwCursor(h.historyValsTable)
  1070  		if err != nil {
  1071  			return err
  1072  		}
  1073  		defer valsC.Close()
  1074  	} else {
  1075  		valsCDup, err = h.tx.RwCursorDupSort(h.historyValsTable)
  1076  		if err != nil {
  1077  			return err
  1078  		}
  1079  		defer valsCDup.Close()
  1080  	}
  1081  	for k, v, err = historyKeysCursor.Seek(txKey[:]); err == nil && k != nil; k, v, err = historyKeysCursor.Next() {
  1082  		txNum := binary.BigEndian.Uint64(k)
  1083  		if txNum >= txTo {
  1084  			break
  1085  		}
  1086  		if limit == 0 {
  1087  			return nil
  1088  		}
  1089  		limit--
  1090  
  1091  		if h.largeValues {
  1092  			seek := append(common.Copy(v), k...)
  1093  			if err := valsC.Delete(seek); err != nil {
  1094  				return err
  1095  			}
  1096  		} else {
  1097  			vv, err := valsCDup.SeekBothRange(v, k)
  1098  			if err != nil {
  1099  				return err
  1100  			}
  1101  			if binary.BigEndian.Uint64(vv) != txNum {
  1102  				continue
  1103  			}
  1104  			if err = valsCDup.DeleteCurrent(); err != nil {
  1105  				return err
  1106  			}
  1107  		}
  1108  
  1109  		// This DeleteCurrent needs to the last in the loop iteration, because it invalidates k and v
  1110  		if _, _, err = historyKeysCursorForDeletes.SeekBothExact(k, v); err != nil {
  1111  			return err
  1112  		}
  1113  		if err = historyKeysCursorForDeletes.DeleteCurrent(); err != nil {
  1114  			return err
  1115  		}
  1116  	}
  1117  	return nil
  1118  }
  1119  
  1120  type HistoryContext struct {
  1121  	h  *History
  1122  	ic *InvertedIndexContext
  1123  
  1124  	files   []ctxItem // have no garbage (canDelete=true, overlaps, etc...)
  1125  	getters []*compress.Getter
  1126  	readers []*recsplit.IndexReader
  1127  
  1128  	trace bool
  1129  }
  1130  
  1131  func (h *History) MakeContext() *HistoryContext {
  1132  
  1133  	var hc = HistoryContext{
  1134  		h:     h,
  1135  		ic:    h.InvertedIndex.MakeContext(),
  1136  		files: *h.roFiles.Load(),
  1137  
  1138  		trace: false,
  1139  	}
  1140  	for _, item := range hc.files {
  1141  		if !item.src.frozen {
  1142  			item.src.refcount.Add(1)
  1143  		}
  1144  	}
  1145  
  1146  	return &hc
  1147  }
  1148  
  1149  func (hc *HistoryContext) statelessGetter(i int) *compress.Getter {
  1150  	if hc.getters == nil {
  1151  		hc.getters = make([]*compress.Getter, len(hc.files))
  1152  	}
  1153  	r := hc.getters[i]
  1154  	if r == nil {
  1155  		r = hc.files[i].src.decompressor.MakeGetter()
  1156  		hc.getters[i] = r
  1157  	}
  1158  	return r
  1159  }
  1160  func (hc *HistoryContext) statelessIdxReader(i int) *recsplit.IndexReader {
  1161  	if hc.readers == nil {
  1162  		hc.readers = make([]*recsplit.IndexReader, len(hc.files))
  1163  	}
  1164  	r := hc.readers[i]
  1165  	if r == nil {
  1166  		r = hc.files[i].src.index.GetReaderFromPool()
  1167  		hc.readers[i] = r
  1168  	}
  1169  	return r
  1170  }
  1171  
  1172  func (hc *HistoryContext) Close() {
  1173  	hc.ic.Close()
  1174  	for _, item := range hc.files {
  1175  		if item.src.frozen {
  1176  			continue
  1177  		}
  1178  		refCnt := item.src.refcount.Add(-1)
  1179  		//if hc.h.filenameBase == "accounts" && item.src.canDelete.Load() {
  1180  		//	log.Warn("[history] HistoryContext.Close: check file to remove", "refCnt", refCnt, "name", item.src.decompressor.FileName())
  1181  		//}
  1182  		//GC: last reader responsible to remove useles files: close it and delete
  1183  		if refCnt == 0 && item.src.canDelete.Load() {
  1184  			item.src.closeFilesAndRemove()
  1185  		}
  1186  	}
  1187  	for _, r := range hc.readers {
  1188  		r.Close()
  1189  	}
  1190  
  1191  }
  1192  
  1193  func (hc *HistoryContext) getFile(from, to uint64) (it ctxItem, ok bool) {
  1194  	for _, item := range hc.files {
  1195  		if item.startTxNum == from && item.endTxNum == to {
  1196  			return item, true
  1197  		}
  1198  	}
  1199  	return it, false
  1200  }
  1201  
  1202  func (hc *HistoryContext) GetNoState(key []byte, txNum uint64) ([]byte, bool, error) {
  1203  	exactStep1, exactStep2, lastIndexedTxNum, foundExactShard1, foundExactShard2 := hc.h.localityIndex.lookupIdxFiles(hc.ic.loc, key, txNum)
  1204  
  1205  	//fmt.Printf("GetNoState [%x] %d\n", key, txNum)
  1206  	var foundTxNum uint64
  1207  	var foundEndTxNum uint64
  1208  	var foundStartTxNum uint64
  1209  	var found bool
  1210  	var findInFile = func(item ctxItem) bool {
  1211  		reader := hc.ic.statelessIdxReader(item.i)
  1212  		if reader.Empty() {
  1213  			return true
  1214  		}
  1215  		offset := reader.Lookup(key)
  1216  		g := hc.ic.statelessGetter(item.i)
  1217  		g.Reset(offset)
  1218  		k, _ := g.NextUncompressed()
  1219  
  1220  		if !bytes.Equal(k, key) {
  1221  			//if bytes.Equal(key, hex.MustDecodeString("009ba32869045058a3f05d6f3dd2abb967e338f6")) {
  1222  			//	fmt.Printf("not in this shard: %x, %d, %d-%d\n", k, txNum, item.startTxNum/hc.h.aggregationStep, item.endTxNum/hc.h.aggregationStep)
  1223  			//}
  1224  			return true
  1225  		}
  1226  		eliasVal, _ := g.NextUncompressed()
  1227  		ef, _ := eliasfano32.ReadEliasFano(eliasVal)
  1228  		n, ok := ef.Search(txNum)
  1229  		if hc.trace {
  1230  			n2, _ := ef.Search(n + 1)
  1231  			n3, _ := ef.Search(n - 1)
  1232  			fmt.Printf("hist: files: %s %d<-%d->%d->%d, %x\n", hc.h.filenameBase, n3, txNum, n, n2, key)
  1233  		}
  1234  		if ok {
  1235  			foundTxNum = n
  1236  			foundEndTxNum = item.endTxNum
  1237  			foundStartTxNum = item.startTxNum
  1238  			found = true
  1239  			return false
  1240  		}
  1241  		return true
  1242  	}
  1243  
  1244  	// -- LocaliyIndex opimization --
  1245  	// check up to 2 exact files
  1246  	if foundExactShard1 {
  1247  		from, to := exactStep1*hc.h.aggregationStep, (exactStep1+StepsInBiggestFile)*hc.h.aggregationStep
  1248  		item, ok := hc.ic.getFile(from, to)
  1249  		if ok {
  1250  			findInFile(item)
  1251  		}
  1252  		//for _, item := range hc.invIndexFiles {
  1253  		//	if item.startTxNum == from && item.endTxNum == to {
  1254  		//		findInFile(item)
  1255  		//	}
  1256  		//}
  1257  		//exactShard1, ok := hc.invIndexFiles.Get(ctxItem{startTxNum: exactStep1 * hc.h.aggregationStep, endTxNum: (exactStep1 + StepsInBiggestFile) * hc.h.aggregationStep})
  1258  		//if ok {
  1259  		//	findInFile(exactShard1)
  1260  		//}
  1261  	}
  1262  	if !found && foundExactShard2 {
  1263  		from, to := exactStep2*hc.h.aggregationStep, (exactStep2+StepsInBiggestFile)*hc.h.aggregationStep
  1264  		item, ok := hc.ic.getFile(from, to)
  1265  		if ok {
  1266  			findInFile(item)
  1267  		}
  1268  		//exactShard2, ok := hc.invIndexFiles.Get(ctxItem{startTxNum: exactStep2 * hc.h.aggregationStep, endTxNum: (exactStep2 + StepsInBiggestFile) * hc.h.aggregationStep})
  1269  		//if ok {
  1270  		//	findInFile(exactShard2)
  1271  		//}
  1272  	}
  1273  	// otherwise search in recent non-fully-merged files (they are out of LocalityIndex scope)
  1274  	// searchFrom - variable already set for this
  1275  	// if there is no LocaliyIndex available
  1276  	// -- LocaliyIndex opimization End --
  1277  
  1278  	if !found {
  1279  		for _, item := range hc.ic.files {
  1280  			if item.endTxNum <= lastIndexedTxNum {
  1281  				continue
  1282  			}
  1283  			if !findInFile(item) {
  1284  				break
  1285  			}
  1286  		}
  1287  		//hc.invIndexFiles.AscendGreaterOrEqual(ctxItem{startTxNum: lastIndexedTxNum, endTxNum: lastIndexedTxNum}, findInFile)
  1288  	}
  1289  
  1290  	if found {
  1291  		historyItem, ok := hc.getFile(foundStartTxNum, foundEndTxNum)
  1292  		if !ok {
  1293  			return nil, false, fmt.Errorf("hist file not found: key=%x, %s.%d-%d", key, hc.h.filenameBase, foundStartTxNum/hc.h.aggregationStep, foundEndTxNum/hc.h.aggregationStep)
  1294  		}
  1295  		var txKey [8]byte
  1296  		binary.BigEndian.PutUint64(txKey[:], foundTxNum)
  1297  		reader := hc.statelessIdxReader(historyItem.i)
  1298  		offset := reader.Lookup2(txKey[:], key)
  1299  		//fmt.Printf("offset = %d, txKey=[%x], key=[%x]\n", offset, txKey[:], key)
  1300  		g := hc.statelessGetter(historyItem.i)
  1301  		g.Reset(offset)
  1302  		if hc.h.compressVals {
  1303  			v, _ := g.Next(nil)
  1304  			return v, true, nil
  1305  		}
  1306  		v, _ := g.NextUncompressed()
  1307  		return v, true, nil
  1308  	}
  1309  	return nil, false, nil
  1310  }
  1311  
  1312  func (hs *HistoryStep) GetNoState(key []byte, txNum uint64) ([]byte, bool, uint64) {
  1313  	//fmt.Printf("GetNoState [%x] %d\n", key, txNum)
  1314  	if hs.indexFile.reader.Empty() {
  1315  		return nil, false, txNum
  1316  	}
  1317  	offset := hs.indexFile.reader.Lookup(key)
  1318  	g := hs.indexFile.getter
  1319  	g.Reset(offset)
  1320  	k, _ := g.NextUncompressed()
  1321  	if !bytes.Equal(k, key) {
  1322  		return nil, false, txNum
  1323  	}
  1324  	//fmt.Printf("Found key=%x\n", k)
  1325  	eliasVal, _ := g.NextUncompressed()
  1326  	ef, _ := eliasfano32.ReadEliasFano(eliasVal)
  1327  	n, ok := ef.Search(txNum)
  1328  	if !ok {
  1329  		return nil, false, ef.Max()
  1330  	}
  1331  	var txKey [8]byte
  1332  	binary.BigEndian.PutUint64(txKey[:], n)
  1333  	offset = hs.historyFile.reader.Lookup2(txKey[:], key)
  1334  	//fmt.Printf("offset = %d, txKey=[%x], key=[%x]\n", offset, txKey[:], key)
  1335  	g = hs.historyFile.getter
  1336  	g.Reset(offset)
  1337  	if hs.compressVals {
  1338  		v, _ := g.Next(nil)
  1339  		return v, true, txNum
  1340  	}
  1341  	v, _ := g.NextUncompressed()
  1342  	return v, true, txNum
  1343  }
  1344  
  1345  func (hs *HistoryStep) MaxTxNum(key []byte) (bool, uint64) {
  1346  	if hs.indexFile.reader.Empty() {
  1347  		return false, 0
  1348  	}
  1349  	offset := hs.indexFile.reader.Lookup(key)
  1350  	g := hs.indexFile.getter
  1351  	g.Reset(offset)
  1352  	k, _ := g.NextUncompressed()
  1353  	if !bytes.Equal(k, key) {
  1354  		return false, 0
  1355  	}
  1356  	//fmt.Printf("Found key=%x\n", k)
  1357  	eliasVal, _ := g.NextUncompressed()
  1358  	return true, eliasfano32.Max(eliasVal)
  1359  }
  1360  
  1361  // GetNoStateWithRecent searches history for a value of specified key before txNum
  1362  // second return value is true if the value is found in the history (even if it is nil)
  1363  func (hc *HistoryContext) GetNoStateWithRecent(key []byte, txNum uint64, roTx kv.Tx) ([]byte, bool, error) {
  1364  	v, ok, err := hc.GetNoState(key, txNum)
  1365  	if err != nil {
  1366  		return nil, ok, err
  1367  	}
  1368  	if ok {
  1369  		return v, true, nil
  1370  	}
  1371  
  1372  	// Value not found in history files, look in the recent history
  1373  	if roTx == nil {
  1374  		return nil, false, fmt.Errorf("roTx is nil")
  1375  	}
  1376  	return hc.getNoStateFromDB(key, txNum, roTx)
  1377  }
  1378  
  1379  func (hc *HistoryContext) getNoStateFromDB(key []byte, txNum uint64, tx kv.Tx) ([]byte, bool, error) {
  1380  	if hc.h.largeValues {
  1381  		c, err := tx.Cursor(hc.h.historyValsTable)
  1382  		if err != nil {
  1383  			return nil, false, err
  1384  		}
  1385  		defer c.Close()
  1386  		seek := make([]byte, len(key)+8)
  1387  		copy(seek, key)
  1388  		binary.BigEndian.PutUint64(seek[len(key):], txNum)
  1389  		kAndTxNum, val, err := c.Seek(seek)
  1390  		if err != nil {
  1391  			return nil, false, err
  1392  		}
  1393  		if kAndTxNum == nil || !bytes.Equal(kAndTxNum[:len(kAndTxNum)-8], key) {
  1394  			return nil, false, nil
  1395  		}
  1396  		// val == []byte{},m eans key was created in this txNum and doesn't exists before.
  1397  		return val, true, nil
  1398  	}
  1399  	c, err := tx.CursorDupSort(hc.h.historyValsTable)
  1400  	if err != nil {
  1401  		return nil, false, err
  1402  	}
  1403  	defer c.Close()
  1404  	seek := make([]byte, len(key)+8)
  1405  	copy(seek, key)
  1406  	binary.BigEndian.PutUint64(seek[len(key):], txNum)
  1407  	val, err := c.SeekBothRange(key, seek[len(key):])
  1408  	if err != nil {
  1409  		return nil, false, err
  1410  	}
  1411  	if val == nil {
  1412  		return nil, false, nil
  1413  	}
  1414  	// `val == []byte{}` means key was created in this txNum and doesn't exists before.
  1415  	return val[8:], true, nil
  1416  }
  1417  
  1418  func (hc *HistoryContext) WalkAsOf(startTxNum uint64, from, to []byte, roTx kv.Tx, limit int) iter.KV {
  1419  	hi := &StateAsOfIterF{
  1420  		from: from, to: to, limit: limit,
  1421  
  1422  		hc:           hc,
  1423  		compressVals: hc.h.compressVals,
  1424  		startTxNum:   startTxNum,
  1425  	}
  1426  	for _, item := range hc.ic.files {
  1427  		if item.endTxNum <= startTxNum {
  1428  			continue
  1429  		}
  1430  		// TODO: seek(from)
  1431  		g := item.src.decompressor.MakeGetter()
  1432  		g.Reset(0)
  1433  		if g.HasNext() {
  1434  			key, offset := g.NextUncompressed()
  1435  			heap.Push(&hi.h, &ReconItem{g: g, key: key, startTxNum: item.startTxNum, endTxNum: item.endTxNum, txNum: item.endTxNum, startOffset: offset, lastOffset: offset})
  1436  		}
  1437  	}
  1438  	binary.BigEndian.PutUint64(hi.startTxKey[:], startTxNum)
  1439  	if err := hi.advanceInFiles(); err != nil {
  1440  		panic(err)
  1441  	}
  1442  
  1443  	dbit := &StateAsOfIterDB{
  1444  		largeValues: hc.h.largeValues,
  1445  		roTx:        roTx,
  1446  		valsTable:   hc.h.historyValsTable,
  1447  		from:        from, to: to, limit: limit,
  1448  
  1449  		startTxNum: startTxNum,
  1450  	}
  1451  	binary.BigEndian.PutUint64(dbit.startTxKey[:], startTxNum)
  1452  	if err := dbit.advance(); err != nil {
  1453  		panic(err)
  1454  	}
  1455  	return iter.UnionKV(hi, dbit, limit)
  1456  }
  1457  
  1458  // StateAsOfIter - returns state range at given time in history
  1459  type StateAsOfIterF struct {
  1460  	hc    *HistoryContext
  1461  	limit int
  1462  
  1463  	from, to []byte
  1464  	nextVal  []byte
  1465  	nextKey  []byte
  1466  
  1467  	h            ReconHeap
  1468  	startTxNum   uint64
  1469  	startTxKey   [8]byte
  1470  	txnKey       [8]byte
  1471  	compressVals bool
  1472  
  1473  	k, v, kBackup, vBackup []byte
  1474  }
  1475  
  1476  func (hi *StateAsOfIterF) Close() {
  1477  }
  1478  
  1479  func (hi *StateAsOfIterF) advanceInFiles() error {
  1480  	for hi.h.Len() > 0 {
  1481  		top := heap.Pop(&hi.h).(*ReconItem)
  1482  		key := top.key
  1483  		var idxVal []byte
  1484  		if hi.compressVals {
  1485  			idxVal, _ = top.g.Next(nil)
  1486  		} else {
  1487  			idxVal, _ = top.g.NextUncompressed()
  1488  		}
  1489  		if top.g.HasNext() {
  1490  			if hi.compressVals {
  1491  				top.key, _ = top.g.Next(nil)
  1492  			} else {
  1493  				top.key, _ = top.g.NextUncompressed()
  1494  			}
  1495  			if hi.to == nil || bytes.Compare(top.key, hi.to) < 0 {
  1496  				heap.Push(&hi.h, top)
  1497  			}
  1498  		}
  1499  
  1500  		if hi.from != nil && bytes.Compare(key, hi.from) < 0 { //TODO: replace by Seek()
  1501  			continue
  1502  		}
  1503  
  1504  		if bytes.Equal(key, hi.nextKey) {
  1505  			continue
  1506  		}
  1507  		ef, _ := eliasfano32.ReadEliasFano(idxVal)
  1508  		n, ok := ef.Search(hi.startTxNum)
  1509  		if !ok {
  1510  			continue
  1511  		}
  1512  
  1513  		hi.nextKey = key
  1514  		binary.BigEndian.PutUint64(hi.txnKey[:], n)
  1515  		historyItem, ok := hi.hc.getFile(top.startTxNum, top.endTxNum)
  1516  		if !ok {
  1517  			return fmt.Errorf("no %s file found for [%x]", hi.hc.h.filenameBase, hi.nextKey)
  1518  		}
  1519  		reader := hi.hc.statelessIdxReader(historyItem.i)
  1520  		offset := reader.Lookup2(hi.txnKey[:], hi.nextKey)
  1521  		g := hi.hc.statelessGetter(historyItem.i)
  1522  		g.Reset(offset)
  1523  		if hi.compressVals {
  1524  			hi.nextVal, _ = g.Next(nil)
  1525  		} else {
  1526  			hi.nextVal, _ = g.NextUncompressed()
  1527  		}
  1528  		return nil
  1529  	}
  1530  	hi.nextKey = nil
  1531  	return nil
  1532  }
  1533  
  1534  func (hi *StateAsOfIterF) HasNext() bool {
  1535  	return hi.limit != 0 && hi.nextKey != nil
  1536  }
  1537  
  1538  func (hi *StateAsOfIterF) Next() ([]byte, []byte, error) {
  1539  	hi.limit--
  1540  	hi.k, hi.v = append(hi.k[:0], hi.nextKey...), append(hi.v[:0], hi.nextVal...)
  1541  
  1542  	// Satisfy iter.Dual Invariant 2
  1543  	hi.k, hi.kBackup, hi.v, hi.vBackup = hi.kBackup, hi.k, hi.vBackup, hi.v
  1544  	if err := hi.advanceInFiles(); err != nil {
  1545  		return nil, nil, err
  1546  	}
  1547  	return hi.kBackup, hi.vBackup, nil
  1548  }
  1549  
  1550  // StateAsOfIterDB - returns state range at given time in history
  1551  type StateAsOfIterDB struct {
  1552  	largeValues bool
  1553  	roTx        kv.Tx
  1554  	valsC       kv.Cursor
  1555  	valsCDup    kv.CursorDupSort
  1556  	valsTable   string
  1557  
  1558  	from, to []byte
  1559  	limit    int
  1560  
  1561  	nextKey, nextVal []byte
  1562  
  1563  	startTxNum uint64
  1564  	startTxKey [8]byte
  1565  
  1566  	k, v, kBackup, vBackup []byte
  1567  	err                    error
  1568  }
  1569  
  1570  func (hi *StateAsOfIterDB) Close() {
  1571  	if hi.valsC != nil {
  1572  		hi.valsC.Close()
  1573  	}
  1574  }
  1575  
  1576  func (hi *StateAsOfIterDB) advance() (err error) {
  1577  	// not large:
  1578  	//   keys: txNum -> key1+key2
  1579  	//   vals: key1+key2 -> txNum + value (DupSort)
  1580  	// large:
  1581  	//   keys: txNum -> key1+key2
  1582  	//   vals: key1+key2+txNum -> value (not DupSort)
  1583  	if hi.largeValues {
  1584  		return hi.advanceLargeVals()
  1585  	}
  1586  	return hi.advanceSmallVals()
  1587  }
  1588  func (hi *StateAsOfIterDB) advanceLargeVals() error {
  1589  	var seek []byte
  1590  	var err error
  1591  	if hi.valsC == nil {
  1592  		if hi.valsC, err = hi.roTx.Cursor(hi.valsTable); err != nil {
  1593  			return err
  1594  		}
  1595  		firstKey, _, err := hi.valsC.Seek(hi.from)
  1596  		if err != nil {
  1597  			return err
  1598  		}
  1599  		if firstKey == nil {
  1600  			hi.nextKey = nil
  1601  			return nil
  1602  		}
  1603  		seek = append(common.Copy(firstKey[:len(firstKey)-8]), hi.startTxKey[:]...)
  1604  	} else {
  1605  		next, ok := kv.NextSubtree(hi.nextKey)
  1606  		if !ok {
  1607  			hi.nextKey = nil
  1608  			return nil
  1609  		}
  1610  
  1611  		seek = append(next, hi.startTxKey[:]...)
  1612  	}
  1613  	for k, v, err := hi.valsC.Seek(seek); k != nil; k, v, err = hi.valsC.Seek(seek) {
  1614  		if err != nil {
  1615  			return err
  1616  		}
  1617  		if hi.to != nil && bytes.Compare(k[:len(k)-8], hi.to) >= 0 {
  1618  			break
  1619  		}
  1620  		if !bytes.Equal(seek[:len(k)-8], k[:len(k)-8]) {
  1621  			copy(seek[:len(k)-8], k[:len(k)-8])
  1622  			continue
  1623  		}
  1624  		hi.nextKey = k[:len(k)-8]
  1625  		hi.nextVal = v
  1626  		return nil
  1627  	}
  1628  	hi.nextKey = nil
  1629  	return nil
  1630  }
  1631  func (hi *StateAsOfIterDB) advanceSmallVals() error {
  1632  	var seek []byte
  1633  	var err error
  1634  	if hi.valsCDup == nil {
  1635  		if hi.valsCDup, err = hi.roTx.CursorDupSort(hi.valsTable); err != nil {
  1636  			return err
  1637  		}
  1638  		seek = hi.from
  1639  	} else {
  1640  		next, ok := kv.NextSubtree(hi.nextKey)
  1641  		if !ok {
  1642  			hi.nextKey = nil
  1643  			return nil
  1644  		}
  1645  		seek = next
  1646  	}
  1647  	for k, _, err := hi.valsCDup.Seek(seek); k != nil; k, _, err = hi.valsCDup.NextNoDup() {
  1648  		if err != nil {
  1649  			return err
  1650  		}
  1651  		if hi.to != nil && bytes.Compare(k, hi.to) >= 0 {
  1652  			break
  1653  		}
  1654  		v, err := hi.valsCDup.SeekBothRange(k, hi.startTxKey[:])
  1655  		if err != nil {
  1656  			return err
  1657  		}
  1658  		if v == nil {
  1659  			continue
  1660  		}
  1661  		hi.nextKey = k
  1662  		hi.nextVal = v[8:]
  1663  		return nil
  1664  	}
  1665  	hi.nextKey = nil
  1666  	return nil
  1667  }
  1668  
  1669  func (hi *StateAsOfIterDB) HasNext() bool {
  1670  	if hi.err != nil {
  1671  		return true
  1672  	}
  1673  	return hi.limit != 0 && hi.nextKey != nil
  1674  }
  1675  
  1676  func (hi *StateAsOfIterDB) Next() ([]byte, []byte, error) {
  1677  	if hi.err != nil {
  1678  		return nil, nil, hi.err
  1679  	}
  1680  	hi.limit--
  1681  	hi.k, hi.v = hi.nextKey, hi.nextVal
  1682  
  1683  	// Satisfy iter.Dual Invariant 2
  1684  	hi.k, hi.kBackup, hi.v, hi.vBackup = hi.kBackup, hi.k, hi.vBackup, hi.v
  1685  	if err := hi.advance(); err != nil {
  1686  		return nil, nil, err
  1687  	}
  1688  	return hi.kBackup, hi.vBackup, nil
  1689  }
  1690  
  1691  func (hc *HistoryContext) iterateChangedFrozen(fromTxNum, toTxNum int, asc order.By, limit int) (iter.KV, error) {
  1692  	if asc == false {
  1693  		panic("not supported yet")
  1694  	}
  1695  	if len(hc.ic.files) == 0 {
  1696  		return iter.EmptyKV, nil
  1697  	}
  1698  
  1699  	if fromTxNum >= 0 && hc.ic.files[len(hc.ic.files)-1].endTxNum <= uint64(fromTxNum) {
  1700  		return iter.EmptyKV, nil
  1701  	}
  1702  
  1703  	hi := &HistoryChangesIterFiles{
  1704  		hc:           hc,
  1705  		compressVals: hc.h.compressVals,
  1706  		startTxNum:   cmp.Max(0, uint64(fromTxNum)),
  1707  		endTxNum:     toTxNum,
  1708  		limit:        limit,
  1709  	}
  1710  	if fromTxNum >= 0 {
  1711  		binary.BigEndian.PutUint64(hi.startTxKey[:], uint64(fromTxNum))
  1712  	}
  1713  	for _, item := range hc.ic.files {
  1714  		if fromTxNum >= 0 && item.endTxNum <= uint64(fromTxNum) {
  1715  			continue
  1716  		}
  1717  		if toTxNum >= 0 && item.startTxNum >= uint64(toTxNum) {
  1718  			break
  1719  		}
  1720  		g := item.src.decompressor.MakeGetter()
  1721  		g.Reset(0)
  1722  		if g.HasNext() {
  1723  			key, offset := g.NextUncompressed()
  1724  			heap.Push(&hi.h, &ReconItem{g: g, key: key, startTxNum: item.startTxNum, endTxNum: item.endTxNum, txNum: item.endTxNum, startOffset: offset, lastOffset: offset})
  1725  		}
  1726  	}
  1727  	if err := hi.advance(); err != nil {
  1728  		return nil, err
  1729  	}
  1730  	return hi, nil
  1731  }
  1732  
  1733  func (hc *HistoryContext) iterateChangedRecent(fromTxNum, toTxNum int, asc order.By, limit int, roTx kv.Tx) (iter.KV, error) {
  1734  	if asc == order.Desc {
  1735  		panic("not supported yet")
  1736  	}
  1737  	rangeIsInFiles := toTxNum >= 0 && len(hc.ic.files) > 0 && hc.ic.files[len(hc.ic.files)-1].endTxNum >= uint64(toTxNum)
  1738  	if rangeIsInFiles {
  1739  		return iter.EmptyKV, nil
  1740  	}
  1741  	dbi := &HistoryChangesIterDB{
  1742  		endTxNum:    toTxNum,
  1743  		roTx:        roTx,
  1744  		largeValues: hc.h.largeValues,
  1745  		valsTable:   hc.h.historyValsTable,
  1746  		limit:       limit,
  1747  	}
  1748  	if fromTxNum >= 0 {
  1749  		binary.BigEndian.PutUint64(dbi.startTxKey[:], uint64(fromTxNum))
  1750  	}
  1751  	if err := dbi.advance(); err != nil {
  1752  		return nil, err
  1753  	}
  1754  	return dbi, nil
  1755  }
  1756  
  1757  func (hc *HistoryContext) HistoryRange(fromTxNum, toTxNum int, asc order.By, limit int, roTx kv.Tx) (iter.KV, error) {
  1758  	if asc == order.Desc {
  1759  		panic("not supported yet")
  1760  	}
  1761  	itOnFiles, err := hc.iterateChangedFrozen(fromTxNum, toTxNum, asc, limit)
  1762  	if err != nil {
  1763  		return nil, err
  1764  	}
  1765  	itOnDB, err := hc.iterateChangedRecent(fromTxNum, toTxNum, asc, limit, roTx)
  1766  	if err != nil {
  1767  		return nil, err
  1768  	}
  1769  
  1770  	return iter.UnionKV(itOnFiles, itOnDB, limit), nil
  1771  }
  1772  
  1773  type HistoryChangesIterFiles struct {
  1774  	hc           *HistoryContext
  1775  	nextVal      []byte
  1776  	nextKey      []byte
  1777  	h            ReconHeap
  1778  	startTxNum   uint64
  1779  	endTxNum     int
  1780  	startTxKey   [8]byte
  1781  	txnKey       [8]byte
  1782  	compressVals bool
  1783  
  1784  	k, v, kBackup, vBackup []byte
  1785  	err                    error
  1786  	limit                  int
  1787  }
  1788  
  1789  func (hi *HistoryChangesIterFiles) Close() {
  1790  }
  1791  
  1792  func (hi *HistoryChangesIterFiles) advance() error {
  1793  	for hi.h.Len() > 0 {
  1794  		top := heap.Pop(&hi.h).(*ReconItem)
  1795  		key := top.key
  1796  		var idxVal []byte
  1797  		if hi.compressVals {
  1798  			idxVal, _ = top.g.Next(nil)
  1799  		} else {
  1800  			idxVal, _ = top.g.NextUncompressed()
  1801  		}
  1802  		if top.g.HasNext() {
  1803  			if hi.compressVals {
  1804  				top.key, _ = top.g.Next(nil)
  1805  			} else {
  1806  				top.key, _ = top.g.NextUncompressed()
  1807  			}
  1808  			heap.Push(&hi.h, top)
  1809  		}
  1810  
  1811  		if bytes.Equal(key, hi.nextKey) {
  1812  			continue
  1813  		}
  1814  		ef, _ := eliasfano32.ReadEliasFano(idxVal)
  1815  		n, ok := ef.Search(hi.startTxNum) //TODO: if startTxNum==0, can do ef.Get(0)
  1816  		if !ok {
  1817  			continue
  1818  		}
  1819  		if int(n) >= hi.endTxNum {
  1820  			continue
  1821  		}
  1822  
  1823  		hi.nextKey = key
  1824  		binary.BigEndian.PutUint64(hi.txnKey[:], n)
  1825  		historyItem, ok := hi.hc.getFile(top.startTxNum, top.endTxNum)
  1826  		if !ok {
  1827  			return fmt.Errorf("HistoryChangesIterFiles: no %s file found for [%x]", hi.hc.h.filenameBase, hi.nextKey)
  1828  		}
  1829  		reader := hi.hc.statelessIdxReader(historyItem.i)
  1830  		offset := reader.Lookup2(hi.txnKey[:], hi.nextKey)
  1831  		g := hi.hc.statelessGetter(historyItem.i)
  1832  		g.Reset(offset)
  1833  		if hi.compressVals {
  1834  			hi.nextVal, _ = g.Next(nil)
  1835  		} else {
  1836  			hi.nextVal, _ = g.NextUncompressed()
  1837  		}
  1838  		return nil
  1839  	}
  1840  	hi.nextKey = nil
  1841  	return nil
  1842  }
  1843  
  1844  func (hi *HistoryChangesIterFiles) HasNext() bool {
  1845  	if hi.err != nil { // always true, then .Next() call will return this error
  1846  		return true
  1847  	}
  1848  	if hi.limit == 0 { // limit reached
  1849  		return false
  1850  	}
  1851  	if hi.nextKey == nil { // EndOfTable
  1852  		return false
  1853  	}
  1854  	return true
  1855  	//if hi.toPrefix == nil { // s.nextK == nil check is above
  1856  	//	return true
  1857  	//}
  1858  }
  1859  
  1860  func (hi *HistoryChangesIterFiles) Next() ([]byte, []byte, error) {
  1861  	if hi.err != nil {
  1862  		return nil, nil, hi.err
  1863  	}
  1864  	hi.limit--
  1865  	hi.k, hi.v = append(hi.k[:0], hi.nextKey...), append(hi.v[:0], hi.nextVal...)
  1866  
  1867  	// Satisfy iter.Dual Invariant 2
  1868  	hi.k, hi.kBackup, hi.v, hi.vBackup = hi.kBackup, hi.k, hi.vBackup, hi.v
  1869  	if err := hi.advance(); err != nil {
  1870  		return nil, nil, err
  1871  	}
  1872  	return hi.kBackup, hi.vBackup, nil
  1873  }
  1874  
  1875  type HistoryChangesIterDB struct {
  1876  	largeValues     bool
  1877  	roTx            kv.Tx
  1878  	valsC           kv.Cursor
  1879  	valsCDup        kv.CursorDupSort
  1880  	valsTable       string
  1881  	limit, endTxNum int
  1882  	startTxKey      [8]byte
  1883  
  1884  	nextKey, nextVal []byte
  1885  	k, v             []byte
  1886  	err              error
  1887  }
  1888  
  1889  func (hi *HistoryChangesIterDB) Close() {
  1890  	if hi.valsC != nil {
  1891  		hi.valsC.Close()
  1892  	}
  1893  	if hi.valsCDup != nil {
  1894  		hi.valsCDup.Close()
  1895  	}
  1896  }
  1897  func (hi *HistoryChangesIterDB) advance() (err error) {
  1898  	// not large:
  1899  	//   keys: txNum -> key1+key2
  1900  	//   vals: key1+key2 -> txNum + value (DupSort)
  1901  	// large:
  1902  	//   keys: txNum -> key1+key2
  1903  	//   vals: key1+key2+txNum -> value (not DupSort)
  1904  	if hi.largeValues {
  1905  		return hi.advanceLargeVals()
  1906  	}
  1907  	return hi.advanceSmallVals()
  1908  }
  1909  func (hi *HistoryChangesIterDB) advanceLargeVals() error {
  1910  	var seek []byte
  1911  	var err error
  1912  	if hi.valsC == nil {
  1913  		if hi.valsC, err = hi.roTx.Cursor(hi.valsTable); err != nil {
  1914  			return err
  1915  		}
  1916  		firstKey, _, err := hi.valsC.First()
  1917  		if err != nil {
  1918  			return err
  1919  		}
  1920  		if firstKey == nil {
  1921  			hi.nextKey = nil
  1922  			return nil
  1923  		}
  1924  		seek = append(common.Copy(firstKey[:len(firstKey)-8]), hi.startTxKey[:]...)
  1925  	} else {
  1926  		next, ok := kv.NextSubtree(hi.nextKey)
  1927  		if !ok {
  1928  			hi.nextKey = nil
  1929  			return nil
  1930  		}
  1931  
  1932  		seek = append(next, hi.startTxKey[:]...)
  1933  	}
  1934  	for k, v, err := hi.valsC.Seek(seek); k != nil; k, v, err = hi.valsC.Seek(seek) {
  1935  		if err != nil {
  1936  			return err
  1937  		}
  1938  		if hi.endTxNum >= 0 && int(binary.BigEndian.Uint64(k[len(k)-8:])) >= hi.endTxNum {
  1939  			next, ok := kv.NextSubtree(k[:len(k)-8])
  1940  			if !ok {
  1941  				hi.nextKey = nil
  1942  				return nil
  1943  			}
  1944  			seek = append(next, hi.startTxKey[:]...)
  1945  			continue
  1946  		}
  1947  		if !bytes.Equal(seek[:len(k)-8], k[:len(k)-8]) {
  1948  			copy(seek[:len(k)-8], k[:len(k)-8])
  1949  			continue
  1950  		}
  1951  		hi.nextKey = k[:len(k)-8]
  1952  		hi.nextVal = v
  1953  		return nil
  1954  	}
  1955  	hi.nextKey = nil
  1956  	return nil
  1957  }
  1958  func (hi *HistoryChangesIterDB) advanceSmallVals() (err error) {
  1959  	var k []byte
  1960  	if hi.valsCDup == nil {
  1961  		if hi.valsCDup, err = hi.roTx.CursorDupSort(hi.valsTable); err != nil {
  1962  			return err
  1963  		}
  1964  
  1965  		if k, _, err = hi.valsCDup.First(); err != nil {
  1966  			return err
  1967  		}
  1968  	} else {
  1969  		if k, _, err = hi.valsCDup.NextNoDup(); err != nil {
  1970  			return err
  1971  		}
  1972  	}
  1973  	for ; k != nil; k, _, err = hi.valsCDup.NextNoDup() {
  1974  		if err != nil {
  1975  			return err
  1976  		}
  1977  		v, err := hi.valsCDup.SeekBothRange(k, hi.startTxKey[:])
  1978  		if err != nil {
  1979  			return err
  1980  		}
  1981  		if v == nil {
  1982  			continue
  1983  		}
  1984  		foundTxNumVal := v[:8]
  1985  		if hi.endTxNum >= 0 && int(binary.BigEndian.Uint64(foundTxNumVal)) >= hi.endTxNum {
  1986  			continue
  1987  		}
  1988  		hi.nextKey = k
  1989  		hi.nextVal = v[8:]
  1990  		return nil
  1991  	}
  1992  	hi.nextKey = nil
  1993  	return nil
  1994  }
  1995  
  1996  func (hi *HistoryChangesIterDB) HasNext() bool {
  1997  	if hi.err != nil { // always true, then .Next() call will return this error
  1998  		return true
  1999  	}
  2000  	if hi.limit == 0 { // limit reached
  2001  		return false
  2002  	}
  2003  	if hi.nextKey == nil { // EndOfTable
  2004  		return false
  2005  	}
  2006  	return true
  2007  }
  2008  
  2009  func (hi *HistoryChangesIterDB) Next() ([]byte, []byte, error) {
  2010  	if hi.err != nil {
  2011  		return nil, nil, hi.err
  2012  	}
  2013  	hi.limit--
  2014  	hi.k, hi.v = hi.nextKey, hi.nextVal
  2015  	if err := hi.advance(); err != nil {
  2016  		return nil, nil, err
  2017  	}
  2018  	return hi.k, hi.v, nil
  2019  }
  2020  
  2021  func (h *History) DisableReadAhead() {
  2022  	h.InvertedIndex.DisableReadAhead()
  2023  	h.files.Walk(func(items []*filesItem) bool {
  2024  		for _, item := range items {
  2025  			item.decompressor.DisableReadAhead()
  2026  			if item.index != nil {
  2027  				item.index.DisableReadAhead()
  2028  			}
  2029  		}
  2030  		return true
  2031  	})
  2032  }
  2033  
  2034  func (h *History) EnableReadAhead() *History {
  2035  	h.InvertedIndex.EnableReadAhead()
  2036  	h.files.Walk(func(items []*filesItem) bool {
  2037  		for _, item := range items {
  2038  			item.decompressor.EnableReadAhead()
  2039  			if item.index != nil {
  2040  				item.index.EnableReadAhead()
  2041  			}
  2042  		}
  2043  		return true
  2044  	})
  2045  	return h
  2046  }
  2047  func (h *History) EnableMadvWillNeed() *History {
  2048  	h.InvertedIndex.EnableMadvWillNeed()
  2049  	h.files.Walk(func(items []*filesItem) bool {
  2050  		for _, item := range items {
  2051  			item.decompressor.EnableWillNeed()
  2052  			if item.index != nil {
  2053  				item.index.EnableWillNeed()
  2054  			}
  2055  		}
  2056  		return true
  2057  	})
  2058  	return h
  2059  }
  2060  func (h *History) EnableMadvNormalReadAhead() *History {
  2061  	h.InvertedIndex.EnableMadvNormalReadAhead()
  2062  	h.files.Walk(func(items []*filesItem) bool {
  2063  		for _, item := range items {
  2064  			item.decompressor.EnableMadvNormal()
  2065  			if item.index != nil {
  2066  				item.index.EnableMadvNormal()
  2067  			}
  2068  		}
  2069  		return true
  2070  	})
  2071  	return h
  2072  }
  2073  
  2074  // HistoryStep used for incremental state reconsitution, it isolates only one snapshot interval
  2075  type HistoryStep struct {
  2076  	compressVals bool
  2077  	indexItem    *filesItem
  2078  	indexFile    ctxItem
  2079  	historyItem  *filesItem
  2080  	historyFile  ctxItem
  2081  }
  2082  
  2083  // MakeSteps [0, toTxNum)
  2084  func (h *History) MakeSteps(toTxNum uint64) []*HistoryStep {
  2085  	var steps []*HistoryStep
  2086  	h.InvertedIndex.files.Walk(func(items []*filesItem) bool {
  2087  		for _, item := range items {
  2088  			if item.index == nil || !item.frozen || item.startTxNum >= toTxNum {
  2089  				continue
  2090  			}
  2091  
  2092  			step := &HistoryStep{
  2093  				compressVals: h.compressVals,
  2094  				indexItem:    item,
  2095  				indexFile: ctxItem{
  2096  					startTxNum: item.startTxNum,
  2097  					endTxNum:   item.endTxNum,
  2098  					getter:     item.decompressor.MakeGetter(),
  2099  					reader:     recsplit.NewIndexReader(item.index),
  2100  				},
  2101  			}
  2102  			steps = append(steps, step)
  2103  		}
  2104  		return true
  2105  	})
  2106  	i := 0
  2107  	h.files.Walk(func(items []*filesItem) bool {
  2108  		for _, item := range items {
  2109  			if item.index == nil || !item.frozen || item.startTxNum >= toTxNum {
  2110  				continue
  2111  			}
  2112  			steps[i].historyItem = item
  2113  			steps[i].historyFile = ctxItem{
  2114  				startTxNum: item.startTxNum,
  2115  				endTxNum:   item.endTxNum,
  2116  				getter:     item.decompressor.MakeGetter(),
  2117  				reader:     recsplit.NewIndexReader(item.index),
  2118  			}
  2119  			i++
  2120  		}
  2121  		return true
  2122  	})
  2123  	return steps
  2124  }
  2125  
  2126  func (hs *HistoryStep) Clone() *HistoryStep {
  2127  	return &HistoryStep{
  2128  		compressVals: hs.compressVals,
  2129  		indexItem:    hs.indexItem,
  2130  		indexFile: ctxItem{
  2131  			startTxNum: hs.indexFile.startTxNum,
  2132  			endTxNum:   hs.indexFile.endTxNum,
  2133  			getter:     hs.indexItem.decompressor.MakeGetter(),
  2134  			reader:     recsplit.NewIndexReader(hs.indexItem.index),
  2135  		},
  2136  		historyItem: hs.historyItem,
  2137  		historyFile: ctxItem{
  2138  			startTxNum: hs.historyFile.startTxNum,
  2139  			endTxNum:   hs.historyFile.endTxNum,
  2140  			getter:     hs.historyItem.decompressor.MakeGetter(),
  2141  			reader:     recsplit.NewIndexReader(hs.historyItem.index),
  2142  		},
  2143  	}
  2144  }
  2145  
  2146  func (hc *HistoryContext) idxRangeRecent(key []byte, startTxNum, endTxNum int, asc order.By, limit int, roTx kv.Tx) (iter.U64, error) {
  2147  	var dbIt iter.U64
  2148  	if hc.h.largeValues {
  2149  		if asc {
  2150  			from := make([]byte, len(key)+8)
  2151  			copy(from, key)
  2152  			var fromTxNum uint64
  2153  			if startTxNum >= 0 {
  2154  				fromTxNum = uint64(startTxNum)
  2155  			}
  2156  			binary.BigEndian.PutUint64(from[len(key):], fromTxNum)
  2157  
  2158  			to := common.Copy(from)
  2159  			toTxNum := uint64(math.MaxUint64)
  2160  			if endTxNum >= 0 {
  2161  				toTxNum = uint64(endTxNum)
  2162  			}
  2163  			binary.BigEndian.PutUint64(to[len(key):], toTxNum)
  2164  
  2165  			it, err := roTx.RangeAscend(hc.h.historyValsTable, from, to, limit)
  2166  			if err != nil {
  2167  				return nil, err
  2168  			}
  2169  			dbIt = iter.TransformKV2U64(it, func(k, _ []byte) (uint64, error) {
  2170  				return binary.BigEndian.Uint64(k[len(k)-8:]), nil
  2171  			})
  2172  		} else {
  2173  			panic("implement me")
  2174  		}
  2175  	} else {
  2176  		if asc {
  2177  			var from, to []byte
  2178  			if startTxNum >= 0 {
  2179  				from = make([]byte, 8)
  2180  				binary.BigEndian.PutUint64(from, uint64(startTxNum))
  2181  			}
  2182  			if endTxNum >= 0 {
  2183  				to = make([]byte, 8)
  2184  				binary.BigEndian.PutUint64(to, uint64(endTxNum))
  2185  			}
  2186  			it, err := roTx.RangeDupSort(hc.h.historyValsTable, key, from, to, asc, limit)
  2187  			if err != nil {
  2188  				return nil, err
  2189  			}
  2190  			dbIt = iter.TransformKV2U64(it, func(_, v []byte) (uint64, error) {
  2191  				return binary.BigEndian.Uint64(v), nil
  2192  			})
  2193  		} else {
  2194  			panic("implement me")
  2195  		}
  2196  	}
  2197  
  2198  	return dbIt, nil
  2199  }
  2200  func (hc *HistoryContext) IdxRange(key []byte, startTxNum, endTxNum int, asc order.By, limit int, roTx kv.Tx) (iter.U64, error) {
  2201  	frozenIt, err := hc.ic.iterateRangeFrozen(key, startTxNum, endTxNum, asc, limit)
  2202  	if err != nil {
  2203  		return nil, err
  2204  	}
  2205  	recentIt, err := hc.idxRangeRecent(key, startTxNum, endTxNum, asc, limit, roTx)
  2206  	if err != nil {
  2207  		return nil, err
  2208  	}
  2209  	return iter.Union[uint64](frozenIt, recentIt, asc, limit), nil
  2210  }