Skip to content

fix(core/rawdb): return the iterator error from InspectDatabase - #2723

Merged
gzliudan merged 1 commit into
XinFinOrg:dev-upgradefrom
gzliudan:fix-rawdb-inspect-iterator-error
Oct 16, 2026
Merged

gzliudan merged 1 commit into
XinFinOrg:dev-upgradefrom
gzliudan:fix-rawdb-inspect-iterator-error

Conversation

@gzliudan

@gzliudan gzliudan commented Oct 3, 2026 •

Copy link
Copy Markdown
Collaborator

Proposed changes

InspectDatabase walks a keyspace with an iterator and never consulted it.Error(), so a walk that the database aborted on an I/O error was indistinguishable from one that ran to the end - both leave the loop on Next() == false. It then rendered a table built from the partial scan and returned nil, which reads as "inspection passed" to whoever ran db inspect. Only the per-key accounting is affected: nothing on the consensus or sync paths reads this function, and its single caller is the db inspect command (cmd/XDC/dbcmd.go:210).

The fix reports the pending iterator error once the walk ends, before the report is rendered. Upstream does report it, as part of go-ethereum ethereum#32506, but that change landed inside a parallelisation of the traversal (a worker closure over per-range stat structs driven by an errgroup) while the function here is the single-threaded version, so only the semantics are ported, not the shape. The defect predates dev-upgrade, so it lands as its own commit rather than a fixup.

This is the same class of gap that #2707 fixes one function over in DeleteDanglingHashes, split out into its own PR. It carries no dependency on that PR; the only overlap is the shared test fixtures, which dev-upgrade already carries (see the note below).

Tests

TestInspectDatabaseIteratorError wraps ethdb.Database in a test-only store whose NewIterator hands out an iterator that stops after one key and reports an error, so a truncated walk is reproducible without a production seam. It also captures os.Stdout around the call, which pins the check on both sides: an aborted walk must render nothing, and a clean walk over the same fixture must still render the report, down to its per-category rows and a non-zero total. One direction alone cannot tell an early return apart from a function that never renders anything.

Both directions are confirmed with go test -overlay, each failing on a different assertion:

  • deleting the it.Error() check fails with expected the iterator error to be returned, got <nil> - the pre-fix behaviour;
  • moving the check below table.Render() fails on the table built from the partial scan, which is what pins it as an early return rather than a trailing one. The capture helper drains the pipe from its own goroutine and restores os.Stdout through t.Cleanup, so the walk cannot deadlock against the pipe buffer.
go test ./core/rawdb/ -run 'TestInspectDatabaseIteratorError' -v

make all and make quick-test pass on the branch.

Note on the shared test fixture

The store that aborts the walk mid-iteration, and the headers it walks, come from the fixtures failingIterator, failingIterStore and writeOrphanHeaders in core/rawdb/accessors_chain_test.go. dev-upgrade carries them since #2707 landed, and TestDeleteDanglingHashesIteratorError and TestDeleteDanglingHashesCleanWalk use them too. This branch keeps no copy of its own, so the two share one declaration per symbol and can land in either order.

Types of changes

  • build: Changes that affect the build system or external dependencies
  • ci: Changes to CI configuration files and scripts
  • chore: Changes that don't change source code or tests
  • docs: Documentation only changes
  • feat: A new feature
  • fix: A bug fix
  • perf: A code change that improves performance
  • refactor: A code change that neither fixes a bug nor adds a feature
  • revert: Revert something
  • style: Changes that do not affect the meaning of the code
  • test: Adding missing tests or correcting existing tests

Impacted Components

  • Consensus
  • Account
  • Network
  • Geth
  • Smart Contract
  • External components
  • Not sure (Please specify below)

Checklist

  • This PR has sufficient test coverage (unit/integration test) OR I have provided reason in the PR description for not having test coverage
  • Tested on a private network from the genesis block and monitored the chain operating correctly for multiple epochs.
  • Provide an end-to-end test plan in the PR description on how to manually test it on the devnet/testnet.
  • Tested the backwards compatibility.
  • Tested with XDC nodes running this version co-exist with those running the previous version.
  • Relevant documentation has been updated as part of this PR
  • N/A

The three network-facing items are not applicable here: the change only alters an error path that surfaces when the database itself fails mid-walk, so the wire format, consensus rules, storage layout and API signatures are untouched, and nodes running this build coexist with older ones unchanged. Reaching the changed line end to end needs an I/O failure while a keyspace is being walked, which cannot be produced by configuration; the unit test drives it through the injected iterator instead.

@coderabbitai

coderabbitai Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: c045691e-43f7-48a2-846d-a7d1bdba7dd7

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gzliudan
gzliudan force-pushed the fix-rawdb-inspect-iterator-error branch 4 times, most recently from f239ea8 to d47bf69 Compare October 14, 2026 04:14
@gzliudan
gzliudan force-pushed the fix-rawdb-inspect-iterator-error branch 3 times, most recently from 97a46fc to 6fd664e Compare October 16, 2026 09:00
ported from go-ethereum ethereum#32506

InspectDatabase walked the keyspace and never consulted it.Error(), so a walk
that the database aborted printed a table built from a partial scan and returned
nil, which reads as "inspection passed" to whoever ran db inspect. Only the
per-key accounting was affected; nothing on the consensus or sync paths reads
this function, its single caller is the db inspect command.

Upstream landed the check as part of parallelising the traversal, so its
InspectDatabase is a worker closure over per-range stat structs driven by an
errgroup. The version here is the single-threaded one and the two do not line
up line for line, so only the semantics are ported: report the pending
iterator error once the walk ends. The defect predates dev-upgrade, hence a
separate commit rather than a fixup.

The test also pins the user-visible half of the early return, by capturing
os.Stdout around the call: an aborted walk must render nothing, and a clean walk
over the same fixture must still render the report, down to its per-category
rows and a non-zero total. Upstream orders it the same way - its workers
return the iterator error and eg.Wait short-circuits before the table is
rendered - but upstream has no test for either function, so the
assertion is ours. One direction alone cannot tell an early return apart from a
function that never renders anything. Confirmed with go test -overlay, both
directions: deleting the check fails the first assertion with got <nil>, moving
it below table.Render() fails the second one on the table built from the partial
scan. The capture helper drains the pipe from its own goroutine and restores
os.Stdout through t.Cleanup, so the walk cannot deadlock against the pipe buffer.

The aborted-walk fixture is shared with
TestDeleteDanglingHashesIteratorError, which landed with XinFinOrg#2707; it lives in
accessors_chain_test.go, so this test keeps no copy of its own. The zero-total
assertion counts the total rows it recognises and fails when it finds none, so a
column layout it no longer matches cannot leave the check silently vacuous.
@gzliudan
gzliudan force-pushed the fix-rawdb-inspect-iterator-error branch from 6fd664e to b88ee21 Compare October 16, 2026 09:10
@gzliudan
gzliudan merged commit 5ab345a into XinFinOrg:dev-upgrade Oct 16, 2026
16 checks passed
@gzliudan
gzliudan deleted the fix-rawdb-inspect-iterator-error branch October 16, 2026 09:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants