From 5536127d5d9b3f367b3eb30e37da7e7c14ea574f Mon Sep 17 00:00:00 2001 From: Alexey Sharov Date: Fri, 27 Feb 2026 11:07:34 +0700 Subject: [PATCH 1/9] save --- db/seg/decompress.go | 10 ++++++++++ db/seg/seg_auto_rw.go | 6 ++++++ 2 files changed, 16 insertions(+) diff --git a/db/seg/decompress.go b/db/seg/decompress.go index 578daec9619..ce1dc902ce2 100644 --- a/db/seg/decompress.go +++ b/db/seg/decompress.go @@ -513,6 +513,16 @@ func (d *Decompressor) DictWords() int { return d.dictWords } func (d *Decompressor) DictLens() int { return d.dictLens } func (d *Decompressor) CompressedPageValuesCount() int { return int(d.compPageValuesCount) } func (d *Decompressor) CompressionFormatVersion() uint8 { return d.version } +func (d *Decompressor) FileCompression() FileCompression { + c := CompressNone + if d.featureFlagBitmask.Has(KeyCompressionEnabled) { + c |= CompressKeys + } + if d.featureFlagBitmask.Has(ValCompressionEnabled) { + c |= CompressVals + } + return c +} func (d *Decompressor) Size() int64 { return d.size diff --git a/db/seg/seg_auto_rw.go b/db/seg/seg_auto_rw.go index e087473e05d..24a3185514b 100644 --- a/db/seg/seg_auto_rw.go +++ b/db/seg/seg_auto_rw.go @@ -104,6 +104,12 @@ type Writer struct { } func NewWriter(kv *Compressor, compress FileCompression) *Writer { + if compress.Has(CompressKeys) { + kv.featureFlagBitmask.Set(KeyCompressionEnabled) + } + if compress.Has(CompressVals) { + kv.featureFlagBitmask.Set(ValCompressionEnabled) + } return &Writer{kv, false, compress} } From 69074962adaa62f32855577a2323274b84a02887 Mon Sep 17 00:00:00 2001 From: Alexey Sharov Date: Fri, 27 Feb 2026 11:32:18 +0700 Subject: [PATCH 2/9] save --- db/seg/compress.go | 4 ++-- db/seg/decompress.go | 11 +++++++++-- db/seg/seg_interface.go | 4 ++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/db/seg/compress.go b/db/seg/compress.go index e3ddbdeec0d..ce00423dbcc 100644 --- a/db/seg/compress.go +++ b/db/seg/compress.go @@ -193,7 +193,7 @@ func NewCompressor(ctx context.Context, logPrefix, outputFile, tmpDir string, cf lvl: lvl, wg: wg, logger: logger, - version: FileCompressionFormatV1, + version: FileCompressionFormatV2, } if cfg.ValuesOnCompressedPage > 0 { @@ -343,7 +343,7 @@ func (c *Compressor) Compress() error { defer dir.RemoveFile(tmpFileName) defer cf.Close() - if c.version == FileCompressionFormatV1 { + if c.version == FileCompressionFormatV1 || c.version == FileCompressionFormatV2 { if _, err := cf.Write([]byte{c.version, byte(c.featureFlagBitmask)}); err != nil { return err } diff --git a/db/seg/decompress.go b/db/seg/decompress.go index ce1dc902ce2..f6c62dad3f2 100644 --- a/db/seg/decompress.go +++ b/db/seg/decompress.go @@ -258,10 +258,12 @@ func NewDecompressorWithMetadata(compressedFilePath string, hasMetadata bool) (* d.version = d.data[0] - if d.version == FileCompressionFormatV1 { + if d.version == FileCompressionFormatV1 || d.version == FileCompressionFormatV2 { // 1st byte: version, // 2nd byte: defines how exactly the file is compressed - // 3rd byte (otional): exists if PageLevelCompressionEnabled flag is enabled, and defines number of values on compressed page + // 3rd byte (optional): exists if PageLevelCompressionEnabled flag is enabled, and defines number of values on compressed page + // Note: KeyCompressionEnabled / ValCompressionEnabled bits in the bitmask are only + // reliable for V2+; V1 files may have those bits unset even when keys/vals are compressed. d.featureFlagBitmask = FeatureFlagBitmask(d.data[1]) d.data = d.data[2:] } @@ -513,7 +515,12 @@ func (d *Decompressor) DictWords() int { return d.dictWords } func (d *Decompressor) DictLens() int { return d.dictLens } func (d *Decompressor) CompressedPageValuesCount() int { return int(d.compPageValuesCount) } func (d *Decompressor) CompressionFormatVersion() uint8 { return d.version } +// FileCompression returns the key/value compression flags stored in the file header. +// Only reliable for V2+ files; returns CompressNone for V0 and V1. func (d *Decompressor) FileCompression() FileCompression { + if d.version < FileCompressionFormatV2 { + return CompressNone + } c := CompressNone if d.featureFlagBitmask.Has(KeyCompressionEnabled) { c |= CompressKeys diff --git a/db/seg/seg_interface.go b/db/seg/seg_interface.go index 8a9cacb51d3..29b88d442e5 100644 --- a/db/seg/seg_interface.go +++ b/db/seg/seg_interface.go @@ -27,6 +27,10 @@ const ( const ( FileCompressionFormatV0 = uint8(0) FileCompressionFormatV1 = uint8(1) + // FileCompressionFormatV2 is like V1 but the featureFlagBitmask is fully + // populated, including KeyCompressionEnabled / ValCompressionEnabled bits. + // Files at V1 may have those bits unset even when keys/vals are compressed. + FileCompressionFormatV2 = uint8(2) ) type FeatureFlag uint8 From a3cc7aa8fff19282ef6d621e9f212df94f29bb02 Mon Sep 17 00:00:00 2001 From: Alexey Sharov Date: Fri, 27 Feb 2026 12:07:22 +0700 Subject: [PATCH 3/9] save --- db/migrations/migrations.go | 1 + db/migrations/seg_header_v2.go | 122 +++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 db/migrations/seg_header_v2.go diff --git a/db/migrations/migrations.go b/db/migrations/migrations.go index fb86baabe46..e03692b855a 100644 --- a/db/migrations/migrations.go +++ b/db/migrations/migrations.go @@ -53,6 +53,7 @@ var migrations = map[kv.Label][]Migration{ dbcfg.ChainDB: { dbSchemaVersion5, ResetStageTxnLookup, + SegHeaderV2, }, dbcfg.TxPoolDB: {}, dbcfg.SentryDB: {}, diff --git a/db/migrations/seg_header_v2.go b/db/migrations/seg_header_v2.go new file mode 100644 index 00000000000..c4b04494a48 --- /dev/null +++ b/db/migrations/seg_header_v2.go @@ -0,0 +1,122 @@ +// Copyright 2024 The Erigon Authors +// This file is part of Erigon. +// +// Erigon is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Erigon is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Erigon. If not, see . + +package migrations + +import ( + "context" + "os" + "path/filepath" + + "github.com/erigontech/erigon/common/log/v3" + "github.com/erigontech/erigon/db/datadir" + "github.com/erigontech/erigon/db/kv" + "github.com/erigontech/erigon/db/seg" +) + +// SegHeaderV2 upgrades all V1 .seg snapshot files to V2 by patching the +// two-byte file header in-place. V2 is identical to V1 except that the +// featureFlagBitmask byte now reliably encodes KeyCompressionEnabled / +// ValCompressionEnabled in addition to PageLevelCompressionEnabled. +// +// The migration detects the actual per-file compression by calling +// seg.DetectCompressType on a getter, then overwrites bytes [0:2] of the +// file before any other state is touched. The rest of the file is +// unchanged, so no re-compression is needed. +var SegHeaderV2 = Migration{ + Name: "seg_header_v2", + Up: func(db kv.RwDB, dirs datadir.Dirs, progress []byte, BeforeCommit Callback, logger log.Logger) error { + tx, err := db.BeginRw(context.Background()) + if err != nil { + return err + } + defer tx.Rollback() + + snapDirs := []string{ + dirs.Snap, + dirs.SnapDomain, + dirs.SnapHistory, + dirs.SnapIdx, + dirs.SnapAccessors, + dirs.SnapCaplin, + } + + for _, dir := range snapDirs { + if err := upgradeSegFilesInDir(dir, logger); err != nil { + return err + } + } + + if err := BeforeCommit(tx, nil, true); err != nil { + return err + } + return tx.Commit() + }, +} + +func upgradeSegFilesInDir(dir string, logger log.Logger) error { + return filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error { + if err != nil || d.IsDir() || filepath.Ext(path) != ".seg" { + return err + } + return upgradeSegHeaderV1toV2(path, logger) + }) +} + +// upgradeSegHeaderV1toV2 patches a single .seg file from V1 to V2 format. +// It is a no-op for V0 files and files that are already V2+. +func upgradeSegHeaderV1toV2(path string, logger log.Logger) error { + d, err := seg.NewDecompressor(path) + if err != nil { + return err + } + if d.CompressionFormatVersion() != seg.FileCompressionFormatV1 { + d.Close() + return nil + } + + // Probe actual key/value compression before closing the mmap. + pageCnt := d.CompressedPageValuesCount() + fc := seg.DetectCompressType(d.MakeGetter()) + d.Close() // must release mmap before writing + + var bitmask seg.FeatureFlagBitmask + if pageCnt > 0 { + bitmask.Set(seg.PageLevelCompressionEnabled) + } + if fc.Has(seg.CompressKeys) { + bitmask.Set(seg.KeyCompressionEnabled) + } + if fc.Has(seg.CompressVals) { + bitmask.Set(seg.ValCompressionEnabled) + } + + f, err := os.OpenFile(path, os.O_RDWR, 0) + if err != nil { + return err + } + defer f.Close() + + if _, err := f.WriteAt([]byte{seg.FileCompressionFormatV2, byte(bitmask)}, 0); err != nil { + return err + } + if err := f.Sync(); err != nil { + return err + } + + logger.Debug("[seg_header_v2] upgraded", "file", filepath.Base(path)) + return nil +} From c2ccf6c20175583f9b91ddc9333a71e68a8e02da Mon Sep 17 00:00:00 2001 From: Alexey Sharov Date: Fri, 27 Feb 2026 12:29:49 +0700 Subject: [PATCH 4/9] save --- db/migrations/seg_header_v2.go | 93 +++++++++++++++++++++++++++------- 1 file changed, 76 insertions(+), 17 deletions(-) diff --git a/db/migrations/seg_header_v2.go b/db/migrations/seg_header_v2.go index c4b04494a48..80f10aa5321 100644 --- a/db/migrations/seg_header_v2.go +++ b/db/migrations/seg_header_v2.go @@ -20,22 +20,22 @@ import ( "context" "os" "path/filepath" + "strings" "github.com/erigontech/erigon/common/log/v3" "github.com/erigontech/erigon/db/datadir" "github.com/erigontech/erigon/db/kv" "github.com/erigontech/erigon/db/seg" + "github.com/erigontech/erigon/db/state/statecfg" ) -// SegHeaderV2 upgrades all V1 .seg snapshot files to V2 by patching the -// two-byte file header in-place. V2 is identical to V1 except that the +// SegHeaderV2 upgrades all V1 .seg/.v/.ef snapshot files to V2 by patching +// the two-byte file header in-place. V2 is identical to V1 except that the // featureFlagBitmask byte now reliably encodes KeyCompressionEnabled / // ValCompressionEnabled in addition to PageLevelCompressionEnabled. // -// The migration detects the actual per-file compression by calling -// seg.DetectCompressType on a getter, then overwrites bytes [0:2] of the -// file before any other state is touched. The rest of the file is -// unchanged, so no re-compression is needed. +// The correct compression flags are looked up from statecfg.Schema so that +// no heuristic detection (DetectCompressType) is needed. var SegHeaderV2 = Migration{ Name: "seg_header_v2", Up: func(db kv.RwDB, dirs datadir.Dirs, progress []byte, BeforeCommit Callback, logger log.Logger) error { @@ -45,6 +45,8 @@ var SegHeaderV2 = Migration{ } defer tx.Rollback() + lookup := buildSegCompressionLookup() + snapDirs := []string{ dirs.Snap, dirs.SnapDomain, @@ -53,9 +55,8 @@ var SegHeaderV2 = Migration{ dirs.SnapAccessors, dirs.SnapCaplin, } - for _, dir := range snapDirs { - if err := upgradeSegFilesInDir(dir, logger); err != nil { + if err := upgradeSegFilesInDir(dir, lookup, logger); err != nil { return err } } @@ -67,18 +68,26 @@ var SegHeaderV2 = Migration{ }, } -func upgradeSegFilesInDir(dir string, logger log.Logger) error { +// segDataExts are the file extensions written by the seg Compressor that +// carry the version/featureFlag header. +var segDataExts = map[string]bool{".seg": true, ".v": true, ".ef": true} + +func upgradeSegFilesInDir(dir string, lookup map[string]seg.FileCompression, logger log.Logger) error { return filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error { - if err != nil || d.IsDir() || filepath.Ext(path) != ".seg" { + if err != nil || d.IsDir() { return err } - return upgradeSegHeaderV1toV2(path, logger) + ext := filepath.Ext(path) + if !segDataExts[ext] { + return nil + } + return upgradeSegHeaderV1toV2(path, ext, lookup, logger) }) } -// upgradeSegHeaderV1toV2 patches a single .seg file from V1 to V2 format. +// upgradeSegHeaderV1toV2 patches a single seg-format file from V1 to V2. // It is a no-op for V0 files and files that are already V2+. -func upgradeSegHeaderV1toV2(path string, logger log.Logger) error { +func upgradeSegHeaderV1toV2(path, ext string, lookup map[string]seg.FileCompression, logger log.Logger) error { d, err := seg.NewDecompressor(path) if err != nil { return err @@ -88,10 +97,14 @@ func upgradeSegHeaderV1toV2(path string, logger log.Logger) error { return nil } - // Probe actual key/value compression before closing the mmap. pageCnt := d.CompressedPageValuesCount() - fc := seg.DetectCompressType(d.MakeGetter()) - d.Close() // must release mmap before writing + d.Close() // release mmap before writing + + // Determine key/val compression from the schema lookup (tag = last dash-separated + // component of the base name, e.g. "accounts" from "v1-000000-000100-accounts.seg"). + base := filepath.Base(path) + tag := segTag(base, ext) + fc := lookup[tag+ext] // zero value (no compression) if unknown var bitmask seg.FeatureFlagBitmask if pageCnt > 0 { @@ -117,6 +130,52 @@ func upgradeSegHeaderV1toV2(path string, logger log.Logger) error { return err } - logger.Debug("[seg_header_v2] upgraded", "file", filepath.Base(path)) + logger.Debug("[seg_header_v2] upgraded", "file", base) return nil } + +// segTag extracts the file-type tag from a snapshot base name. +// Format: "{version}-{from}-{to}-{tag}.{ext}" (first 3 fields are separated by "-"). +func segTag(base, ext string) string { + withoutExt := base[:len(base)-len(ext)] + parts := strings.SplitN(withoutExt, "-", 4) + if len(parts) == 4 { + return parts[3] + } + return "" +} + +// buildSegCompressionLookup returns a map from "{tag}{ext}" to the FileCompression +// used when writing that file type, derived from the global statecfg.Schema. +func buildSegCompressionLookup() map[string]seg.FileCompression { + s := statecfg.Schema + m := make(map[string]seg.FileCompression) + + domains := []statecfg.DomainCfg{ + s.AccountsDomain, + s.StorageDomain, + s.CodeDomain, + s.CommitmentDomain, + s.ReceiptDomain, + s.RCacheDomain, + } + for _, d := range domains { + name := d.Name.String() + m[name+".seg"] = d.Compression + m[name+".v"] = d.Hist.Compression + // Each domain's inverted-index uses the domain name as its FilenameBase. + m[d.Hist.IiCfg.FilenameBase+".ef"] = d.Hist.IiCfg.Compression + } + + // Standalone inverted indexes (log/trace). + for _, ii := range []statecfg.InvIdxCfg{ + s.LogAddrIdx, + s.LogTopicIdx, + s.TracesFromIdx, + s.TracesToIdx, + } { + m[ii.FilenameBase+".ef"] = ii.Compression + } + + return m +} From 8acd1579bb19ddfbfc5f6087beb0b4bb19db89ed Mon Sep 17 00:00:00 2001 From: Alexey Sharov Date: Sat, 28 Feb 2026 11:57:25 +0700 Subject: [PATCH 5/9] save --- db/seg/compress.go | 15 +++++++++++++++ db/seg/decompress.go | 9 +++++++++ db/seg/seg_auto_rw.go | 2 ++ db/seg/seg_interface.go | 7 ++++--- db/seg/seg_paged_rw.go | 9 +++++++++ 5 files changed, 39 insertions(+), 3 deletions(-) diff --git a/db/seg/compress.go b/db/seg/compress.go index ce00423dbcc..c67f0d6656f 100644 --- a/db/seg/compress.go +++ b/db/seg/compress.go @@ -144,6 +144,7 @@ type Compressor struct { version uint8 featureFlagBitmask FeatureFlagBitmask compPageValuesCount uint8 + totalPairsCount uint64 metadata []byte } @@ -216,6 +217,13 @@ func (c *Compressor) SetTrace(trace bool) { c.trace = trace } func (c *Compressor) FileName() string { return c.outputFileName } func (c *Compressor) WorkersAmount() int { return c.Workers } func (c *Compressor) GetValuesOnCompressedPage() int { return int(c.ValuesOnCompressedPage) } +// SetPairsCount stores the total number of key-value pairs in the file header +// (V2+, PairsCountEnabled). Must be called before Compress(). +func (c *Compressor) SetPairsCount(n uint64) { + c.totalPairsCount = n + c.featureFlagBitmask.Set(PairsCountEnabled) +} + func (c *Compressor) SetMetadata(metadata []byte) { if !c.ExpectMetadata { panic("metadata not expected in compressor") @@ -353,6 +361,13 @@ func (c *Compressor) Compress() error { return err } } + if c.featureFlagBitmask.Has(PairsCountEnabled) { + var buf [8]byte + binary.BigEndian.PutUint64(buf[:], c.totalPairsCount) + if _, err := cf.Write(buf[:]); err != nil { + return err + } + } } if c.ExpectMetadata { diff --git a/db/seg/decompress.go b/db/seg/decompress.go index f6c62dad3f2..d40cf429dd8 100644 --- a/db/seg/decompress.go +++ b/db/seg/decompress.go @@ -188,6 +188,7 @@ type Decompressor struct { version uint8 featureFlagBitmask FeatureFlagBitmask compPageValuesCount uint8 + totalPairsCount uint64 serializedDictSize uint64 lenDictSize uint64 // huffman encoded lengths @@ -272,6 +273,10 @@ func NewDecompressorWithMetadata(compressedFilePath string, hasMetadata bool) (* d.compPageValuesCount = d.data[0] d.data = d.data[1:] } + if d.featureFlagBitmask.Has(PairsCountEnabled) { + d.totalPairsCount = binary.BigEndian.Uint64(d.data[:8]) + d.data = d.data[8:] + } if hasMetadata { metadataLen := binary.BigEndian.Uint32(d.data[:4]) @@ -515,6 +520,10 @@ func (d *Decompressor) DictWords() int { return d.dictWords } func (d *Decompressor) DictLens() int { return d.dictLens } func (d *Decompressor) CompressedPageValuesCount() int { return int(d.compPageValuesCount) } func (d *Decompressor) CompressionFormatVersion() uint8 { return d.version } +// TotalPairsCount returns the number of key-value pairs stored in a page-compressed +// file. Returns 0 for files that do not have PairsCountEnabled in their header (V1 +// files and V2 files written without page compression). +func (d *Decompressor) TotalPairsCount() uint64 { return d.totalPairsCount } // FileCompression returns the key/value compression flags stored in the file header. // Only reliable for V2+ files; returns CompressNone for V0 and V1. func (d *Decompressor) FileCompression() FileCompression { diff --git a/db/seg/seg_auto_rw.go b/db/seg/seg_auto_rw.go index 24a3185514b..003c99bd443 100644 --- a/db/seg/seg_auto_rw.go +++ b/db/seg/seg_auto_rw.go @@ -139,6 +139,8 @@ func (c *Writer) ReadFrom(r *Reader) error { return nil } +func (c *Writer) SetPairsCount(n uint64) { c.Compressor.SetPairsCount(n) } + func (c *Writer) Close() { if c.Compressor != nil { c.Compressor.Close() diff --git a/db/seg/seg_interface.go b/db/seg/seg_interface.go index 29b88d442e5..0174475a9c2 100644 --- a/db/seg/seg_interface.go +++ b/db/seg/seg_interface.go @@ -36,9 +36,10 @@ const ( type FeatureFlag uint8 const ( - PageLevelCompressionEnabled FeatureFlag = 1 << iota // 0b001 - KeyCompressionEnabled // 0b010 - ValCompressionEnabled // 0b100 + PageLevelCompressionEnabled FeatureFlag = 1 << iota // 0b0001 + KeyCompressionEnabled // 0b0010 + ValCompressionEnabled // 0b0100 + PairsCountEnabled // 0b1000 — total key-value pair count follows compPageValuesCount in the header (V2+ only) ) type FeatureFlagBitmask uint8 diff --git a/db/seg/seg_paged_rw.go b/db/seg/seg_paged_rw.go index 17b31c2b0d9..154db2fd6c7 100644 --- a/db/seg/seg_paged_rw.go +++ b/db/seg/seg_paged_rw.go @@ -260,11 +260,20 @@ func (c *PagedWriter) Empty() bool { return c.pairs == 0 } func (c *PagedWriter) Close() { c.parent.Close() } +type pairsCountSetter interface { + SetPairsCount(n uint64) +} + func (c *PagedWriter) Compress() error { // Flush any remaining unwritten page data if err := c.Flush(); err != nil { return err } + if c.pageSize > 1 { + if setter, ok := c.parent.(pairsCountSetter); ok { + setter.SetPairsCount(uint64(c.pairs)) + } + } return c.parent.Compress() } From f0402587e82630dba3878c6ac2758ad03e612665 Mon Sep 17 00:00:00 2001 From: Alexey Sharov Date: Wed, 4 Mar 2026 09:46:11 +0700 Subject: [PATCH 6/9] merge main --- db/seg/compress.go | 1 + 1 file changed, 1 insertion(+) diff --git a/db/seg/compress.go b/db/seg/compress.go index c67f0d6656f..c3a84202cba 100644 --- a/db/seg/compress.go +++ b/db/seg/compress.go @@ -217,6 +217,7 @@ func (c *Compressor) SetTrace(trace bool) { c.trace = trace } func (c *Compressor) FileName() string { return c.outputFileName } func (c *Compressor) WorkersAmount() int { return c.Workers } func (c *Compressor) GetValuesOnCompressedPage() int { return int(c.ValuesOnCompressedPage) } + // SetPairsCount stores the total number of key-value pairs in the file header // (V2+, PairsCountEnabled). Must be called before Compress(). func (c *Compressor) SetPairsCount(n uint64) { From 1190d5babf801e7d2e3dbf91bdcd7b7be95e2db6 Mon Sep 17 00:00:00 2001 From: Alexey Sharov Date: Wed, 4 Mar 2026 09:46:22 +0700 Subject: [PATCH 7/9] merge main --- db/seg/seg_paged_rw.go | 1 + 1 file changed, 1 insertion(+) diff --git a/db/seg/seg_paged_rw.go b/db/seg/seg_paged_rw.go index 154db2fd6c7..0a90507331b 100644 --- a/db/seg/seg_paged_rw.go +++ b/db/seg/seg_paged_rw.go @@ -260,6 +260,7 @@ func (c *PagedWriter) Empty() bool { return c.pairs == 0 } func (c *PagedWriter) Close() { c.parent.Close() } + type pairsCountSetter interface { SetPairsCount(n uint64) } From bcf8ab0add858a0bb56b87cbc8e9e9e54c50fd4f Mon Sep 17 00:00:00 2001 From: Alexey Sharov Date: Wed, 4 Mar 2026 09:46:32 +0700 Subject: [PATCH 8/9] merge main --- db/seg/decompress.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/db/seg/decompress.go b/db/seg/decompress.go index c10109e20de..eca8e4a30ee 100644 --- a/db/seg/decompress.go +++ b/db/seg/decompress.go @@ -520,10 +520,12 @@ func (d *Decompressor) DictWords() int { return d.dictWords } func (d *Decompressor) DictLens() int { return d.dictLens } func (d *Decompressor) CompressedPageValuesCount() int { return int(d.compPageValuesCount) } func (d *Decompressor) CompressionFormatVersion() uint8 { return d.version } + // TotalPairsCount returns the number of key-value pairs stored in a page-compressed // file. Returns 0 for files that do not have PairsCountEnabled in their header (V1 // files and V2 files written without page compression). -func (d *Decompressor) TotalPairsCount() uint64 { return d.totalPairsCount } +func (d *Decompressor) TotalPairsCount() uint64 { return d.totalPairsCount } + // FileCompression returns the key/value compression flags stored in the file header. // Only reliable for V2+ files; returns CompressNone for V0 and V1. func (d *Decompressor) FileCompression() FileCompression { From 30d7090eebfc0c0a2ce1b5fc33dd392f391d8315 Mon Sep 17 00:00:00 2001 From: Alexey Sharov Date: Wed, 4 Mar 2026 11:07:28 +0700 Subject: [PATCH 9/9] save --- db/seg/compress_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/db/seg/compress_test.go b/db/seg/compress_test.go index 7e071c51ed6..655b421f51b 100644 --- a/db/seg/compress_test.go +++ b/db/seg/compress_test.go @@ -65,7 +65,7 @@ func TestCompressEmptyDict(t *testing.T) { if g.HasNext() { t.Fatalf("not expecting anything else") } - if cs := checksum(file); cs != 2900861311 { + if cs := checksum(file); cs != 1708374642 { t.Errorf("result file hash changed, %d", cs) } } @@ -191,7 +191,7 @@ func TestCompressDict1(t *testing.T) { i++ } - if cs := checksum(d.filePath); cs != 3613725886 { + if cs := checksum(d.filePath); cs != 1393846834 { // it's ok if hash changed, but need re-generate all existing snapshot hashes // in https://github.com/erigontech/erigon-snapshot t.Errorf("result file hash changed, %d", cs) @@ -262,7 +262,7 @@ func TestCompressDictCmp(t *testing.T) { i++ } - if cs := checksum(d.filePath); cs != 3613725886 { + if cs := checksum(d.filePath); cs != 1393846834 { // it's ok if hash changed, but need re-generate all existing snapshot hashes // in https://github.com/erigontech/erigon-snapshot t.Errorf("result file hash changed, %d", cs) @@ -329,7 +329,7 @@ func Test_CompressWithMetadata(t *testing.T) { } i++ } - if cs := checksum(d.filePath); cs != 4122484600 { + if cs := checksum(d.filePath); cs != 878767318 { t.Errorf("result file hash changed, %d", cs) } } @@ -384,7 +384,7 @@ func TestCompressNoWordPatterns(t *testing.T) { } require.False(t, g.HasNext()) - if cs := checksum(file); cs != 1879837905 { + if cs := checksum(file); cs != 1750094945 { t.Errorf("fast-path output differs from main, checksum=%d", cs) } }