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 }