From 5984bde7c459815116883bc62c68f0c398c427c4 Mon Sep 17 00:00:00 2001 From: ozpool Date: Wed, 13 May 2026 12:55:14 +0530 Subject: [PATCH 1/2] cmd/geth: report disk space cleared on prune-history completion Fixes #31916. Snapshot the on-disk size of the prunable chain-freezer tables (bodies and receipts) before and after the prune step, and include the delta as cleared= on the existing "History pruning completed" log line. Operators see the impact of the run immediately without inspecting the data directory by hand. The header and hash tables are intentionally excluded from the measurement because they are configured with prunable=false and are never truncated. If AncientSize fails for any table the helper returns 0 so the log shows "cleared=0.00 B" rather than a partial, misleading figure. --- cmd/geth/chaincmd.go | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 98ed348d8c..06fa3fd6e6 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -775,17 +775,41 @@ func pruneHistory(ctx *cli.Context) error { log.Info("Starting history pruning", "head", currentHeader.Number, "target", targetBlock, "targetHash", targetBlockHash.Hex()) start := time.Now() + sizeBefore := prunableFreezerSize(chaindb) rawdb.PruneTransactionIndex(chaindb, targetBlock) if _, err := chaindb.TruncateTail(targetBlock); err != nil { return fmt.Errorf("failed to truncate ancient data: %v", err) } - log.Info("History pruning completed", "tail", targetBlock, "elapsed", common.PrettyDuration(time.Since(start))) + sizeAfter := prunableFreezerSize(chaindb) + var cleared common.StorageSize + if sizeBefore > sizeAfter { + cleared = common.StorageSize(sizeBefore - sizeAfter) + } + log.Info("History pruning completed", "tail", targetBlock, "elapsed", common.PrettyDuration(time.Since(start)), "cleared", cleared) // TODO(s1na): what if there is a crash between the two prune operations? return nil } +// prunableFreezerSize returns the total on-disk size of the chain freezer +// tables that prune-history truncates (bodies and receipts). The header and +// hash tables are retained long-term and are not measured here. +func prunableFreezerSize(db ethdb.Database) uint64 { + var total uint64 + for _, table := range []string{rawdb.ChainFreezerBodiesTable, rawdb.ChainFreezerReceiptTable} { + size, err := db.AncientSize(table) + if err != nil { + // If we can't read the size of any prunable table, the delta would + // be misleading. Return 0 so the caller logs "cleared=0.00 B" rather + // than a partial figure. + return 0 + } + total += size + } + return total +} + // downloadEra is the era1 file downloader tool. func downloadEra(ctx *cli.Context) error { flags.CheckExclusive(ctx, eraBlockFlag, eraEpochFlag, eraAllFlag) From b3c07dc7b9767db7d52a88e63d679863056bb28e Mon Sep 17 00:00:00 2001 From: ozpool Date: Fri, 15 May 2026 16:36:04 +0530 Subject: [PATCH 2/2] cmd/geth, core/rawdb: include tx index delta in prune history cleared total MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rjl493456442 pointed out that the cleared figure logged at the end of `geth db prune-history` only counted the body/receipt flat-file delta via prunableFreezerSize() and silently dropped the tx-index work done just before TruncateTail. Anyone reading the log line saw a number that under-reported the actual on-disk savings. PruneTransactionIndex now returns the logical bytes deleted (sum of key + value lengths for each removed entry) by accumulating during the existing DeleteAllTxLookupEntries iteration. cmd/geth folds that into the StorageSize total before logging. The figure is uncompacted — the KV store doesn't expose per-prefix on-disk usage without a full compaction sweep — but it's the only signal available without changing the prune sequence, and it matches the granularity rjl asked for. Existing rawdb test (TestPruneTransactionIndex) is unchanged; Go's untyped-discard semantics keep it compiling against the new signature. --- cmd/geth/chaincmd.go | 6 +++++- core/rawdb/chain_iterator.go | 10 ++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 06fa3fd6e6..591f3c4839 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -776,7 +776,7 @@ func pruneHistory(ctx *cli.Context) error { log.Info("Starting history pruning", "head", currentHeader.Number, "target", targetBlock, "targetHash", targetBlockHash.Hex()) start := time.Now() sizeBefore := prunableFreezerSize(chaindb) - rawdb.PruneTransactionIndex(chaindb, targetBlock) + txIndexBytes := rawdb.PruneTransactionIndex(chaindb, targetBlock) if _, err := chaindb.TruncateTail(targetBlock); err != nil { return fmt.Errorf("failed to truncate ancient data: %v", err) } @@ -785,6 +785,10 @@ func pruneHistory(ctx *cli.Context) error { if sizeBefore > sizeAfter { cleared = common.StorageSize(sizeBefore - sizeAfter) } + // Include the tx index entries pruned from the KV store; without this the + // reported figure only covers the body/receipt freezer delta and ignores + // the index work above. + cleared += common.StorageSize(txIndexBytes) log.Info("History pruning completed", "tail", targetBlock, "elapsed", common.PrettyDuration(time.Since(start)), "cleared", cleared) // TODO(s1na): what if there is a crash between the two prune operations? diff --git a/core/rawdb/chain_iterator.go b/core/rawdb/chain_iterator.go index afa1aa7a4c..8965e4fc20 100644 --- a/core/rawdb/chain_iterator.go +++ b/core/rawdb/chain_iterator.go @@ -377,8 +377,11 @@ func unindexTransactionsForTesting(db ethdb.Database, from uint64, to uint64, in unindexTransactions(db, from, to, interrupt, hook, false) } -// PruneTransactionIndex removes all tx index entries below a certain block number. -func PruneTransactionIndex(db ethdb.Database, pruneBlock uint64) { +// PruneTransactionIndex removes all tx index entries below a certain block number +// and returns the approximate number of bytes freed (sum of key + value lengths +// for each deleted entry). The figure is logical (uncompacted) but is the only +// per-prefix size signal the KV store exposes without forcing a full compaction. +func PruneTransactionIndex(db ethdb.Database, pruneBlock uint64) (removedBytes uint64) { tail := ReadTxIndexTail(db) if tail == nil || *tail > pruneBlock { return // no index, or index ends above pruneBlock @@ -386,6 +389,7 @@ func PruneTransactionIndex(db ethdb.Database, pruneBlock uint64) { // There are blocks below pruneBlock in the index. Iterate the entire index to remove // their entries. Note if this fails, the index is messed up, but tail still points to // the old tail. + keyLen := uint64(len(txLookupPrefix) + common.HashLength) var count, removed int DeleteAllTxLookupEntries(db, func(txhash common.Hash, v []byte) bool { count++ @@ -399,11 +403,13 @@ func PruneTransactionIndex(db ethdb.Database, pruneBlock uint64) { bn := decodeNumber(v) if bn < pruneBlock { removed++ + removedBytes += keyLen + uint64(len(v)) return true } return false }) WriteTxIndexTail(db, pruneBlock) + return } func decodeNumber(b []byte) uint64 {