-
Notifications
You must be signed in to change notification settings - Fork 1.2k
fix: remove every snapshot lifecycle directory on explicit reindex #7588
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -37,6 +37,35 @@ | |||||||||||||||||
| #include <vector> | ||||||||||||||||||
|
|
||||||||||||||||||
| namespace node { | ||||||||||||||||||
| static bool RemoveSnapshotChainstateArtifacts(const fs::path& data_dir, bilingual_str& error) | ||||||||||||||||||
| { | ||||||||||||||||||
| // Explicit reindexing discards both coins databases and EvoDB, so remove | ||||||||||||||||||
| // every snapshot lifecycle directory in the same stroke. A directory must | ||||||||||||||||||
| // not outlive the markers that describe it: a chainstate_snapshot dir whose | ||||||||||||||||||
| // EvoDB markers were just wiped can no longer be revived by | ||||||||||||||||||
| // ActivateExistingSnapshot(), and a reindex is also the user's request to | ||||||||||||||||||
| // discard the _INVALID forensics directory and any interrupted-swap | ||||||||||||||||||
| // remnant, neither of which the (skipped) recovery pass will see. | ||||||||||||||||||
| const fs::path normal{data_dir / "chainstate"}; | ||||||||||||||||||
| fs::path snapshot{normal}; | ||||||||||||||||||
| snapshot += SNAPSHOT_CHAINSTATE_SUFFIX; | ||||||||||||||||||
| fs::path invalid{snapshot}; | ||||||||||||||||||
| invalid += SNAPSHOT_INVALID_SUFFIX; | ||||||||||||||||||
| fs::path to_delete{normal}; | ||||||||||||||||||
| to_delete += SNAPSHOT_TODELETE_SUFFIX; | ||||||||||||||||||
| for (const auto& path : {snapshot, invalid, to_delete}) { | ||||||||||||||||||
| if (!fs::exists(path)) continue; | ||||||||||||||||||
| try { | ||||||||||||||||||
| RemoveAllDurably(path); | ||||||||||||||||||
| } catch (const fs::filesystem_error& e) { | ||||||||||||||||||
|
Comment on lines
+57
to
+60
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Suggestion: Catch failures from the snapshot artifact existence check
Suggested change
source: ['coderabbit'] |
||||||||||||||||||
| error = strprintf(_("Failed to remove snapshot chainstate artifact %s for reindex: %s"), | ||||||||||||||||||
| fs::PathToString(path), e.what()); | ||||||||||||||||||
| return false; | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| return true; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| static bool RecoverSnapshotCleanup(CEvoDB& evodb, const fs::path& data_dir, bilingual_str& error) | ||||||||||||||||||
| { | ||||||||||||||||||
| const fs::path normal{data_dir / "chainstate"}; | ||||||||||||||||||
|
|
@@ -326,6 +355,13 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize | |||||||||||||||||
|
|
||||||||||||||||||
| LOCK(cs_main); | ||||||||||||||||||
|
|
||||||||||||||||||
| if (options.reindex || options.reindex_chainstate) { | ||||||||||||||||||
| bilingual_str cleanup_error; | ||||||||||||||||||
| if (!RemoveSnapshotChainstateArtifacts(options.data_dir, cleanup_error)) { | ||||||||||||||||||
| return {ChainstateLoadStatus::FAILURE, cleanup_error}; | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| evodb.reset(); | ||||||||||||||||||
| // TODO: pass DbWrapperParams as options instead multiple params | ||||||||||||||||||
| evodb = std::make_unique<CEvoDB>(util::DbWrapperParams{ | ||||||||||||||||||
|
|
@@ -344,15 +380,6 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize | |||||||||||||||||
| // Load the fully validated chainstate. | ||||||||||||||||||
| chainman.InitializeChainstate(options.mempool, *evodb, chain_helper); | ||||||||||||||||||
|
|
||||||||||||||||||
| // Wiping the shared EvoDB above erased the SNAPSHOT best-block marker that | ||||||||||||||||||
| // ActivateExistingSnapshot() requires, so a persisted snapshot chainstate can | ||||||||||||||||||
| // no longer be revived. Discard it here rather than letting startup fail with | ||||||||||||||||||
| // advice ("reindex") the user has just followed, which would never recover. | ||||||||||||||||||
| if ((options.reindex || options.reindex_chainstate) && !DeleteSnapshotChainstateFromDisk()) { | ||||||||||||||||||
| return {ChainstateLoadStatus::FAILURE, | ||||||||||||||||||
| _("Failed to remove the snapshot chainstate directory. Remove it manually before restarting.")}; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| // Load a chain created from a UTXO snapshot, if any exist. | ||||||||||||||||||
| bilingual_str snapshot_error; | ||||||||||||||||||
| if (!chainman.DetectSnapshotChainstate(options.mempool, snapshot_error)) { | ||||||||||||||||||
|
|
||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: dashpay/dash
Length of output: 5167
🏁 Script executed:
Repository: dashpay/dash
Length of output: 11074
🌐 Web query:
C++ std::filesystem::exists(const path&) throws filesystem_error status error specification💡 Result:
The C++ standard library function std::filesystem::exists provides both throwing and non-throwing overloads to handle file system operations [1][2]. When using the overload that takes only a path (std::filesystem::path), the function may throw a std::filesystem::filesystem_error if an underlying operating system API call fails [1][3]. It is important to note that this exception is thrown when an error occurs during the attribute determination process (such as permission issues or invalid path formats that the OS cannot resolve), but it is explicitly NOT thrown simply because a file does not exist [1][2]. If the path is known to not exist, the function returns false without throwing an exception [1][2]. The throwing behavior is specified as follows: - The function internally calls std::filesystem::status [1][2]. - If that call encounters an OS-level error that prevents it from determining the file status (e.g., an I/O error or restricted access that isn't classified as 'not found'), it throws std::filesystem::filesystem_error [1][4]. - Any overload not marked noexcept may also throw std::bad_alloc if memory allocation fails [1][2]. To avoid exceptions, you should use the non-throwing overload that accepts a std::error_code parameter [1][2]: bool exists(const std::filesystem::path& p, std::error_code& ec) noexcept; In this version, if an error occurs, the function does not throw an exception; instead, it sets the provided std::error_code object to the specific OS error code, allowing you to handle the failure programmatically [1][3]. If the file does not exist, the function simply returns false and the error code is cleared or indicates success, depending on the implementation state [1][2].
Citations:
Catch
fs::exists()failures.fs::exists(const fs::path&)forwards to throwingstd::filesystem::exists. A status error can throw beforetry, bypassingcleanup_errorand the definedChainstateLoadStatus::FAILUREreturn. Move the check insidetry.🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Valid — fixed in 2c3e615, though with a different mechanism than suggested: rather than changing
DirectoryCommit()'s documented ignore-errors contract (other callers rely on it), the removal now goes throughRemoveAllDurably(), the existing durable-deletion helper from the snapshot lifecycle work, whoseSyncDirectoryOrThrowpropagates open/fsync failures as thefs::filesystem_errorthis function's catch already converts into a startup failure.🤖 Posted autonomously by Claude on behalf of pasta.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@PastaPastaPasta,RemoveAllDurably()correctly propagates deletion and directory-sync failures.However, the original failure path remains.
fs::exists(path)is still outside thetryblock inRemoveSnapshotChainstateArtifacts(). A filesystem status error from that call can still throw before the function setscleanup_errorand returnsChainstateLoadStatus::FAILURE.Please move the existence check into the
tryblock, or use thestd::error_codeoverload and convert its error tocleanup_error.✏️ Learnings added
You are interacting with an AI system.