Compare commits

...

354 Commits

Author SHA1 Message Date
Ed Hennis
814d14ad2b feat: Log number of nodes rescued during rotation 2026-08-22 15:29:17 -04:00
Ed Hennis
e7cfc46275 Merge branch 'ximinez/online-delete-gaps' into ximinez/online-delete-gaps2 2026-08-21 17:28:58 -04:00
Ed Hennis
c3c555c260 Ensure getAndResetDuplicationCount() is always called after copyNodes 2026-08-21 17:27:15 -04:00
Ed Hennis
3cc27a4a67 Merge remote-tracking branch 'XRPLF/ximinez/online-delete-gaps' into ximinez/online-delete-gaps2
* XRPLF/ximinez/online-delete-gaps:
  Add a 1/4s delay so the server doesn't restore the removed ledger
  Reapply "experiment: find where the deleted ledger is being put back"
  fix: Add assert for account_info flags (7987)
2026-08-21 15:13:13 -04:00
Ed Hennis
17bec9e4d4 fixup! Merge branch 'develop' into ximinez/online-delete-gaps 2026-08-21 15:12:25 -04:00
Ed Hennis
0e54845db0 Merge branch 'develop' into ximinez/online-delete-gaps 2026-08-21 14:27:08 -04:00
Ed Hennis
6f1f53a152 Add a 1/4s delay so the server doesn't restore the removed ledger
- Delay before removing the ledger index so the server can get all its
  record keeping done before I mess with it.
2026-08-21 14:25:01 -04:00
Ed Hennis
080ad9fe34 Merge remote-tracking branch 'XRPLF/ximinez/online-delete-gaps' into ximinez/online-delete-gaps2
* XRPLF/ximinez/online-delete-gaps:
  Revert "experiment: find where the deleted ledger is being put back"
2026-08-21 12:15:45 -04:00
Ed Hennis
2620ae59ae Reapply "experiment: find where the deleted ledger is being put back"
- It's Schrodinger's ledger index. Not checking that it's removed allows
  it to be put back.

This reverts commit 77793df659.
2026-08-21 12:13:59 -04:00
Ed Hennis
d3d5f39787 Merge remote-tracking branch 'XRPLF/ximinez/online-delete-gaps' into ximinez/online-delete-gaps2
* XRPLF/ximinez/online-delete-gaps:
  fix: Reject vault deposits that move nothing from the depositor (8014)
  experiment: find where the deleted ledger is being put back
  clang-tidy: Use switch. Duh
  Fix timing issues in the gap test
  perf: Speed up addition time for drastically different exponents (7825)
  refactor: Collapse transactions.macro settings into a TxSettings struct (8001)
  fix: Enforce MPT balance invariants under the latest cleanup amendment (7889)
  fix: Prevent AMM auction slots from being acquired at zero cost when trading fee is zero (7430)
  docs: Fix yum installation baseurl (8066)
  fix: Return specific and consistent errors from vault_info (8015)
  ci: Save cargo cache only from develop by default (8063)
  fix: Check credential for LoanBrokerCoverWithdraw and VaultWithdraw (7107)
  build: Suppress MSVC linker warning LNK4099 (8049)
  chore: [DEPENDABOT] Bump cxx from 1.0.198 to 1.0.199 in /crates in the rust-dependencies group across 1 directory (8050)
  build: Use debian `any` distribution & signed (hosted) rpm repo (8053)
  ci: Do not cache cargo binaries (8062)
2026-08-21 11:40:03 -04:00
Ed Hennis
77793df659 Revert "experiment: find where the deleted ledger is being put back"
This reverts commit e67e9f6660.
2026-08-21 11:36:56 -04:00
Ed Hennis
6abcc9fe1a Merge remote-tracking branch 'XRPLF/develop' into ximinez/online-delete-gaps
* XRPLF/develop:
  fix: Reject vault deposits that move nothing from the depositor (8014)
  perf: Speed up addition time for drastically different exponents (7825)
  refactor: Collapse transactions.macro settings into a TxSettings struct (8001)
2026-08-21 11:35:50 -04:00
Ed Hennis
e67e9f6660 experiment: find where the deleted ledger is being put back 2026-08-20 22:48:39 -04:00
Ed Hennis
6718b3276b clang-tidy: Use switch. Duh 2026-08-20 21:29:34 -04:00
Ed Hennis
642dbad885 Fix timing issues in the gap test
- The coverage build exposed that the timing was more sensitive than
  I had thought. Force things to run in the order I expect by changing
  the server mode (via NetworkOPs::setMode).
2026-08-20 21:07:10 -04:00
Ed Hennis
833433b25e Merge branch 'develop' into ximinez/online-delete-gaps 2026-08-20 15:39:42 -04:00
Ed Hennis
84e32ed33c Merge remote-tracking branch 'XRPLF/ximinez/online-delete-gaps' into ximinez/online-delete-gaps2
* XRPLF/ximinez/online-delete-gaps:
  clang-tidy: exclude 2 rules from rotation functionality
  Minor fixes from AI code review
2026-08-19 18:24:01 -04:00
Ed Hennis
23369e114a clang-tidy: exclude 2 rules from rotation functionality
- readability-else-after-return
- readability-braces-around-statements
2026-08-19 18:08:01 -04:00
Ed Hennis
d3d0c6366a Minor fixes from AI code review 2026-08-19 17:35:53 -04:00
Ed Hennis
c15c7bfd54 Merge branch 'ximinez/online-delete-gaps' into ximinez/online-delete-gaps2 2026-08-19 12:57:56 -04:00
Ed Hennis
af61140cd9 Merge branch 'develop' into ximinez/online-delete-gaps 2026-08-19 12:57:51 -04:00
Ed Hennis
4e1de43939 clang-tidy: Make changes uglier by adding a bunch of redundant {}s 2026-08-19 11:42:59 -04:00
Ed Hennis
dc71651429 Merge remote-tracking branch 'XRPLF/ximinez/online-delete-gaps' into ximinez/online-delete-gaps2
* XRPLF/ximinez/online-delete-gaps:
  More review feedback from @vlntb: log warnings after a delay
  Review feedback from @vlntb: Circuit breaker
  build: Use AlmaLinux for the RHEL packaging image (8045)
  build: Run nix macos builds in CI; deny nix store references (8023)
  fix: Enable reserve checking on ending sponsorship (8044)
  fix: Add ValidPermissionedDEX invariant track for fully consumed offer (6736)
  AI review feedback: validate config, improve healthWait()
  test: Split Vault_test into topical suites under src/test/app/vault/ (8041)
  fix: Fix assorted NFT and pDEX bugs (7749)
  chore: Publish debian/rpm packages from GitHub directly (8031)
  refactor: Add `SHAMapNodeID::isPrefixOf` (7939)
  fix: Conserve funds correctly when LoanPay fee payee is below reserve (7843)
2026-08-18 21:33:11 -04:00
Ed Hennis
e372201499 Merge branch 'develop' into ximinez/online-delete-gaps 2026-08-18 21:27:07 -04:00
Ed Hennis
8b2a7f6253 More review feedback from @vlntb: log warnings after a delay
- Log the "waiting" messages at warning level once the delay reaches 1/4
  of the way to the circuit breaker cutoff.
- Also fix the exception message on configuration validation.
2026-08-18 21:15:52 -04:00
Ed Hennis
6211dbd1cc Review feedback from @vlntb: Circuit breaker
- Adds a "max_waiting_ledgers" config, which defaults to the
  "online_delete" value. If the rotation gets stuck for that number of
  ledgers, abort the rotation and try again later. The existing gap will
  be ignored on the next rotation.
- Create a new HealthResult::Expired. Update most healthWait() call
  sites to account for the new possible result. Pay special note to the
  calls in SHAMapStoreImp::run() to ensure they don't exit the thread
  prematurely, or keep rotating when they should not.
- Did a little clean up while I was in there.
- Update tests.
2026-08-18 20:46:06 -04:00
Ed Hennis
d89063acae AI review feedback: validate config, improve healthWait()
- Check that recovery_wait_time is non-zero.
- In healthWait(), explicitly check whether the only missing ledger is
  the current validated ledger, in the process of being built. If so,
  log lower, and only wait 1/10 of the time.
- Add the special case from #7793 that will let rotation run if the node
  is disconnected.
2026-08-18 15:48:34 -04:00
Ed Hennis
0e328d8277 Merge branch 'ximinez/online-delete-gaps' into ximinez/online-delete-gaps2 2026-08-17 22:36:53 -04:00
Ed Hennis
7d55bb7d8e Merge branch 'develop' into ximinez/online-delete-gaps 2026-08-17 22:36:49 -04:00
Ed Hennis
14c190fd31 Merge branch 'ximinez/online-delete-gaps' into ximinez/online-delete-gaps2 2026-08-14 19:24:18 -04:00
Ed Hennis
efe843d6b2 Merge branch 'develop' into ximinez/online-delete-gaps 2026-08-14 19:24:14 -04:00
Ed Hennis
41e422d2c1 Merge branch 'ximinez/online-delete-gaps' into ximinez/online-delete-gaps2 2026-08-13 16:01:23 -04:00
Ed Hennis
7f10f3938f Merge branch 'develop' into ximinez/online-delete-gaps 2026-08-13 16:01:19 -04:00
Ed Hennis
9c8b45f0ed Add an instrumentation check for Antithesis 2026-08-12 18:04:53 -04:00
Ed Hennis
f7e96beba7 Merge branch 'ximinez/online-delete-gaps' into ximinez/online-delete-gaps2 2026-08-12 15:36:55 -04:00
Ed Hennis
5a7a715283 Merge branch 'develop' into ximinez/online-delete-gaps 2026-08-12 15:36:50 -04:00
Ed Hennis
8f1974d0ec Review feedback from @sophiax851: inFlight back to bool
- Convert the inFlight tracking to a simple bool instead of ledger
  index. In testing, almost no nodes were skipped anyway, so it's not
  a signficant factor.
- Reset the duplicate node count when reading it.
- Update some comments and logging.
2026-08-11 22:09:30 -04:00
Ed Hennis
f84e07427c Merge branch 'ximinez/online-delete-gaps' into ximinez/online-delete-gaps2 2026-08-10 21:37:12 -04:00
Ed Hennis
d375fa356f Merge branch 'develop' into ximinez/online-delete-gaps 2026-08-10 21:37:07 -04:00
Ed Hennis
5d5e4d3405 AI review feedback: logging, documentation / comments
- Also, use ScopeExit instead of a custom local guard class to reset
  rotationInFlight.
2026-08-10 21:18:30 -04:00
Ed Hennis
1eacb33d72 Revert "Update assert label in src/xrpld/app/misc/SHAMapStoreImp.cpp"
This reverts commit fafc48bda3.
2026-08-10 21:14:58 -04:00
Ed Hennis
fafc48bda3 Update assert label in src/xrpld/app/misc/SHAMapStoreImp.cpp
Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com>
2026-08-10 20:44:43 -04:00
Ed Hennis
9a2b6866a7 Merge branch 'ximinez/online-delete-gaps' into ximinez/online-delete-gaps2 2026-08-10 20:39:09 -04:00
Ed Hennis
8a49f93b78 Merge branch 'develop' into ximinez/online-delete-gaps 2026-08-10 20:39:05 -04:00
Ed Hennis
c5c85eef48 Merge branch 'ximinez/online-delete-gaps' into ximinez/online-delete-gaps2 2026-08-10 15:04:38 -04:00
Ed Hennis
5aed870ff0 Merge branch 'develop' into ximinez/online-delete-gaps 2026-08-10 15:04:32 -04:00
Ed Hennis
0fe2be63f4 Merge branch 'ximinez/online-delete-gaps' into ximinez/online-delete-gaps2 2026-08-07 17:32:10 -04:00
Ed Hennis
4c93c8d5be Merge branch 'develop' into ximinez/online-delete-gaps 2026-08-07 17:32:06 -04:00
Ed Hennis
5817d3547e Merge remote-tracking branch 'XRPLF/ximinez/online-delete-gaps' into ximinez/online-delete-gaps2
* XRPLF/ximinez/online-delete-gaps: (36 commits)
  chore: Bump version to 3.4.0-b0 (7976)
  chore: Bump version to 3.3.0
  chore: Bump version to 3.3.0-rc7
  fix: Increase manifest protocol message size cap and fix manifests relay
  fix: Cap untrusted manifests per message and drop oversized ones
  chore: Bump version to 3.3.0-rc6
  feat: Package validator-keys inside rippled
  chore: Bump version to 3.3.0-rc5
  fix: Switch SponsorshipSet to use a delta for sfFeeAmount
  fix: Re-revert "fix: Set request size limits and differential pricing for get-object-by-hash calls"
  chore: Bump version to 3.3.0-rc4
  fix: Revert "fix: Set request size limits and differential pricing for get-object-by-hash calls"
  chore: Bump version to 3.3.0-rc3
  fix: Reduce untrusted manifest cache cap to 100
  fix: Revert "fix: Reject oversized SHAMap nodes in gotStaleData and fetch-pack path"
  chore: Bump version to 3.3.0-rc2
  fix: Check transaction type before RawTransactions
  fix: Change ConfidentialMPTConvert to no delegate
  fix: Bound untrusted manifest cache
  feat: Make DynamicMPT opt-in-immutable
  ...
2026-08-07 14:23:15 -04:00
Ed Hennis
7a8d86b0b1 Merge branch 'develop' into ximinez/online-delete-gaps 2026-08-07 13:38:10 -04:00
Ed Hennis
1bbe54ad60 Merge branch 'develop' into ximinez/online-delete-gaps 2026-08-07 12:02:27 -04:00
Ed Hennis
0bb9716784 clang-tidy fixes: includes and explicit struct field names 2026-08-06 15:22:32 -04:00
Ed Hennis
295c71b007 Merge branch 'ximinez/online-delete-gaps' into ximinez/online-delete-gaps2 2026-08-06 14:14:43 -04:00
Ed Hennis
78f3148e4c Merge branch 'develop' into ximinez/online-delete-gaps 2026-08-06 14:14:38 -04:00
Ed Hennis
894263bb89 Update renamed namespace 2026-08-05 22:49:46 -04:00
Ed Hennis
9de4e7e58d Merge branch 'ximinez/online-delete-gaps' into ximinez/online-delete-gaps2 2026-08-05 12:17:07 -04:00
Ed Hennis
ec844351d9 Merge branch 'develop' into ximinez/online-delete-gaps 2026-08-05 12:17:03 -04:00
Ed Hennis
41b4cf36e6 Merge branch 'ximinez/online-delete-gaps' into ximinez/online-delete-gaps2 2026-08-04 19:41:56 -04:00
Ed Hennis
6f7ad510e3 Merge branch 'develop' into ximinez/online-delete-gaps 2026-08-04 19:41:52 -04:00
Ed Hennis
5db10a4d3b Merge branch 'release/3.2.x' into ximinez/online-delete-gaps2
* release/3.2.x:
  chore: Bump version to 3.2.1
  chore: Bump version to 3.2.1-rc1
  fix: Cap untrusted manifests per message and drop oversized ones
  fix: Reject oversized validator manifest before decoding
  fix: Reduce untrusted manifest cache cap to 100
  fix: Bound untrusted manifest cache
2026-07-31 22:53:33 -04:00
Ed Hennis
919cfd5f49 Merge branch 'ximinez/online-delete-gaps' into ximinez/online-delete-gaps2 2026-07-31 10:01:28 -04:00
Ed Hennis
476a64d5ce Merge branch 'develop' into ximinez/online-delete-gaps 2026-07-31 10:01:23 -04:00
Ed Hennis
303952aeb4 Merge branch 'ximinez/online-delete-gaps' into ximinez/online-delete-gaps2 2026-07-30 15:41:40 -04:00
Ed Hennis
85a6dfee1f Merge branch 'develop' into ximinez/online-delete-gaps 2026-07-30 15:41:18 -04:00
Ed Hennis
621f876337 Merge remote-tracking branch 'XRPLF/ximinez/online-delete-gaps' into ximinez/online-delete-gaps2
* XRPLF/ximinez/online-delete-gaps:
  Update lastLedger in the healthWait loop in the right order
2026-07-27 22:57:28 -04:00
Ed Hennis
da2f36c8a6 Update lastLedger in the healthWait loop in the right order 2026-07-27 22:47:33 -04:00
Ed Hennis
fab69c28d2 Merge remote-tracking branch 'XRPLF/ximinez/online-delete-gaps' into ximinez/online-delete-gaps2
* XRPLF/ximinez/online-delete-gaps:
  chore: Move semantic version tests to gtest (7872)
  style: Make clang-tidy format files using clang-format rules (7880)
  ci: Change `server_definitions` upload config name (7878)
  chore: Trivial gtest migrations (7865)
  ci: Group github-actions dependabot updates (7876)
  ci: Update CI image and prepare-runner action (7874)
  test: Migrate `nodestore` tests from Beast to GTest (7292)
  test: Improve the server status test to not race and randomly fail (7304)
  ci: [DEPENDABOT] bump actions/checkout from 7.0.0 to 7.0.1 (7871)
  chore: Fix clang version in devshell (7860)
  chore: Verify tooling version for Nix-managed environments (7862)
2026-07-27 18:33:07 -04:00
Ed Hennis
afedf445f0 Merge branch 'develop' into ximinez/online-delete-gaps 2026-07-27 17:00:51 -04:00
Ed Hennis
fe1b1c8f92 Count the number of explicitly duplicated nodes, and log it
- The "copied ledger" count may differ from the "Rotating:" count,
  because the latter will also count nodes from the cache.
2026-07-24 19:55:03 -04:00
Ed Hennis
e61b134742 Rescue node from copyNode as NodeType::AccountNode
- Nodes rescued from freshenCaches are checked dynamically, and not
  written if not AccountNode.
2026-07-24 18:34:24 -04:00
Ed Hennis
2ba3edaf14 Merge remote-tracking branch 'XRPLF/ximinez/online-delete-gaps' into ximinez/online-delete-gaps2
* XRPLF/ximinez/online-delete-gaps:
  build: Patсh binary in local Linux nix environment (7859)
2026-07-24 11:55:11 -04:00
Ed Hennis
7cf063b1d8 Merge branch 'develop' into ximinez/online-delete-gaps 2026-07-24 11:54:19 -04:00
Ed Hennis
5f087f6446 Improve node store rotation
- Add a special case to healthWait() to not pause when the server is
  DISCONNECTED.
- Log sequence differences at the start and end of rotation.
- Limit copy-forward to ledgers we're not about to delete, and unknown.
  (Changes rotationInFlight_ from a bool to a LedgerIndex.)
- Set the "rotation in flight" index right at the beginning of the rotation
    - Because the node copy process can take a long time, other ledgers may
      get validated. Any reads for those ledgers have the potential to be
      served by the archive DB and thus lost, too. Why wait?
- Add an assertion suggested on @vlntb in #7763.
- Don't wait as long for ledgers that should be built soon.
- Rescue nodes from the tree node cache, too.
2026-07-23 21:17:06 -04:00
Ed Hennis
a2446e8f99 Merge branch 'develop' into ximinez/online-delete-gaps 2026-07-23 20:08:50 -04:00
Ed Hennis
6accc87a10 Merge branch 'develop' into ximinez/online-delete-gaps 2026-07-22 20:40:49 -04:00
Ed Hennis
313d0d188b Merge branch 'develop' into ximinez/online-delete-gaps 2026-07-22 13:54:28 -04:00
Ed Hennis
53dc3b4051 Merge branch 'develop' into ximinez/online-delete-gaps 2026-07-21 14:10:04 -04:00
Ed Hennis
b589985dae Merge branch 'develop' into ximinez/online-delete-gaps 2026-07-20 17:23:09 -04:00
Ed Hennis
180c979835 Merge branch 'develop' into ximinez/online-delete-gaps 2026-07-20 12:45:06 -04:00
Ed Hennis
88665a6de0 clang-format: remove unused header 2026-07-17 19:11:42 -04:00
Ed Hennis
66526a8a48 AI review feedback: clean up, enforce parameter validity, tests 2026-07-16 20:28:33 -04:00
Ed Hennis
18c11097b6 Merge branch 'develop' into ximinez/online-delete-gaps 2026-07-16 14:29:48 -04:00
Ed Hennis
bfe54849c9 Merge branch 'develop' into ximinez/online-delete-gaps 2026-07-15 19:12:13 -04:00
Ed Hennis
831ac87627 Merge branch 'develop' into ximinez/online-delete-gaps 2026-07-15 11:28:12 -04:00
Ed Hennis
1b7af3165d Merge branch 'develop' into ximinez/online-delete-gaps 2026-07-14 20:25:39 -04:00
Ed Hennis
48ca6e0025 Merge branch 'develop' into ximinez/online-delete-gaps 2026-07-14 13:48:02 -04:00
Ed Hennis
0047ed4db2 Merge branch 'develop' into ximinez/online-delete-gaps 2026-07-13 17:22:24 -04:00
Ed Hennis
6dc73cb528 Merge branch 'develop' into ximinez/online-delete-gaps 2026-07-13 15:07:28 -04:00
Ed Hennis
7f58c3d7d6 Merge remote-tracking branch 'XRPLF/develop' into ximinez/online-delete-gaps
* XRPLF/develop:
  test: Add tests for check doxygen style (7795)
  style: Add pre-commit hook to check doxygen style (7794)
  style: Unify style for all Doxygen comments (7776)
2026-07-13 12:21:59 -04:00
Ed Hennis
86f52c412c Merge branch 'develop' into ximinez/online-delete-gaps 2026-07-12 18:38:08 -04:00
Ed Hennis
4dce2f22b6 Merge branch 'develop' into ximinez/online-delete-gaps 2026-07-08 17:23:11 -04:00
Ed Hennis
fe8368b970 Update test to ensure correct starting conditions 2026-07-07 20:26:55 -04:00
Ed Hennis
d68273c755 Merge branch 'develop' into ximinez/online-delete-gaps 2026-07-07 19:32:20 -04:00
Ed Hennis
df489c7a6e Tweak log message levels
- lastGoodValidatedLedger_ update is now trace.
- Log the first healthWait failure for a given validated ledger as
  trace.
2026-07-07 17:29:42 -04:00
Ed Hennis
b45038f70a Merge branch 'develop' into ximinez/online-delete-gaps 2026-07-07 15:50:15 -04:00
Ed Hennis
44cc4e656f Merge branch 'develop' into ximinez/online-delete-gaps 2026-07-06 13:32:00 -04:00
Ed Hennis
ac3435bf0a Merge branch 'develop' into ximinez/online-delete-gaps 2026-07-02 19:03:48 -04:00
Ed Hennis
bd2f5dbd35 AI review feedback: variable initialization, comments, headers 2026-07-02 19:01:51 -04:00
Ed Hennis
4644347d21 Apply suggestions from code review: fix typos
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-02 18:48:43 -04:00
Ed Hennis
e34e05ebec Merge remote-tracking branch 'XRPLF/develop' into ximinez/online-delete-gaps
* XRPLF/develop:
  refactor: Retire DisallowIncomingV1 fix (7364)
  build: Add verify-headers target to cleanup headers (7670)
2026-07-02 11:17:33 -04:00
Ed Hennis
9b22c6c80b Reorganize healthWait to avoid deadlock and duplicated code 2026-07-01 18:38:03 -04:00
Ed Hennis
d4e166bbdc Merge branch 'develop' into ximinez/online-delete-gaps 2026-07-01 13:24:20 -04:00
Ed Hennis
b80776b0fe Merge branch 'develop' into ximinez/online-delete-gaps 2026-06-30 14:38:54 -04:00
Ed Hennis
9af4fb521e Merge branch 'develop' into ximinez/online-delete-gaps 2026-06-29 20:05:54 -04:00
Ed Hennis
7ee290674c Merge branch 'develop' into ximinez/online-delete-gaps 2026-06-25 11:43:17 -04:00
Ed Hennis
68f050b186 Merge branch 'develop' into ximinez/online-delete-gaps 2026-06-24 19:59:59 -04:00
Ed Hennis
2be13c18a1 Merge branch 'develop' into ximinez/online-delete-gaps 2026-06-23 11:09:48 -04:00
Ed Hennis
df5fa5aade Correct a couple of comments 2026-06-22 16:48:00 -04:00
Ed Hennis
51ef3a7343 Merge branch 'develop' into ximinez/online-delete-gaps 2026-06-22 16:41:46 -04:00
Ed Hennis
dd4eca22b7 Merge branch 'develop' into ximinez/online-delete-gaps 2026-06-17 15:01:23 -04:00
Ed Hennis
1116dbfd0e Merge branch 'develop' into ximinez/online-delete-gaps 2026-06-16 18:56:01 -04:00
Ed Hennis
258d5e1f1f clang-tidy: nodiscard rendezvous, headers, const correctness, {}s 2026-06-16 18:47:40 -04:00
Ed Hennis
4270367efc Merge branch 'develop' into ximinez/online-delete-gaps 2026-06-15 21:32:49 -04:00
Ed Hennis
fad34e852d More AI review feedback:
- Move a log message into the same block as the thing it's logging.
- Change default recovery time from 1s to 2s. This may need some more
  tuning.
- Rename the online_delete test config helper function to onlineDelete.
- Copy some class values while under lock, even though they can't
  change, as defense in depth.
- Use RAII scope unlock in healthWait().
2026-06-15 21:28:23 -04:00
Ed Hennis
f5b4ac358f AI review feedback
- Remove extraneous function declaration.
- Add a timeout to SHAMapStore_test::testLedgerGaps so it won't hang in
  case of regression.
- Add an optional timeout parameter to SHAMapStore::rendezvous, and
  return a success flag.
  - Use the timeout in SHAMapStore_test::testLedgerGaps to show lack of
    progress in the Store without an arbitrary sleep delay.
- Refactor LedgerMaster::missingFromCompleteLedgerRange to use RangeSet
  functions instead of a naive iteration.
- If lastGoodValidatedLedger_ is default (0) in healthWait(), then act
  as if no ledgers are missing.
2026-06-15 20:13:40 -04:00
Ed Hennis
875bcc530e Correct a couple of variable names 2026-06-15 16:47:29 -04:00
Ed Hennis
e75f5b101b Merge branch 'develop' into ximinez/online-delete-gaps 2026-06-15 15:28:44 -04:00
Ed Hennis
95f74d61b2 Merge branch 'develop' into ximinez/online-delete-gaps 2026-06-15 12:11:54 -04:00
Ed Hennis
4e12b787be Merge remote-tracking branch 'XRPLF/develop' into ximinez/online-delete-gaps
* XRPLF/develop: (22 commits)
  test: Add null check unit test for `Oracle::aggregatePrice` (7306)
  ci: Patch conan recipe for Nix to be able to use on macOS (7532)
  ci: Run sanitizers on release builds too (7527)
  fix: Correct hybrid offer deletion on credential expiry (6843)
  ci: Make sanitizer flags lists in the profile, not a string (7449)
  ci: Make configurations launch on certain event types (7447)
  fix: Add [[maybe_unused]] to fix320Enabled for assert=OFF builds (7446)
  ci: Add `gh` and `file` to nix packages (7444)
  fix: Disable transaction invariants (7409)
  perf: Dispatch "hasInvalidAmount()" on type tag instead of dynamic_cast (7402)
  refactor: Retire fixUniversalNumber amendment (5962)
  test: Do not create data directory for memory databases (7323)
  ci: Launch upload-conan-deps on profile change (7442)
  fix: Fix Number comparison operator (7406)
  feat: Use C++ 23 standard (7431)
  refactor: Introduce XRPL_ASSERT_IF for amendment-gated assertions (7378)
  refactor: Change config section and key string literals into constants (7095)
  refactor: Use `std::move` and `std::string_view` where possible (7424)
  refactor: Use const function arguments where possible (7423)
  ci: Use XRPLF/actions build-multiarch-image workflow (7428)
  ...
2026-06-11 21:34:19 -04:00
Ed Hennis
8b6c80027b Merge branch 'develop' into ximinez/online-delete-gaps 2026-06-05 18:48:40 -04:00
Ed Hennis
ace73678cf Merge branch 'develop' into ximinez/online-delete-gaps 2026-06-04 13:32:19 -04:00
Ed Hennis
12bbba00b0 Merge branch 'develop' into ximinez/online-delete-gaps 2026-06-04 10:08:25 -04:00
Ed Hennis
2affb66f50 Merge branch 'develop' into ximinez/online-delete-gaps 2026-06-02 11:58:29 -04:00
Ed Hennis
d09e785c39 Merge branch 'develop' into ximinez/online-delete-gaps 2026-06-01 14:24:26 -04:00
Ed Hennis
97fdf310f9 Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-28 23:47:49 -04:00
Ed Hennis
d3ce76825c Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-27 17:00:52 -04:00
Ed Hennis
1e7e274727 Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-27 15:18:10 -04:00
Ed Hennis
b272d71cc7 Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-27 12:06:52 -04:00
Ed Hennis
21268d9c36 Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-26 19:20:55 -04:00
Ed Hennis
932e22df7d Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-26 16:56:36 -04:00
Ed Hennis
652a7cd225 Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-26 16:01:32 -04:00
Ed Hennis
4a224dbfa4 Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-22 17:56:07 -04:00
Ed Hennis
d2a5981f87 Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-21 14:25:41 -04:00
Ed Hennis
a977836630 Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-21 10:09:42 -04:00
Ed Hennis
bf075200bb Merge remote-tracking branch 'XRPLF/develop' into ximinez/online-delete-gaps
* XRPLF/develop:
  docs: Add --parallel flag to cmake build commands in BUILD.md (7302)
  fix: Fix wrong hybrid offer orderbook placement and update `LedgerStateFix` to amend `ExchangeRate` meta (7087)
  style: More clang-tidy identifier renaming (7290)
  fix: Update pDEX invariant firing under a valid offer deletion (7118)
  fix: Fix multisign and signfor to check for delegate (7064)
  refactor: Fix `sfGeneric` and `sfInvalid` field names (7300)
  docs: Fix some comments to improve readability (7122)
  feat: Propagate underlying MPT flags to vault shares (7077)
2026-05-21 11:59:26 +01:00
Ed Hennis
ee49e76a16 Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-19 16:53:27 -04:00
Ed Hennis
c1318990f3 Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-19 10:15:02 -04:00
Ed Hennis
69128294f8 Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-19 05:15:40 -04:00
Ed Hennis
149e884ebc Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-15 21:32:03 -04:00
Ed Hennis
d27353225c Merge remote-tracking branch 'XRPLF/develop' into ximinez/online-delete-gaps
* XRPLF/develop:
  release: Set version to 3.3.0-b0 (7280)
  refactor: Rename static constants (7120)
  refactor: Use `isFlag` where possible instead of bitwise math (7278)
  ci: Update XRPLF/actions (7281)
2026-05-15 15:47:01 -04:00
Ed Hennis
13a0f77eb5 Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-14 20:39:04 -04:00
Ed Hennis
549c093398 Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-13 20:16:12 -04:00
Ed Hennis
fb66ca7a2e Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-13 12:03:27 -04:00
Ed Hennis
e2af73b0a0 Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-12 19:18:39 -04:00
Ed Hennis
14c3c9a256 Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-12 16:26:02 -04:00
Ed Hennis
4b6851a287 Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-12 15:59:13 -04:00
Ed Hennis
d182673d24 Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-11 13:33:56 -04:00
Ed Hennis
bea609d805 Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-07 18:09:54 -04:00
Ed Hennis
56eeb20bc8 Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-07 14:18:14 -04:00
Ed Hennis
e293a3d918 Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-07 13:28:18 -04:00
Ed Hennis
3dff580d39 Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-06 22:34:11 -04:00
Ed Hennis
487f5a0fd3 clang-tidy fix: lock_guard -> scoped_lock 2026-05-06 18:44:16 -04:00
Ed Hennis
d8134f98e9 Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-06 14:17:47 -04:00
Ed Hennis
450a623d4b Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-05 16:46:26 -04:00
Ed Hennis
9e517be4ce Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-05 10:50:18 -04:00
Ed Hennis
b8370438fb Merge remote-tracking branch 'XRPLF/develop' into ximinez/online-delete-gaps
* XRPLF/develop:
  ci: Rewrite clang-tidy workflow(s) in a reusable manner (7062)
  chore: Ignore identifier-naming update in git blame (7066)
  refactor: Enable clang-tidy `readability-identifier-naming` check (6571)
2026-05-04 21:36:05 -04:00
Ed Hennis
ecb5604d3b Merge branch 'develop' into ximinez/online-delete-gaps 2026-05-01 12:54:31 -04:00
Ed Hennis
c4527e7b0f Merge remote-tracking branch 'XRPLF/develop' into ximinez/online-delete-gaps
* XRPLF/develop:
  fix: Gate -mcmodel flags to x86_64 in sanitizer builds (7049)
  fix: Prevents overwriting a bool value in an invariant (6609)
  fix: Address code review comments regarding `boost::coroutine2` (6977)
  refactor: Apply various minor improvements and corrections (7045)
  fix: Store `Delegate` object in delegating and authorized account directories for proper deletion (6681)
  ci: Use print-env from XRPLF/actions (7052)
  fix: Make assorted RPC fixes (6529)
  chore: Enable clang-tidy v21 new checks (7031)
2026-04-30 14:30:36 -04:00
Ed Hennis
778e2b3ce8 Fix clang-tidy: missing include 2026-04-28 15:44:14 -05:00
Ed Hennis
295d03aec8 Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-28 16:23:08 -04:00
Ed Hennis
9262c2e624 Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-25 14:41:57 -04:00
Ed Hennis
b385a41aa5 Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-23 15:55:43 -04:00
Ed Hennis
4eb9726097 Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-22 23:39:00 -04:00
Ed Hennis
28a38ef1cc Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-22 14:48:51 -04:00
Ed Hennis
39f9380b2b Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-22 11:36:03 -04:00
Ed Hennis
239fcaceb4 clang-tidy fix: Use std::to_string for std::uint32_t 2026-04-21 19:30:05 -04:00
Ed Hennis
8ab86f009e Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-21 18:47:53 -04:00
Ed Hennis
d4b58a74f4 Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-21 14:37:52 -04:00
Ed Hennis
ff900591ae Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-21 10:40:45 -04:00
Ed Hennis
a5b7471af6 Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-20 17:49:20 -04:00
Ed Hennis
e0734986dd Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-20 15:44:37 -04:00
Ed Hennis
8440f479e5 Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-20 11:37:54 -04:00
Ed Hennis
ac390622e0 Fix clang-tidy issues 2026-04-17 17:54:37 -04:00
Ed Hennis
5d807f0d6d Merge remote-tracking branch 'XRPLF/develop' into ximinez/online-delete-gaps
* XRPLF/develop:
  chore: Enable clang-tidy include cleaner (6947)
  fix: Change AMMClawback return code to tecNO_PERMISSION (6946)
  ci: [DEPENDABOT] bump actions/upload-pages-artifact from 4.0.0 to 5.0.0 (6927)
  ci: [DEPENDABOT] bump actions/upload-artifact from 7.0.0 to 7.0.1 (6928)
  chore: Enable clang-tidy readability checks (6930)
2026-04-17 15:23:33 -04:00
Ed Hennis
b93294f26b Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-16 13:44:23 -04:00
Ed Hennis
ef95ace0f9 Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-15 19:06:15 -04:00
Ed Hennis
fc58bf6edf Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-15 14:28:41 -04:00
Ed Hennis
348555d5ba Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-13 20:28:29 -04:00
Ed Hennis
302802e42d Fix clang-tidy issues 2026-04-13 20:23:23 -04:00
Ed Hennis
5d881f87a3 Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-10 12:12:27 -04:00
Ed Hennis
a8a8035b32 Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-10 09:10:39 -04:00
Ed Hennis
45af14231f Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-09 11:39:55 -04:00
Ed Hennis
380bf274d0 Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-08 16:56:56 -04:00
Ed Hennis
460ec5eeea Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-08 15:12:46 -04:00
Ed Hennis
14be8ca4ea Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-07 16:47:32 -04:00
Ed Hennis
948264d44c Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-06 19:06:51 -04:00
Ed Hennis
9c816b2043 Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-06 18:33:20 -04:00
Ed Hennis
f68402acd1 Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-06 16:52:50 -04:00
Ed Hennis
5358e25eaa Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-06 13:24:22 -04:00
Ed Hennis
a34d1e5537 Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-01 17:26:14 -04:00
Ed Hennis
78a122943d Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-01 13:39:05 -04:00
Ed Hennis
06135e7203 Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-01 11:45:32 -04:00
Ed Hennis
f20425fa4f Merge branch 'develop' into ximinez/online-delete-gaps 2026-03-30 21:29:37 -04:00
Ed Hennis
3e94546acc Merge branch 'develop' into ximinez/online-delete-gaps 2026-03-12 15:05:04 -04:00
Ed Hennis
3b088ed0dc Merge branch 'develop' into ximinez/online-delete-gaps 2026-03-10 12:46:18 -04:00
Ed Hennis
26182ed52e Merge branch 'develop' into ximinez/online-delete-gaps 2026-03-06 13:04:00 -04:00
Ed Hennis
de2a3e10f5 Merge branch 'develop' into ximinez/online-delete-gaps 2026-03-05 21:34:29 -04:00
Ed Hennis
e17f8554fc Merge branch 'develop' into ximinez/online-delete-gaps 2026-03-04 17:11:27 -04:00
Ed Hennis
386a7192ba Merge branch 'develop' into ximinez/online-delete-gaps 2026-03-03 20:46:41 -04:00
Ed Hennis
12cc6e424d Merge branch 'develop' into ximinez/online-delete-gaps 2026-03-03 15:54:32 -04:00
Ed Hennis
c9deecf1b7 Merge branch 'develop' into ximinez/online-delete-gaps 2026-02-24 17:34:32 -04:00
Ed Hennis
ddd1b49f38 Merge branch 'develop' into ximinez/online-delete-gaps 2026-02-24 16:45:55 -04:00
Ed Hennis
c1b2a24005 Merge branch 'develop' into ximinez/online-delete-gaps 2026-02-20 18:49:39 -04:00
Ed Hennis
86d88eca31 Merge branch 'develop' into ximinez/online-delete-gaps 2026-02-20 18:25:58 -04:00
Ed Hennis
ef09eaea00 Merge branch 'develop' into ximinez/online-delete-gaps 2026-02-20 17:31:41 -04:00
Ed Hennis
c504cfb291 Merge branch 'develop' into ximinez/online-delete-gaps 2026-02-20 17:21:03 -04:00
Ed Hennis
0c217dfa2b Merge branch 'develop' into ximinez/online-delete-gaps 2026-02-20 15:14:23 -04:00
Ed Hennis
b0198d2566 Merge remote-tracking branch 'upstream/develop' into ximinez/online-delete-gaps
* upstream/develop:
  ci: Add dependabot config (6379)
  Fix tautological assertion (6393)
  chore: Apply clang-format width 100 (6387)
2026-02-20 12:20:07 -05:00
Ed Hennis
7eee8ca802 Update formatting 2026-02-20 12:15:30 -05:00
Ed Hennis
2a079a0154 Merge commit '25cca465538a56cce501477f9e5e2c1c7ea2d84c' into ximinez/online-delete-gaps
* commit '25cca465538a56cce501477f9e5e2c1c7ea2d84c':
  chore: Set clang-format width to 100 in config file (6387)
2026-02-20 12:12:56 -05:00
Ed Hennis
40989c1178 Merge branch 'develop' into ximinez/online-delete-gaps 2026-02-19 16:21:11 -05:00
Ed Hennis
addc831eb3 Merge branch 'develop' into ximinez/online-delete-gaps 2026-02-18 18:06:20 -04:00
Ed Hennis
b4efc6d116 Merge branch 'develop' into ximinez/online-delete-gaps 2026-02-04 16:29:49 -04:00
Ed Hennis
125d075d6e Merge branch 'develop' into ximinez/online-delete-gaps 2026-02-04 14:16:24 -04:00
Ed Hennis
370a775479 Merge branch 'develop' into ximinez/online-delete-gaps 2026-02-03 16:07:47 -04:00
Ed Hennis
1a2ee706eb Fix formatting 2026-01-28 19:43:23 -05:00
Ed Hennis
2a981357ba Merge branch 'develop' into ximinez/online-delete-gaps 2026-01-28 18:40:24 -04:00
Ed Hennis
1ae475e724 Merge commit '5f638f55536def0d88b970d1018a465a238e55f4' into ximinez/online-delete-gaps
* commit '5f638f55536def0d88b970d1018a465a238e55f4':
  chore: Set ColumnLimit to 120 in clang-format (6288)
2026-01-28 17:38:29 -05:00
Ed Hennis
a3e9401fbc Merge commit '92046785d1fea5f9efe5a770d636792ea6cab78b' into ximinez/online-delete-gaps
* commit '92046785d1fea5f9efe5a770d636792ea6cab78b':
  test: Fix the `xrpl.net` unit test using async read (6241)
  ci: Upload Conan recipes for develop, release candidates, and releases (6286)
  fix: Stop embedded tests from hanging on ARM by using `atomic_flag` (6248)
  fix:  Remove DEFAULT fields that change to the default in associateAsset (6259) (6273)
  refactor: Update Boost to 1.90 (6280)
  refactor: clean up uses of `std::source_location` (6272)
  ci: Pass missing sanitizers input to actions (6266)
  ci: Properly propagate Conan credentials (6265)
  ci: Explicitly set version when exporting the Conan recipe (6264)
  ci: Use plus instead of hyphen for Conan recipe version suffix (6261)
  chore: Detect uninitialized variables in CMake files (6247)
  ci: Run on-trigger and on-pr when generate-version is modified (6257)
  refactor: Enforce 15-char limit and simplify labels for thread naming (6212)
  docs: Update Ripple Bug Bounty public key (6258)
  ci: Add missing commit hash to Conan recipe version (6256)
  fix: Include `<functional>` header in `Number.h` (6254)
  ci: Upload Conan recipe for merges into develop and commits to release (6235)
  Limit reply size on `TMGetObjectByHash` queries (6110)
  ci: remove 'master' branch as a trigger (6234)
  Improve ledger_entry lookups for fee, amendments, NUNL, and hashes (5644)
2026-01-28 17:38:13 -05:00
Ed Hennis
9091469f9e Merge branch 'develop' into ximinez/online-delete-gaps 2026-01-15 13:03:03 -04:00
Ed Hennis
17fa54f1f9 Merge branch 'develop' into ximinez/online-delete-gaps 2026-01-15 12:05:41 -04:00
Ed Hennis
8fb5347c2d Merge branch 'develop' into ximinez/online-delete-gaps 2026-01-14 19:20:11 -04:00
Ed Hennis
6739bf998f Merge branch 'develop' into ximinez/online-delete-gaps 2026-01-13 18:04:18 -04:00
Ed Hennis
6eea38ba67 Merge branch 'develop' into ximinez/online-delete-gaps 2026-01-13 16:15:44 -04:00
Ed Hennis
e9cf88b359 Merge branch 'develop' into ximinez/online-delete-gaps 2026-01-13 15:01:56 -04:00
Ed Hennis
645b203476 Merge branch 'develop' into ximinez/online-delete-gaps 2026-01-12 21:07:23 -04:00
Ed Hennis
be2aff1f4c Merge branch 'develop' into ximinez/online-delete-gaps 2026-01-12 14:51:34 -04:00
Ed Hennis
56ed237e82 Merge branch 'develop' into ximinez/online-delete-gaps 2026-01-11 00:48:27 -04:00
Ed Hennis
fd7b0fd135 Merge branch 'develop' into ximinez/online-delete-gaps 2026-01-08 17:05:28 -04:00
Ed Hennis
e700994891 Merge branch 'develop' into ximinez/online-delete-gaps 2026-01-08 13:03:41 -04:00
Ed Hennis
c76f7029ac Merge remote-tracking branch 'XRPLF/develop' into ximinez/online-delete-gaps
* XRPLF/develop:
  test: add more tests for `ledger_entry` RPC (5858)
  refactor: Rename `rippled.cfg` to `xrpld.cfg` (6098)
  Revert "chore: Pin ruamel.yaml<0.19 in pre-commit-hooks (6166)" (6167)
  chore: Pin ruamel.yaml<0.19 in pre-commit-hooks (6166)
  fix: Remove cryptographic libs from libxrpl Conan package (6163)
2026-01-06 11:27:23 -05:00
Ed Hennis
d535c5fb2a Merge branch 'develop' into ximinez/online-delete-gaps 2025-12-22 17:39:25 -05:00
Ed Hennis
54f860463e Merge branch 'develop' into ximinez/online-delete-gaps 2025-12-18 18:15:15 -05:00
Ed Hennis
950434b8ff Merge branch 'develop' into ximinez/online-delete-gaps 2025-12-17 12:12:39 -05:00
Ed Hennis
ee365e876d Merge branch 'develop' into ximinez/online-delete-gaps 2025-12-12 20:34:29 -05:00
Ed Hennis
c6c59834b9 Update View info() to header() 2025-12-12 15:35:07 -05:00
Ed Hennis
63b47914b8 Merge remote-tracking branch 'XRPLF/develop' into ximinez/online-delete-gaps
* XRPLF/develop:
  refactor: Rename `ripple` namespace to `xrpl` (5982)
  refactor: Move JobQueue and related classes into xrpl.core module (6121)
  refactor: Rename `rippled` binary to `xrpld` (5983)
2025-12-11 15:25:43 -05:00
Ed Hennis
9e02e5be2e Merge branch 'develop' into ximinez/online-delete-gaps 2025-12-10 18:55:14 -05:00
Ed Hennis
093cd70fa1 Merge branch 'develop' into ximinez/online-delete-gaps 2025-12-05 21:12:43 -05:00
Ed Hennis
376d65a483 Merge branch 'develop' into ximinez/online-delete-gaps 2025-12-02 17:21:39 -05:00
Ed Hennis
a0d9a2458e Merge branch 'develop' into ximinez/online-delete-gaps 2025-12-01 14:40:15 -05:00
Ed Hennis
456f639cf7 Merge branch 'develop' into ximinez/online-delete-gaps 2025-11-28 15:46:19 -05:00
Ed Hennis
2c559ec2f3 Merge branch 'develop' into ximinez/online-delete-gaps 2025-11-27 01:48:33 -05:00
Ed Hennis
619c81f463 Merge branch 'develop' into ximinez/online-delete-gaps 2025-11-26 00:24:50 -05:00
Ed Hennis
f1490df960 Merge branch 'develop' into ximinez/online-delete-gaps 2025-11-25 14:54:38 -05:00
Ed Hennis
7bdf74de98 Merge branch 'develop' into ximinez/online-delete-gaps 2025-11-24 21:48:42 -05:00
Ed Hennis
1743d6fb98 Merge branch 'develop' into ximinez/online-delete-gaps 2025-11-24 21:29:52 -05:00
Ed Hennis
ca7a5bb926 Merge branch 'develop' into ximinez/online-delete-gaps 2025-11-21 12:47:28 -05:00
Ed Hennis
ce8b1a3f1e Merge branch 'develop' into ximinez/online-delete-gaps 2025-11-18 22:39:06 -05:00
Ed Hennis
486fa75a10 Merge branch 'develop' into ximinez/online-delete-gaps 2025-11-15 03:08:18 -05:00
Ed Hennis
f8d68cd3d3 Merge branch 'develop' into ximinez/online-delete-gaps 2025-11-13 12:18:08 -05:00
Ed Hennis
ef7a3f5606 Merge branch 'develop' into ximinez/online-delete-gaps 2025-11-12 13:59:45 -05:00
Ed Hennis
4f84ed7490 Merge branch 'develop' into ximinez/online-delete-gaps 2025-11-10 19:52:42 -05:00
Ed Hennis
d534103131 Merge branch 'develop' into ximinez/online-delete-gaps 2025-11-10 15:34:55 -05:00
Ed Hennis
82dff3c2ce Merge branch 'develop' into ximinez/online-delete-gaps 2025-11-08 22:49:26 -05:00
Ed Hennis
30d73eb5ba Merge branch 'develop' into ximinez/online-delete-gaps 2025-11-06 23:50:23 -05:00
Ed Hennis
1b2754bac2 Merge branch 'develop' into ximinez/online-delete-gaps 2025-11-05 22:23:07 -05:00
Ed Hennis
cf80cafc75 Merge branch 'develop' into ximinez/online-delete-gaps 2025-11-04 18:02:30 -05:00
Ed Hennis
b8897d51de Merge branch 'develop' into ximinez/online-delete-gaps 2025-11-03 18:03:56 -05:00
Ed Hennis
3ff25eeb65 Merge branch 'develop' into ximinez/online-delete-gaps 2025-11-03 12:41:48 -05:00
Ed Hennis
2bbfc4e786 Merge branch 'develop' into ximinez/online-delete-gaps 2025-10-31 13:51:15 -04:00
Ed Hennis
2b1eb052e6 Merge branch 'develop' into ximinez/online-delete-gaps 2025-10-31 12:51:24 -04:00
Ed Hennis
360e214e54 Merge branch 'develop' into ximinez/online-delete-gaps 2025-10-29 14:23:10 -04:00
Ed Hennis
2618afed94 Merge branch 'develop' into ximinez/online-delete-gaps 2025-10-29 13:42:21 -04:00
Ed Hennis
698ba2c788 Merge branch 'develop' into ximinez/online-delete-gaps 2025-10-29 12:54:16 -04:00
Ed Hennis
b614e99588 Merge branch 'develop' into ximinez/online-delete-gaps 2025-10-28 17:38:21 -04:00
Ed Hennis
fe8e4af2fa Merge branch 'develop' into ximinez/online-delete-gaps 2025-10-26 19:12:33 -04:00
Ed Hennis
0a897f1528 Merge branch 'develop' into ximinez/online-delete-gaps 2025-10-23 13:24:25 -04:00
Ed Hennis
cf8a3f5779 Merge branch 'develop' into ximinez/online-delete-gaps 2025-10-22 11:38:49 -04:00
Ed Hennis
db39a39868 Merge branch 'develop' into ximinez/online-delete-gaps 2025-10-21 22:20:04 -04:00
Ed Hennis
37a03d28c2 Merge branch 'develop' into ximinez/online-delete-gaps 2025-10-17 18:21:36 -04:00
Ed Hennis
19d275425a Merge branch 'develop' into ximinez/online-delete-gaps 2025-10-16 13:12:08 -04:00
Ed Hennis
88e9045602 Merge branch 'develop' into ximinez/online-delete-gaps 2025-10-16 10:48:43 -04:00
Ed Hennis
5adbc536b6 Merge branch 'develop' into ximinez/online-delete-gaps 2025-10-10 13:01:32 -04:00
Ed Hennis
e27af94ba9 Merge branch 'develop' into ximinez/online-delete-gaps 2025-10-09 15:14:47 -04:00
Ed Hennis
43fe1e7e9c Merge branch 'develop' into ximinez/online-delete-gaps 2025-10-08 14:21:34 -04:00
Ed Hennis
f456a858c8 Merge branch 'develop' into ximinez/online-delete-gaps 2025-10-02 11:03:07 -04:00
Ed Hennis
084c3aa88e Merge branch 'develop' into ximinez/online-delete-gaps 2025-10-01 18:10:57 -04:00
Ed Hennis
34f9b63921 Merge branch 'develop' into ximinez/online-delete-gaps 2025-10-01 13:14:22 -04:00
Ed Hennis
bd3de79817 Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-30 22:28:53 -04:00
Ed Hennis
304eee2259 Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-29 18:34:43 -04:00
Ed Hennis
9e729b7f59 Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-29 17:37:16 -04:00
Ed Hennis
dd141468c4 Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-29 13:32:03 -04:00
Ed Hennis
933147c21f Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-26 19:25:58 -04:00
Ed Hennis
9201a4f591 Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-26 13:41:27 -04:00
Ed Hennis
5adb1e9b8b Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-26 12:09:09 -04:00
Ed Hennis
4df84d7988 Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-25 13:27:09 -04:00
Bart
cd87c0968b Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-24 09:35:19 +02:00
Ed Hennis
8a8e7c90bf Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-20 15:44:32 -04:00
Ed Hennis
e806069065 Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-18 14:08:23 -04:00
Ed Hennis
ce948cbec0 Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-18 12:26:34 -04:00
Ed Hennis
6ed34b3294 Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-18 11:54:30 -04:00
Ed Hennis
7161a235ca Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-17 10:49:03 -04:00
Ed Hennis
71463810de Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-16 10:46:37 -04:00
Ed Hennis
e997219a85 Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-15 11:13:28 -04:00
Ed Hennis
895cc13fa6 Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-11 10:33:14 -04:00
Ed Hennis
8d3c3ca29a Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-10 18:53:24 -04:00
Ed Hennis
9829553807 Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-09 17:14:20 -04:00
Ed Hennis
e551f9731a Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-08 11:41:46 -04:00
Ed Hennis
fd827bf58b Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-05 17:44:06 -04:00
Ed Hennis
5a3baba34d Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-04 20:24:55 -04:00
Ed Hennis
c78f5b160f Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-04 16:43:48 -04:00
Ed Hennis
485f78761a Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-04 12:26:55 -04:00
Ed Hennis
23cd2f7b21 Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-04 10:14:10 -04:00
Ed Hennis
5753266c43 Merge branch 'develop' into ximinez/online-delete-gaps 2025-09-03 14:04:01 -04:00
Ed Hennis
4722d2607d Merge branch 'develop' into ximinez/online-delete-gaps 2025-08-29 15:52:53 -04:00
Ed Hennis
85b5b4f855 Merge branch 'develop' into ximinez/online-delete-gaps 2025-08-29 10:42:50 -04:00
Ed Hennis
a16f492f0f Merge branch 'develop' into ximinez/online-delete-gaps 2025-08-28 18:17:26 -04:00
Ed Hennis
3633dc632c Merge branch 'develop' into ximinez/online-delete-gaps 2025-08-27 11:14:59 -04:00
Ed Hennis
b3b30c3a86 Merge branch 'develop' into ximinez/online-delete-gaps 2025-08-25 14:10:52 -04:00
Ed Hennis
c78a7684f4 Remove trailing space 2025-08-25 14:09:57 -04:00
Ed Hennis
cf83d92630 Merge remote-tracking branch 'upstream/develop' into ximinez/online-delete-gaps
* upstream/develop:
  chore: Remove codecov token check to support tokenless uploads on forks (5722)
  Set version to 2.6.0-rc3
  Revert "perf: Move mutex to the partition level (5486)"
  chore: Update clang-format and prettier with pre-commit (5709)
  fix(test): handle null metadata for unvalidated tx in Env::meta (5715)
  chore: Workaround for CI build errors on arm64 (5717)
  chore: Fix file formatting (5718)
  fix: Skip notify-clio when running in a fork, reorder config fields (5712)
  chore: Reverts formatting changes to external files, adds formatting changes to proto files (5711)
2025-08-25 14:05:49 -04:00
Ed Hennis
a56b1274d8 Merge branch 'develop' into ximinez/online-delete-gaps 2025-08-21 11:38:54 -04:00
Ed Hennis
ae4bdd0492 Merge branch 'develop' into ximinez/online-delete-gaps 2025-08-19 16:05:17 -04:00
Ed Hennis
e90102dd3b Merge branch 'develop' into ximinez/online-delete-gaps 2025-08-18 12:15:38 -04:00
Ed Hennis
71f0e8db3d Merge branch 'develop' into ximinez/online-delete-gaps 2025-08-08 18:23:24 -04:00
Ed Hennis
638929373a Merge branch 'develop' into ximinez/online-delete-gaps 2025-08-08 11:10:20 -04:00
Ed Hennis
8440654377 Merge branch 'develop' into ximinez/online-delete-gaps 2025-08-06 21:03:00 -04:00
Ed Hennis
9fa66c4741 Merge branch 'develop' into ximinez/online-delete-gaps 2025-08-05 21:16:44 -04:00
Ed Hennis
38a9235145 Merge branch 'develop' into ximinez/online-delete-gaps 2025-08-04 13:04:52 -04:00
Ed Hennis
c7a3cc9108 Merge branch 'develop' into ximinez/online-delete-gaps 2025-07-29 20:33:29 -04:00
Ed Hennis
248337908d Merge branch 'develop' into ximinez/online-delete-gaps 2025-07-29 11:54:16 -04:00
Ed Hennis
3d003619fd Merge branch 'develop' into ximinez/online-delete-gaps 2025-07-28 20:57:13 -04:00
Ed Hennis
f163dca12c Merge branch 'develop' into ximinez/online-delete-gaps 2025-07-24 15:50:09 -04:00
Ed Hennis
6e0ce458e5 Revert "TEMP: Change some logging to fatal to diagnose CI failures"
This reverts commit 69cf18158b.
2025-07-22 19:41:02 -04:00
Ed Hennis
5fae8480f1 Revert "TEMP: Add logging to SHAMapStore test"
This reverts commit fe7d0798a7.
2025-07-22 19:40:58 -04:00
Ed Hennis
e6587d374a fixup! Tweak SHAMapStore test timing more 2025-07-22 19:39:58 -04:00
Ed Hennis
376cc404e0 Tweak SHAMapStore test timing more 2025-07-22 18:44:07 -04:00
Ed Hennis
9898ca638f Merge branch 'develop' into ximinez/online-delete-gaps 2025-07-22 14:09:33 -04:00
Ed Hennis
34b46d8f7c Merge branch 'develop' into ximinez/online-delete-gaps 2025-07-21 18:20:41 -04:00
Ed Hennis
fe7d0798a7 TEMP: Add logging to SHAMapStore test 2025-07-21 18:19:53 -04:00
Ed Hennis
0cecc09d71 Tweak timing of SHAMapStore test 2025-07-21 18:19:28 -04:00
Ed Hennis
e091d55561 Try to fix timing of LedgerMaster test 2025-07-21 15:14:42 -04:00
Ed Hennis
69cf18158b TEMP: Change some logging to fatal to diagnose CI failures 2025-07-21 14:32:08 -04:00
Ed Hennis
6513c53817 Improve logging
- There's still a race condition in there
2025-07-21 14:30:49 -04:00
Ed Hennis
e13baa58a5 Fix build errors 2025-07-21 13:31:09 -04:00
Ed Hennis
951056fe9b Merge branch 'develop' into ximinez/online-delete-gaps 2025-07-18 18:33:10 -04:00
Ed Hennis
67700ea6bd Merge branch 'develop' into ximinez/online-delete-gaps 2025-07-16 12:54:23 -04:00
Ed Hennis
e5442cf3f1 Merge branch 'develop' into ximinez/online-delete-gaps 2025-07-15 19:36:25 -04:00
Ed Hennis
da68076f04 Change default recovery wait time to 1s
See https://github.com/XRPLF/rippled/pull/5531#issuecomment-3058218837
2025-07-14 14:13:32 -04:00
Ed Hennis
b24116a118 Improve locking, logging, and test output
- Add more info to the error message on some failed tests.
- Add logging details to SHAMapStoreImp
2025-07-14 14:13:27 -04:00
Ed Hennis
f67398c6bf Merge branch 'develop' into ximinez/online-delete-gaps 2025-07-11 19:27:31 -04:00
Ed Hennis
43d3eb1a24 Merge branch 'develop' into ximinez/online-delete-gaps 2025-07-10 21:25:22 -04:00
Ed Hennis
0993315ed5 Merge branch 'develop' into ximinez/online-delete-gaps 2025-07-10 12:30:18 -04:00
Ed Hennis
0bc383ada9 Fix build errors 2025-07-08 20:15:05 -04:00
Ed Hennis
1841ceca43 Add more logging to SHAMapStore rotation 2025-07-08 16:05:03 -04:00
Ed Hennis
2714cebabd Revert "TEMP: Change logging to show progress during unit test"
This reverts commit e184db4ce2.
2025-07-08 16:02:45 -04:00
Ed Hennis
e184db4ce2 TEMP: Change logging to show progress during unit test 2025-07-08 16:02:19 -04:00
Ed Hennis
ac6dc6943c Tweak when the starting range of ledger gap detection is set
- Add a test to exercise online delete ledger gap detection
2025-07-08 16:01:23 -04:00
Ed Hennis
ddd53806df Add a test to exercise LedgerMaster::missingFromCompleteLedgerRange 2025-07-08 13:22:47 -04:00
Ed Hennis
e629a1f70e Merge branch 'develop' into ximinez/online-delete-gaps 2025-07-03 15:51:23 -04:00
Ed Hennis
68076d969c fixup! fixup! Pause online delete if there any any gaps in recent ledger history 2025-07-02 19:05:06 -04:00
Ed Hennis
d3009d3e1c fixup! Pause online delete if there any any gaps in recent ledger history 2025-07-02 18:51:42 -04:00
Ed Hennis
54f7f3c894 Pause online delete if there any any gaps in recent ledger history 2025-07-02 18:45:48 -04:00
15 changed files with 929 additions and 157 deletions

View File

@@ -1094,8 +1094,8 @@
# Default is 100.
#
# back_off_milliseconds
# Number of milliseconds to wait between
# online_delete batches to allow other functions
# Number of milliseconds to wait between online_delete
# SQL deletion batches to allow other functions
# to catch up.
# Default is 100.
#
@@ -1109,10 +1109,22 @@
# The online delete process checks periodically
# that xrpld is still in sync with the network,
# and that the validated ledger is less than
# 'age_threshold_seconds' old. If not, then continue
# 'age_threshold_seconds' old, and that all
# recent ledgers are available. If not, then continue
# sleeping for this number of seconds and
# checking until healthy.
# Default is 5.
# Default is 2.
#
# max_waiting_ledgers
# The maximum number of ledgers that may be validated
# while online deletion is waiting for the node to get
# fully synced with the rest of the network. If more than
# this number of ledgers are validated while waiting, then
# online deletion gives up on the current ledger and tries
# again later. Note this only affects situations that cause
# rotation to wait, such as going out of sync, or missing
# ledgers. Forward progress is not penalized. Minimum is 64.
# Default is the online_delete value.
#
# Notes:
# The 'node_db' entry configures the primary, persistent storage.

View File

@@ -125,6 +125,7 @@ struct Keys
static constexpr auto kMaximumTxnInLedger = "maximum_txn_in_ledger";
static constexpr auto kMaximumTxnPerAccount = "maximum_txn_per_account";
static constexpr auto kMemoryLevel = "memory_level";
static constexpr auto kMaxWaitingLedgers = "max_waiting_ledgers";
static constexpr auto kMinLedgersToComputeSizeLimit = "min_ledgers_to_compute_size_limit";
static constexpr auto kMinimumEscalationMultiplier = "minimum_escalation_multiplier";
static constexpr auto kMinimumLastLedgerBuffer = "minimum_last_ledger_buffer";

View File

@@ -5,6 +5,7 @@
#include <xrpl/nodestore/Database.h>
#include <xrpl/nodestore/Scheduler.h>
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
@@ -54,6 +55,12 @@ public:
*/
virtual void
setRotationInFlight(bool inFlight) = 0;
virtual bool
isRotationInFlight() const = 0;
[[nodiscard]]
virtual std::uint64_t
getAndResetDuplicationCount() = 0;
};
} // namespace xrpl::node_store

View File

@@ -72,19 +72,28 @@ public:
void
setRotationInFlight(bool inFlight) override;
bool
isRotationInFlight() const override;
[[nodiscard]]
std::uint64_t
getAndResetDuplicationCount() override;
private:
std::shared_ptr<Backend> writableBackend_;
std::shared_ptr<Backend> archiveBackend_;
mutable std::mutex mutex_;
// True between SHAMapStore starting the cache-freshen phase and the
// completion of rotate(). While true, archive hits on ordinary
// (duplicate == false) fetches are copied forward into the writable
// backend; copyForwardCount_ tallies them per rotation for the
// Set to true during the entire SHAMapStore rotation process.
// While true, archive hits on ordinary (duplicate == false)
// fetches are copied forward into the writable backend.
// copyForwardCount_ tallies them per rotation for the
// summary line logged at swap.
std::atomic<bool> rotationInFlight_{false};
std::atomic<std::uint64_t> copyForwardCount_{0};
// Duplication count tracks the number of nodes that are directly duplicated because they're in
// the target ledger or rescued from a cache.
std::atomic<std::uint64_t> duplicationCount_{0};
std::shared_ptr<NodeObject>
fetchNodeObject(uint256 const& hash, std::uint32_t, FetchReport& fetchReport, bool duplicate)

View File

@@ -54,6 +54,7 @@ DatabaseRotatingImp::rotate(
// deleted.
std::shared_ptr<node_store::Backend> oldArchiveBackend;
std::uint64_t copyForwards = 0;
std::uint64_t duplications = 0;
{
std::scoped_lock const lock(mutex_);
@@ -66,6 +67,7 @@ DatabaseRotatingImp::rotate(
writableBackend_ = std::move(newBackend);
copyForwards = copyForwardCount_.exchange(0, std::memory_order_relaxed);
duplications = duplicationCount_.exchange(0, std::memory_order_relaxed);
}
if (copyForwards > 0)
@@ -74,6 +76,11 @@ DatabaseRotatingImp::rotate(
<< " archive-served reads into the writable backend "
"during the rotation window";
}
if (duplications > 0)
{
JLOG(j_.warn()) << "Rotating: duplicated " << duplications
<< " nodes into the writable backend for the relevant cache.";
}
f(newWritableBackendName, newArchiveBackendName);
}
@@ -86,6 +93,22 @@ DatabaseRotatingImp::setRotationInFlight(bool inFlight)
<< (inFlight ? "enabled" : "disabled");
}
bool
DatabaseRotatingImp::isRotationInFlight() const
{
return rotationInFlight_.load(std::memory_order_acquire);
}
[[nodiscard]]
std::uint64_t
DatabaseRotatingImp::getAndResetDuplicationCount()
{
std::uint64_t duplications = 0;
duplications = duplicationCount_.exchange(0, std::memory_order_relaxed);
return duplications;
}
std::string
DatabaseRotatingImp::getName() const
{
@@ -141,7 +164,7 @@ DatabaseRotatingImp::sweep()
std::shared_ptr<NodeObject>
DatabaseRotatingImp::fetchNodeObject(
uint256 const& hash,
std::uint32_t,
std::uint32_t ledgerSeq,
FetchReport& fetchReport,
bool duplicate)
{
@@ -190,22 +213,29 @@ DatabaseRotatingImp::fetchNodeObject(
nodeObject = fetch(archive);
if (nodeObject)
{
{
// Refresh the writable backend pointer
std::scoped_lock const lock(mutex_);
writable = writableBackend_;
}
// Update writable backend with data from the archive backend.
// While a rotation is in flight, ordinary (duplicate == false)
// reads served by the archive are copied forward too: the
// archive is about to be deleted, and a body canonicalized
// into the cache after the freshen getKeys() snapshot would
// otherwise survive only in RAM once the archive is dropped.
if (duplicate || rotationInFlight_.load(std::memory_order_acquire))
auto const inFlight = isRotationInFlight();
if (duplicate || inFlight)
{
if (!duplicate)
{
// Refresh the writable backend pointer since we need to use it
std::scoped_lock const lock(mutex_);
writable = writableBackend_;
}
if (duplicate)
{
duplicationCount_.fetch_add(1, std::memory_order_relaxed);
}
else
{
copyForwardCount_.fetch_add(1, std::memory_order_relaxed);
}
writable->store(nodeObject);
}
}

View File

@@ -5,17 +5,21 @@
#include <test/jtx/noop.h>
#include <xrpld/app/ledger/LedgerMaster.h>
#include <xrpld/app/misc/SHAMapStore.h>
#include <xrpld/core/Config.h>
#include <xrpl/basics/ToString.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/STTx.h>
#include <cstdint>
#include <memory>
#include <sstream>
#include <vector>
namespace xrpl::test {
@@ -111,6 +115,71 @@ class LedgerMaster_test : public beast::unit_test::Suite
}
}
void
testCompleteLedgerRange(FeatureBitset features)
{
// Note that this test is intentionally very similar to
// SHAMapStore_test::testLedgerGaps, but has a different
// focus.
testcase("Complete Ledger operations");
using namespace test::jtx;
auto const deleteInterval = 8;
Env env{*this, envconfig(onlineDelete, deleteInterval)};
auto const alice = Account("alice");
env.fund(XRP(1000), alice);
env.close();
auto& lm = env.app().getLedgerMaster();
LedgerIndex minSeq = 2;
LedgerIndex maxSeq = env.closed()->header().seq;
auto& store = env.app().getSHAMapStore();
BEAST_EXPECT(store.rendezvous());
LedgerIndex lastRotated = store.getLastRotated();
BEAST_EXPECTS(maxSeq == 3, to_string(maxSeq));
BEAST_EXPECTS(lm.getCompleteLedgers() == "2-3", lm.getCompleteLedgers());
BEAST_EXPECTS(lastRotated == 3, to_string(lastRotated));
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq, maxSeq) == 0);
BEAST_EXPECT(minSeq + 1 > maxSeq - 1);
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq - 1, maxSeq + 1) == 2);
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq - 2, maxSeq - 2) == 2);
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq + 2, maxSeq + 2) == 2);
// Close enough ledgers to rotate a few times
for (int i = 0; i < 24; ++i)
{
for (int t = 0; t < 3; ++t)
{
env(noop(alice));
}
env.close();
BEAST_EXPECT(store.rendezvous());
++maxSeq;
if (maxSeq == lastRotated + deleteInterval)
{
minSeq = lastRotated;
lastRotated = maxSeq;
}
BEAST_EXPECTS(
env.closed()->header().seq == maxSeq, to_string(env.closed()->header().seq));
BEAST_EXPECTS(store.getLastRotated() == lastRotated, to_string(store.getLastRotated()));
std::stringstream expectedRange;
expectedRange << minSeq << "-" << maxSeq;
BEAST_EXPECTS(lm.getCompleteLedgers() == expectedRange.str(), lm.getCompleteLedgers());
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq, maxSeq) == 0);
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq + 1, maxSeq - 1) == 0);
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq - 1, maxSeq + 1) == 2);
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq - 2, maxSeq - 2) == 2);
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq + 2, maxSeq + 2) == 2);
}
}
public:
void
run() override
@@ -124,6 +193,7 @@ public:
testWithFeats(FeatureBitset features)
{
testTxnIdFromIndex(features);
testCompleteLedgerRange(features);
}
};

View File

@@ -1,7 +1,9 @@
#include <test/jtx/Env.h>
#include <test/jtx/amount.h>
#include <test/jtx/envconfig.h>
#include <test/jtx/noop.h>
#include <xrpld/app/ledger/LedgerMaster.h>
#include <xrpld/app/main/Application.h>
#include <xrpld/app/main/NodeStoreScheduler.h>
#include <xrpld/app/misc/SHAMapStore.h>
@@ -22,16 +24,21 @@
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/server/NetworkOPs.h>
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <filesystem>
#include <limits>
#include <map>
#include <memory>
#include <optional>
#include <sstream>
#include <string>
#include <thread>
#include <utility>
#include <vector>
namespace xrpl::test {
@@ -42,9 +49,8 @@ class SHAMapStore_test : public beast::unit_test::Suite
static auto
onlineDelete(std::unique_ptr<Config> cfg)
{
cfg->ledgerHistory = kDeleteInterval;
auto& section = cfg->section(Sections::kNodeDatabase);
section.set(Keys::kOnlineDelete, std::to_string(kDeleteInterval));
cfg = jtx::onlineDelete(std::move(cfg), kDeleteInterval);
cfg->section(Sections::kNodeDatabase).set(Keys::kRecoveryWaitSeconds, "1");
return cfg;
}
@@ -143,11 +149,11 @@ class SHAMapStore_test : public beast::unit_test::Suite
auto& store = env.app().getSHAMapStore();
int ledgerSeq = 3;
store.rendezvous();
BEAST_EXPECT(store.rendezvous());
BEAST_EXPECT(!store.getLastRotated());
env.close();
store.rendezvous();
BEAST_EXPECT(store.rendezvous());
auto ledger = env.rpc("ledger", "validated");
BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq++)));
@@ -227,7 +233,7 @@ public:
BEAST_EXPECT(goodLedger(env, ledger, std::to_string(kDeleteInterval + 4)));
}
store.rendezvous();
BEAST_EXPECT(store.rendezvous());
BEAST_EXPECT(store.getLastRotated() == kDeleteInterval + 3);
lastRotated = store.getLastRotated();
@@ -254,7 +260,7 @@ public:
!getHash(ledgers[i]).empty());
}
store.rendezvous();
BEAST_EXPECT(store.rendezvous());
BEAST_EXPECT(store.getLastRotated() == kDeleteInterval + lastRotated);
@@ -292,7 +298,7 @@ public:
BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq), true));
}
store.rendezvous();
BEAST_EXPECT(store.rendezvous());
// The database will always have back to ledger 2,
// regardless of lastRotated.
@@ -307,7 +313,7 @@ public:
BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq++), true));
}
store.rendezvous();
BEAST_EXPECT(store.rendezvous());
ledgerCheck(env, ledgerSeq - lastRotated, lastRotated);
BEAST_EXPECT(lastRotated != store.getLastRotated());
@@ -323,7 +329,7 @@ public:
BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq), true));
}
store.rendezvous();
BEAST_EXPECT(store.rendezvous());
ledgerCheck(env, kDeleteInterval + 1, lastRotated);
BEAST_EXPECT(lastRotated != store.getLastRotated());
@@ -362,7 +368,7 @@ public:
BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq), true));
}
store.rendezvous();
BEAST_EXPECT(store.rendezvous());
ledgerCheck(env, ledgerSeq - 2, 2);
BEAST_EXPECT(lastRotated == store.getLastRotated());
@@ -372,7 +378,7 @@ public:
BEAST_EXPECT(!rpc::containsError(canDelete[jss::result]));
BEAST_EXPECT(canDelete[jss::result][jss::can_delete] == ledgerSeq + (kDeleteInterval / 2));
store.rendezvous();
BEAST_EXPECT(store.rendezvous());
ledgerCheck(env, ledgerSeq - 2, 2);
BEAST_EXPECT(store.getLastRotated() == lastRotated);
@@ -385,7 +391,7 @@ public:
BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq++), true));
}
store.rendezvous();
BEAST_EXPECT(store.rendezvous());
ledgerCheck(env, ledgerSeq - lastRotated, lastRotated);
@@ -401,7 +407,7 @@ public:
BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq), true));
}
store.rendezvous();
BEAST_EXPECT(store.rendezvous());
BEAST_EXPECT(store.getLastRotated() == lastRotated);
@@ -413,7 +419,7 @@ public:
BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq++), true));
}
store.rendezvous();
BEAST_EXPECT(store.rendezvous());
ledgerCheck(env, ledgerSeq - firstBatch, firstBatch);
@@ -435,7 +441,7 @@ public:
BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq), true));
}
store.rendezvous();
BEAST_EXPECT(store.rendezvous());
BEAST_EXPECT(store.getLastRotated() == lastRotated);
@@ -447,7 +453,7 @@ public:
BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq++), true));
}
store.rendezvous();
BEAST_EXPECT(store.rendezvous());
ledgerCheck(env, ledgerSeq - lastRotated, lastRotated);
@@ -468,7 +474,7 @@ public:
BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq), true));
}
store.rendezvous();
BEAST_EXPECT(store.rendezvous());
BEAST_EXPECT(store.getLastRotated() == lastRotated);
@@ -480,7 +486,7 @@ public:
BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq++), true));
}
store.rendezvous();
BEAST_EXPECT(store.rendezvous());
ledgerCheck(env, ledgerSeq - lastRotated, lastRotated);
@@ -603,6 +609,302 @@ public:
BEAST_EXPECT(dbr->getName() == "3");
}
void
testLedgerGaps()
{
// Note that this test is intentionally very similar to
// LedgerMaster_test::testCompleteLedgerRange, but has a different
// focus.
testcase("Wait for ledger gaps to fill in");
using namespace test::jtx;
Env env{*this, envconfig(onlineDelete)};
auto failureMessage = [&](char const* label, auto expected, auto actual) {
std::stringstream ss;
ss << label << ": Expected: " << expected << ", Got: " << actual;
return ss.str();
};
auto const alice = Account("alice");
env.fund(XRP(1000), alice);
env.close();
auto& lm = env.app().getLedgerMaster();
LedgerIndex minSeq = 2;
LedgerIndex maxSeq = env.closed()->header().seq;
auto& store = env.app().getSHAMapStore();
LedgerIndex lastRotated = store.getLastRotated();
auto& netOPs = env.app().getOPs();
while (lastRotated != 3)
{
BEAST_EXPECT(store.rendezvous());
lastRotated = store.getLastRotated();
}
BEAST_EXPECTS(maxSeq == 3, std::to_string(maxSeq));
BEAST_EXPECTS(lm.getCompleteLedgers() == "2-3", lm.getCompleteLedgers());
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq, maxSeq) == 0);
BEAST_EXPECT(minSeq + 1 > maxSeq - 1);
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq - 1, maxSeq + 1) == 2);
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq - 2, maxSeq - 2) == 2);
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq + 2, maxSeq + 2) == 2);
auto expectedRange =
[](LedgerIndex minSeq, std::vector<LedgerIndex> const& deleteSeqs, LedgerIndex maxSeq) {
std::stringstream expectedRange;
expectedRange << minSeq;
auto lastDelete = minSeq - 1;
for (auto deleteSeq : deleteSeqs)
{
if (deleteSeq <= lastDelete)
continue;
expectedRange << "-" << (deleteSeq - 1);
if (deleteSeq + 1 <= maxSeq)
expectedRange << "," << (deleteSeq + 1);
lastDelete = deleteSeq;
}
if (lastDelete + 1 < maxSeq)
{
expectedRange << "-" << maxSeq;
}
return expectedRange.str();
};
auto deleteLedgerSeq =
[&lm, &store, &netOPs, &minSeq, &lastRotated, &expectedRange, &failureMessage, this](
Env& env,
LedgerIndex& maxSeq,
std::vector<LedgerIndex>& deleteSeqs) -> LedgerIndex {
using namespace std::chrono_literals;
// The next ledger will trigger a rotation. Delete the
// current ledger from LedgerMaster.
netOPs.setMode(OperatingMode::CONNECTED);
LedgerIndex const deleteSeq = maxSeq;
std::size_t iterations = 30;
while (!lm.haveLedger(deleteSeq) && --iterations > 0)
{
std::this_thread::sleep_for(10ms);
}
// Even the slowest machines should be able to finalize deleteSeq within 10
// loops (100ms). If this test ever actually fails feel free to lower this
// cutoff. The intent of this test is to flag if the loop takes a very long
// time, but still allow the rest of this function to finish.
BEAST_EXPECTS(iterations > 20, std::to_string(iterations));
if (!BEAST_EXPECT(lm.haveLedger(deleteSeq)))
return 0;
// This test may be timing sensitive, because it's messing with server internals in ways
// that they can't be messed with normally. Sleep a little bit to give the server time
// to finish any internal work before we delete the ledger.
std::this_thread::sleep_for(250ms);
lm.clearLedger(deleteSeq);
deleteSeqs.push_back(deleteSeq);
if (!BEAST_EXPECT(!lm.haveLedger(deleteSeq)))
return 0;
BEAST_EXPECTS(
lm.getCompleteLedgers() == expectedRange(minSeq, deleteSeqs, maxSeq),
failureMessage(
"Complete ledgers",
expectedRange(minSeq, deleteSeqs, maxSeq),
lm.getCompleteLedgers()));
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq, maxSeq) == deleteSeqs.size());
if (!BEAST_EXPECT(!lm.haveLedger(deleteSeq)))
return 0;
// Close another ledger, which will trigger a rotation, but the
// rotation will be stuck until the missing ledger is filled in.
env.close();
// Do not call rendezvous() here without a timeout; it will block until the missing
// ledger is backfilled. That will not happen automatically. It's a manual step that
// is done later in this test.
++maxSeq;
if (!BEAST_EXPECT(!lm.haveLedger(deleteSeq)))
return 0;
netOPs.setMode(OperatingMode::FULL);
if (!BEAST_EXPECT(!lm.haveLedger(deleteSeq)))
return 0;
BEAST_EXPECT(!store.rendezvous(10ms));
BEAST_EXPECT(netOPs.getOperatingMode() == OperatingMode::FULL);
// Nothing has changed
BEAST_EXPECTS(
store.getLastRotated() == lastRotated,
failureMessage("lastRotated", lastRotated, store.getLastRotated()));
BEAST_EXPECTS(
lm.getCompleteLedgers() == expectedRange(minSeq, deleteSeqs, maxSeq),
failureMessage(
"Complete ledgers",
expectedRange(minSeq, deleteSeqs, maxSeq),
lm.getCompleteLedgers()));
return deleteSeq;
};
std::vector<LedgerIndex> deleteSeqs;
// Close enough ledgers to rotate a few times
while (maxSeq < 40)
{
for (int t = 0; t < 3; ++t)
{
env(noop(alice));
}
env.close();
BEAST_EXPECT(store.rendezvous());
++maxSeq;
if (maxSeq + 1 == lastRotated + kDeleteInterval)
{
using namespace std::chrono_literals;
{
// Trigger the circuit breaker in SHAMapStoreImp::healthWait() to ensure it
// doesn't block forever.
LedgerIndex const deleteSeq = deleteLedgerSeq(env, maxSeq, deleteSeqs);
if (!BEAST_EXPECT(deleteSeq > 0))
return;
if (!BEAST_EXPECT(!lm.haveLedger(deleteSeq)))
return;
// Close 7 more ledgers, waiting a little bit in between to
// simulate the ledger making progress while online delete waits
// for the missing ledger to be filled in.
// After the 7th ledger, the circuit breaker will trigger and abort the attempt.
while (maxSeq < lastRotated + (kDeleteInterval * 2) - 2)
{
env.close();
++maxSeq;
// Nothing has changed
BEAST_EXPECTS(
store.getLastRotated() == lastRotated,
failureMessage("lastRotated", lastRotated, store.getLastRotated()));
BEAST_EXPECTS(
lm.getCompleteLedgers() == expectedRange(minSeq, deleteSeqs, maxSeq),
failureMessage(
"Complete Ledgers",
expectedRange(minSeq, deleteSeqs, maxSeq),
lm.getCompleteLedgers()));
// The Store is "stuck" in healthWait() and won't finish the run() loop
// until it's backfilled
if (!BEAST_EXPECT(!lm.haveLedger(deleteSeq)))
return;
}
// Close one more ledger, which will NOT trigger the circuit breaker. Wait for
// the full 1 second recovery wait timeout to ensure the circuit breaker is not
// triggered.
env.close();
++maxSeq;
// The Store is "stuck" in healthWait() and won't finish the run() loop
// until it's backfilled
BEAST_EXPECT(!store.rendezvous(1s));
// Close one more ledger, which will trigger the circuit breaker and abort the
// attempt to rotate.
env.close();
++maxSeq;
// Nothing has changed
BEAST_EXPECTS(
store.getLastRotated() == lastRotated,
failureMessage("lastRotated", lastRotated, store.getLastRotated()));
BEAST_EXPECTS(
lm.getCompleteLedgers() == expectedRange(minSeq, deleteSeqs, maxSeq),
failureMessage(
"Complete Ledgers",
expectedRange(minSeq, deleteSeqs, maxSeq),
lm.getCompleteLedgers()));
// The circuit breaker has been triggered.
BEAST_EXPECT(store.rendezvous());
}
{
// Recover before the circuit breaker triggers, so the test can continue.
LedgerIndex const deleteSeq = deleteLedgerSeq(env, maxSeq, deleteSeqs);
if (!BEAST_EXPECT(deleteSeq > 0))
return;
if (!BEAST_EXPECT(!lm.haveLedger(deleteSeq)))
return;
// Close 5 more ledgers, waiting a little bit in between to
// simulate the ledger making progress while online delete waits
// for the missing ledger to be filled in.
// This ensures the healthWait check has time to run and
// detect the gap.
for (int l = 0; l < 5; ++l)
{
env.close();
++maxSeq;
// Nothing has changed
BEAST_EXPECTS(
store.getLastRotated() == lastRotated,
failureMessage("lastRotated", lastRotated, store.getLastRotated()));
BEAST_EXPECTS(
lm.getCompleteLedgers() == expectedRange(minSeq, deleteSeqs, maxSeq),
failureMessage(
"Complete Ledgers",
expectedRange(minSeq, deleteSeqs, maxSeq),
lm.getCompleteLedgers()));
if (!BEAST_EXPECT(!lm.haveLedger(deleteSeq)))
return;
}
// The Store is "stuck" in healthWait() and won't finish the run() loop
// until it's backfilled
// Wait for the full 1 second recovery wait timeout to ensure the circuit
// breaker is not triggered, and this isn't some other timing fluke.
BEAST_EXPECT(!store.rendezvous(1s));
// Put the missing ledger back in LedgerMaster
lm.setLedgerRangePresent(deleteSeq, deleteSeq);
BEAST_EXPECT(deleteSeqs.back() == deleteSeq);
deleteSeqs.pop_back();
// Wait for the rotation to finish
BEAST_EXPECT(store.rendezvous());
minSeq = lastRotated;
while (deleteSeqs.front() < minSeq)
{
deleteSeqs.erase(deleteSeqs.begin());
}
lastRotated = deleteSeq + 1;
}
}
BEAST_EXPECT(maxSeq != lastRotated + kDeleteInterval);
BEAST_EXPECTS(
env.closed()->header().seq == maxSeq,
failureMessage("maxSeq", maxSeq, env.closed()->header().seq));
BEAST_EXPECTS(
store.getLastRotated() == lastRotated,
failureMessage("lastRotated", lastRotated, store.getLastRotated()));
{
auto const expected = expectedRange(minSeq, deleteSeqs, maxSeq);
BEAST_EXPECTS(
lm.getCompleteLedgers() == expected,
failureMessage("CompleteLedgers", expected, lm.getCompleteLedgers()));
}
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq, maxSeq) == deleteSeqs.size());
BEAST_EXPECT(
lm.missingFromCompleteLedgerRange(minSeq + 1, maxSeq - 1) == deleteSeqs.size());
BEAST_EXPECT(
lm.missingFromCompleteLedgerRange(minSeq - 1, maxSeq + 1) == deleteSeqs.size() + 2);
BEAST_EXPECT(
lm.missingFromCompleteLedgerRange(minSeq - 2, maxSeq - 2) == deleteSeqs.size() + 2);
BEAST_EXPECT(
lm.missingFromCompleteLedgerRange(minSeq + 2, maxSeq + 2) == deleteSeqs.size() + 2);
}
}
void
run() override
{
@@ -610,6 +912,7 @@ public:
testAutomatic();
testCanDelete();
testRotate();
testLedgerGaps();
}
};

View File

@@ -3,6 +3,7 @@
#include <xrpld/core/Config.h>
#include <atomic>
#include <cstdint>
#include <map>
#include <memory>
#include <string>
@@ -62,6 +63,19 @@ envconfig(F&& modfunc, Args&&... args)
return modfunc(envconfig(), std::forward<Args>(args)...);
}
/**
* @brief adjust config to enable online_delete
*
* @param cfg config instance to be modified
*
* @param deleteInterval how many new ledgers should be available before
* rotating. Defaults to 8, because the standalone minimum is 8.
*
* @return unique_ptr to Config instance
*/
std::unique_ptr<Config>
onlineDelete(std::unique_ptr<Config> cfg, std::uint32_t deleteInterval = 8);
/**
* @brief adjust config so no admin ports are enabled
*

View File

@@ -7,8 +7,10 @@
#include <xrpl/config/Constants.h>
#include <atomic>
#include <cstdint>
#include <map>
#include <memory>
#include <string>
#include <vector>
namespace xrpl::test {
@@ -60,6 +62,15 @@ setupConfigForUnitTests(Config& cfg)
namespace jtx {
std::unique_ptr<Config>
onlineDelete(std::unique_ptr<Config> cfg, std::uint32_t deleteInterval)
{
cfg->ledgerHistory = deleteInterval;
auto& section = cfg->section(Sections::kNodeDatabase);
section.set(Keys::kOnlineDelete, std::to_string(deleteInterval));
return cfg;
}
std::unique_ptr<Config>
noAdmin(std::unique_ptr<Config> cfg)
{

View File

@@ -123,7 +123,10 @@ public:
failedSave(std::uint32_t seq, uint256 const& hash);
std::string
getCompleteLedgers();
getCompleteLedgers() const;
std::size_t
missingFromCompleteLedgerRange(LedgerIndex first, LedgerIndex last) const;
/**
* Apply held transactions to the open ledger
@@ -190,7 +193,7 @@ public:
fixMismatch(ReadView const& ledger);
bool
haveLedger(std::uint32_t seq);
haveLedger(std::uint32_t seq) const;
void
clearLedger(std::uint32_t seq);
bool
@@ -348,7 +351,7 @@ private:
// A set of transactions to replay during the next close
std::unique_ptr<LedgerReplay> replayData_;
std::recursive_mutex completeLock_;
std::recursive_mutex mutable completeLock_;
RangeSet<std::uint32_t> completeLedgers_;
// Publish thread is running.

View File

@@ -57,6 +57,7 @@
#include <xrpl/shamap/SHAMapMissingNode.h>
#include <xrpl/shamap/SHAMapTreeNode.h>
#include <boost/icl/concept/interval_associator.hpp>
#include <boost/icl/concept/interval_set.hpp>
#include <xrpl.pb.h>
@@ -492,7 +493,7 @@ LedgerMaster::setBuildingLedger(LedgerIndex i)
}
bool
LedgerMaster::haveLedger(std::uint32_t seq)
LedgerMaster::haveLedger(std::uint32_t seq) const
{
std::scoped_lock const sl(completeLock_);
return boost::icl::contains(completeLedgers_, seq);
@@ -1576,12 +1577,36 @@ LedgerMaster::getPublishedLedger()
}
std::string
LedgerMaster::getCompleteLedgers()
LedgerMaster::getCompleteLedgers() const
{
std::scoped_lock const sl(completeLock_);
return to_string(completeLedgers_);
}
std::size_t
LedgerMaster::missingFromCompleteLedgerRange(LedgerIndex first, LedgerIndex last) const
{
if (first > last)
{
// In expected usage, this will never happen because "first" is generally initialized to
// "last", "last" is guaranteed to grow monotonically, and "first" either doesn't change
// or grows more slowly.
// LCOV_EXCL_START
UNREACHABLE("xrpl::LedgerMaster::missingFromCompleteLedgerRange : invalid parameters");
return 0;
// LCOV_EXCL_STOP
}
RangeSet<LedgerIndex> const target{range(first, last)};
auto const missing = [&target, this] {
std::scoped_lock const sl(completeLock_);
return target - completeLedgers_;
}();
return boost::icl::size(missing);
}
std::optional<NetClock::time_point>
LedgerMaster::getCloseTimeBySeq(LedgerIndex ledgerIndex)
{

View File

@@ -8,6 +8,7 @@
#include <xrpl/nodestore/Scheduler.h>
#include <xrpl/protocol/Protocol.h>
#include <chrono>
#include <cstdint>
#include <memory>
#include <optional>
@@ -34,8 +35,8 @@ public:
virtual void
start() = 0;
virtual void
rendezvous() const = 0;
[[nodiscard]] virtual bool
rendezvous(std::optional<std::chrono::milliseconds> const& timeout = {}) const = 0;
virtual void
stop() = 0;

View File

@@ -9,6 +9,7 @@
#include <xrpl/basics/FileUtilities.h>
#include <xrpl/basics/Log.h>
#include <xrpl/basics/contract.h>
#include <xrpl/basics/scope.h>
#include <xrpl/beast/core/CurrentThreadName.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/instrumentation.h>
@@ -30,6 +31,8 @@
#include <boost/algorithm/string/predicate.hpp>
#include <algorithm>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <filesystem>
#include <functional>
@@ -127,22 +130,6 @@ SHAMapStoreImp::SHAMapStoreImp(
if (deleteInterval_ != 0u)
{
// Configuration that affects the behavior of online delete
getIfExists(section, Keys::kDeleteBatch, deleteBatch_);
std::uint32_t temp = 0;
if (getIfExists(section, Keys::kBackOffMilliseconds, temp) ||
// Included for backward compatibility with an undocumented setting
getIfExists(section, Keys::kBackOff, temp))
{
backOff_ = std::chrono::milliseconds{temp};
}
if (getIfExists(section, Keys::kAgeThresholdSeconds, temp))
ageThreshold_ = std::chrono::seconds{temp};
if (getIfExists(section, Keys::kRecoveryWaitSeconds, temp))
recoveryWaitTime_ = std::chrono::seconds{temp};
getIfExists(section, Keys::kAdvisoryDelete, advisoryDelete_);
auto const minInterval =
config.standalone() ? kMinimumDeletionIntervalSa : kMinimumDeletionInterval;
if (deleteInterval_ < minInterval)
@@ -159,6 +146,40 @@ SHAMapStoreImp::SHAMapStoreImp(
std::to_string(config.ledgerHistory) + ")");
}
// Configuration that affects the behavior of online delete
getIfExists(section, Keys::kDeleteBatch, deleteBatch_);
std::uint32_t temp = 0;
if (getIfExists(section, Keys::kBackOffMilliseconds, temp) ||
// Included for backward compatibility with an undocumented setting
getIfExists(section, Keys::kBackOff, temp))
{
backOff_ = std::chrono::milliseconds{temp};
}
if (getIfExists(section, Keys::kAgeThresholdSeconds, temp))
ageThreshold_ = std::chrono::seconds{temp};
if (getIfExists(section, Keys::kRecoveryWaitSeconds, temp))
recoveryWaitTime_ = std::chrono::seconds{temp};
if (recoveryWaitTime_ < std::chrono::seconds{1})
Throw<std::runtime_error>("recovery_wait_seconds must be at least 1 second");
getIfExists(section, Keys::kAdvisoryDelete, advisoryDelete_);
if (getIfExists(section, Keys::kMaxWaitingLedgers, temp))
{
maxWaitingLedgers_ = temp;
}
else
{
maxWaitingLedgers_ = deleteInterval_;
}
auto const minWaiting = minInterval / 4;
if (maxWaitingLedgers_ < minWaiting)
{
Throw<std::runtime_error>(
"max_waiting_ledgers must be at least " + std::to_string(minWaiting));
}
stateDb_.init(config, dbName_);
dbPaths();
}
@@ -235,14 +256,22 @@ SHAMapStoreImp::onLedgerClosed(std::shared_ptr<Ledger const> const& ledger)
cond_.notify_one();
}
void
SHAMapStoreImp::rendezvous() const
[[nodiscard]]
bool
SHAMapStoreImp::rendezvous(std::optional<std::chrono::milliseconds> const& timeout) const
{
if (!working_)
return;
return true;
auto notWorking = [&] { return !working_; };
std::unique_lock<std::mutex> lock(mutex_);
rendezvous_.wait(lock, [&] { return !working_; });
if (timeout)
{
return rendezvous_.wait_for(lock, *timeout, notWorking);
}
rendezvous_.wait(lock, notWorking);
return true;
}
int
@@ -251,31 +280,80 @@ SHAMapStoreImp::fdRequired() const
return fdRequired_;
}
void
SHAMapStoreImp::rescueNode(SHAMapTreeNode const& node, std::optional<NodeObjectType> expectedType)
{
XRPL_ASSERT(node.cowid() == 0, "SHAMapStoreImp::rescueNode : rescued node must be clean");
// Reachable from the validated state map in memory, but present in
// neither backend: its only on-disk copy lived in a backend removed by
// an earlier rotation, and it was never rewritten because it is clean
// (cowid == 0, so flushDirty skips it). Persist the in-memory body
// directly into the writable backend so it survives this rotation
// instead of later surfacing as an unresolvable SHAMapMissingNode.
auto const nodeType = node.getType();
auto const objectType = std::invoke([nodeType, expectedType] {
switch (nodeType)
{
case SHAMapNodeType::TnAccountState:
return NodeObjectType::AccountNode;
// We don't expect to see transaction nodes. The check below will prevent writing them.
case SHAMapNodeType::TnTransactionNm:
case SHAMapNodeType::TnTransactionMd:
return NodeObjectType::TransactionNode;
case SHAMapNodeType::TnInner:
return expectedType.value_or(NodeObjectType::Unknown);
default:
return NodeObjectType::Unknown;
}
});
auto const hash = node.getHash().asUInt256();
XRPL_ASSERT_IF(
expectedType,
*expectedType == objectType,
"SHAMapStoreImp::rescueNode : expected node type");
if (objectType != NodeObjectType::AccountNode || (expectedType && *expectedType != objectType))
{
// LCOV_EXCL_START
JLOG(journal_.warn())
<< "rescueNode: unable to re-store node with unsupported/unknown type, hash=" << hash
<< " type=" << static_cast<int>(nodeType);
// We do not expect to see Inner nodes rescued without an expected type. Analysis and
// experimentation so far indicate that it just doesn't happen, specifically in
// freshenCaches. This UNREACHABLE is as much a developer alert as it is a safety check. If
// it does happen, we want to know about it. It won't affect production deployments.
UNREACHABLE("SHAMapStoreImp::rescueNode : unsupported node type");
return;
// LCOV_EXCL_STOP
}
Serializer s;
node.serializeWithPrefix(s);
dbRotating_->store(objectType, std::move(s.modData()), hash, 0);
JLOG(journal_.info()) << "rescueNode: re-stored node missing from both backends, hash=" << hash
<< " type=" << static_cast<int>(nodeType);
}
bool
SHAMapStoreImp::copyNode(std::uint64_t& nodeCount, SHAMapTreeNode const& node)
SHAMapStoreImp::copyNode(
std::uint64_t& nodeCount,
std::uint64_t& rescuedCount,
SHAMapTreeNode const& node)
{
// Copy a single record from node to dbRotating_
auto obj = dbRotating_->fetchNodeObject(
node.getHash().asUInt256(), 0, node_store::FetchType::Synchronous, true);
if (!obj)
{
XRPL_ASSERT(node.cowid() == 0, "SHAMapStoreImp::copyNode : rescued node must be clean");
// Reachable from the validated state map in memory, but present in
// neither backend: its only on-disk copy lived in a backend removed by
// an earlier rotation, and it was never rewritten because it is clean
// (cowid == 0, so flushDirty skips it). Persist the in-memory body
// directly into the writable backend so it survives this rotation
// instead of later surfacing as an unresolvable SHAMapMissingNode.
auto const hash = node.getHash().asUInt256();
Serializer s;
node.serializeWithPrefix(s);
dbRotating_->store(NodeObjectType::AccountNode, std::move(s.modData()), hash, 0);
JLOG(journal_.warn()) << "copyNode: re-stored node missing from both backends, hash="
<< hash << " type=" << static_cast<int>(node.getType());
rescueNode(node, NodeObjectType::AccountNode);
++rescuedCount;
}
if ((++nodeCount % checkHealthInterval_) == 0u)
{
if (healthWait() == HealthResult::Stopping)
if (healthWait() != HealthResult::KeepGoing)
return false;
}
@@ -297,6 +375,11 @@ SHAMapStoreImp::run()
while (true)
{
XRPL_ASSERT(
!dbRotating_->isRotationInFlight(),
"SHAMapStoreImp::run : rotationInFlight_ must be false "
"outside rotation window");
healthy_ = true;
std::shared_ptr<Ledger const> validatedLedger;
@@ -326,30 +409,76 @@ SHAMapStoreImp::run()
stateDb_.setLastRotated(lastRotated);
}
// We're starting a new cycle, so reset back to the default.
lastSuccessfulHealthCheck_ = 0;
bool const readyToRotate = validatedSeq >= lastRotated + deleteInterval_ &&
canDelete_ >= lastRotated - 1 && healthWait() == HealthResult::KeepGoing;
{
// Note that this is set after the healthWait() check, so that we
// don't start the rotation until the validated ledger is fully
// processed. It is not guaranteed to be done at this point. It also
// allows the testLedgerGaps unit test to work.
std::unique_lock<std::mutex> lock(mutex_);
if (newLedger_)
{
// It is possible, though very unlikely outside of tests which manipulate internals,
// that healthWait() took so long that the validated ledger (newLedger_) has moved
// on from where we started. If that's the case, update lastGoodValidatedLedger_
// to that ledger's sequence number.
lastGoodValidatedLedger_ = newLedger_->header().seq;
}
else
{
lastGoodValidatedLedger_ = validatedSeq;
}
auto const l = lastGoodValidatedLedger_;
lock.unlock();
JLOG(journal_.trace()) << "run: Set lastGoodValidatedLedger_ to " << l;
}
// will delete up to (not including) lastRotated
if (readyToRotate)
{
JLOG(journal_.warn()) << "rotating validatedSeq " << validatedSeq << " lastRotated "
<< lastRotated << " deleteInterval " << deleteInterval_
<< " canDelete_ " << canDelete_ << " state "
auto const diff = validatedSeq - lastRotated;
JLOG(journal_.warn()) << "ROTATING: validatedSeq " << validatedSeq << " lastRotated "
<< lastRotated << " diff " << diff << " deleteInterval "
<< deleteInterval_ << " canDelete_ " << canDelete_ << " state "
<< app_.getOPs().strOperatingMode(false) << " age "
<< ledgerMaster_->getValidatedLedgerAge().count() << 's';
<< ledgerMaster_->getValidatedLedgerAge().count()
<< "s. Complete ledgers: " << ledgerMaster_->getCompleteLedgers();
// Close the getKeys()->swap exposure window: from here until
// rotate() completes, an ordinary read for new ledgers served by the archive is
// copied forward into the writable backend, so a node fetched
// from the doomed archive cannot be left RAM-only when the
// archive is deleted. Use ScopeExit so the early returns and continues below (and any
// exceptions) also clear the flag.
ScopeExit const clearRotationInFlight{
[this] { dbRotating_->setRotationInFlight(false); }};
dbRotating_->setRotationInFlight(true);
clearPrior(lastRotated);
if (healthWait() == HealthResult::Stopping)
return;
switch (healthWait())
{
case HealthResult::Stopping:
return;
case HealthResult::Expired:
continue;
case HealthResult::KeepGoing:
break;
}
JLOG(journal_.debug()) << "copying ledger " << validatedSeq;
std::uint64_t nodeCount = 0;
std::uint64_t rescuedCount = 0;
try
{
validatedLedger->stateMap().snapShot(false)->visitNodes(
[this, &nodeCount](SHAMapTreeNode const& node) {
return copyNode(nodeCount, node);
[this, &nodeCount, &rescuedCount](SHAMapTreeNode const& node) {
return copyNode(nodeCount, rescuedCount, node);
});
}
catch (SHAMapMissingNode const& e)
@@ -359,43 +488,53 @@ SHAMapStoreImp::run()
continue;
}
if (healthWait() == HealthResult::Stopping)
return;
// Only log if we completed without a "health" abort
JLOG(journal_.debug())
<< "copied ledger " << validatedSeq << " nodecount " << nodeCount;
// Close the getKeys()->swap exposure window: from here until
// rotate() completes, an ordinary read served by the archive is
// copied forward into the writable backend, so a node fetched
// from the doomed archive cannot be left RAM-only when the
// archive is deleted. RAII so the early returns below (and any
// exception) also clear the flag.
struct RotationExposureGuard
switch (healthWait())
{
node_store::DatabaseRotating& db;
~RotationExposureGuard()
{
db.setRotationInFlight(false);
}
};
RotationExposureGuard const rotationExposureGuard{*dbRotating_};
dbRotating_->setRotationInFlight(true);
case HealthResult::Stopping:
return;
case HealthResult::Expired:
continue;
case HealthResult::KeepGoing:
break;
}
{
// Only log if we completed without a "health" abort
auto const copyDuplications = dbRotating_->getAndResetDuplicationCount();
JLOG(journal_.debug())
<< "copied ledger " << validatedSeq << " duplicated " << copyDuplications
<< " / " << nodeCount << " nodes. Rescued " << rescuedCount << " nodes";
}
JLOG(journal_.debug()) << "freshening caches";
freshenCaches();
if (healthWait() == HealthResult::Stopping)
return;
rescuedCount = 0;
freshenCaches(rescuedCount);
switch (healthWait())
{
case HealthResult::Stopping:
return;
case HealthResult::Expired:
continue;
case HealthResult::KeepGoing:
break;
}
// Only log if we completed without a "health" abort
JLOG(journal_.debug()) << validatedSeq << " freshened caches";
JLOG(journal_.debug())
<< validatedSeq << " freshened caches. Rescued " << rescuedCount << " nodes.";
JLOG(journal_.trace()) << "Making a new backend";
auto newBackend = makeBackendRotating();
JLOG(journal_.debug()) << validatedSeq << " new backend " << newBackend->getName();
clearCaches(validatedSeq);
if (healthWait() == HealthResult::Stopping)
return;
switch (healthWait())
{
case HealthResult::Stopping:
return;
case HealthResult::Expired:
continue;
case HealthResult::KeepGoing:
break;
}
lastRotated = validatedSeq;
@@ -411,7 +550,14 @@ SHAMapStoreImp::run()
clearCaches(validatedSeq);
});
JLOG(journal_.warn()) << "finished rotation " << validatedSeq;
auto const currentValidatedSeq = ledgerMaster_->getValidLedgerIndex();
auto const processingDiff = currentValidatedSeq - validatedSeq;
JLOG(journal_.warn())
<< "FINISHED ROTATION: validatedSeq: " << validatedSeq
<< ", lastRotated: " << lastRotated << " diff " << diff
<< ". Updated validated seq is " << currentValidatedSeq << ", " << processingDiff
<< " ledgers were validated during the rotation processs. Complete ledgers: "
<< ledgerMaster_->getCompleteLedgers();
}
}
}
@@ -559,7 +705,7 @@ SHAMapStoreImp::clearSql(
min = *m;
}
if (min > lastRotated || healthWait() == HealthResult::Stopping)
if (min > lastRotated || healthWait() != HealthResult::KeepGoing)
return;
if (min == lastRotated)
{
@@ -572,18 +718,19 @@ SHAMapStoreImp::clearSql(
<< lastRotated;
while (min < lastRotated)
{
// The very first sleep is, arguably wasted, but clearSql is called multiple times for
// different tables, so the time is amortized among all the operations. This results in
// a backoff in between each set of tables, too.
std::this_thread::sleep_for(backOff_);
if (healthWait() != HealthResult::KeepGoing)
return;
min = std::min(lastRotated, min + deleteBatch_);
JLOG(journal_.trace()) << "Begin: Delete up to " << deleteBatch_
<< " rows with LedgerSeq < " << min << " from: " << tableName;
deleteBeforeSeq(min);
JLOG(journal_.trace()) << "End: Delete up to " << deleteBatch_ << " rows with LedgerSeq < "
<< min << " from: " << tableName;
if (healthWait() == HealthResult::Stopping)
return;
if (min < lastRotated)
std::this_thread::sleep_for(backOff_);
if (healthWait() == HealthResult::Stopping)
return;
}
JLOG(journal_.debug()) << "finished deleting from: " << tableName;
}
@@ -599,12 +746,11 @@ SHAMapStoreImp::clearCaches(LedgerIndex validatedSeq)
}
void
SHAMapStoreImp::freshenCaches()
SHAMapStoreImp::freshenCaches(std::uint64_t& rescuedCount)
{
if (freshenCache(*treeNodeCache_))
return;
if (freshenCache(app_.getMasterTransaction().getCache()))
if (freshenCache(*treeNodeCache_, rescuedCount))
return;
freshenCache(app_.getMasterTransaction().getCache(), rescuedCount);
}
void
@@ -616,7 +762,7 @@ SHAMapStoreImp::clearPrior(LedgerIndex lastRotated)
JLOG(journal_.trace()) << "Begin: Clear internal ledgers up to " << lastRotated;
ledgerMaster_->clearPriorLedgers(lastRotated);
JLOG(journal_.trace()) << "End: Clear internal ledgers up to " << lastRotated;
if (healthWait() == HealthResult::Stopping)
if (healthWait() != HealthResult::KeepGoing)
return;
auto& db = app_.getRelationalDatabase();
@@ -626,7 +772,7 @@ SHAMapStoreImp::clearPrior(LedgerIndex lastRotated)
"Ledgers",
[&db]() -> std::optional<LedgerIndex> { return db.getMinLedgerSeq(); },
[&db](LedgerIndex min) -> void { db.deleteBeforeLedgerSeq(min); });
if (healthWait() == HealthResult::Stopping)
if (healthWait() != HealthResult::KeepGoing)
return;
if (!app_.config().useTxTables())
@@ -637,7 +783,7 @@ SHAMapStoreImp::clearPrior(LedgerIndex lastRotated)
"Transactions",
[&db]() -> std::optional<LedgerIndex> { return db.getTransactionsMinLedgerSeq(); },
[&db](LedgerIndex min) -> void { db.deleteTransactionsBeforeLedgerSeq(min); });
if (healthWait() == HealthResult::Stopping)
if (healthWait() != HealthResult::KeepGoing)
return;
clearSql(
@@ -645,30 +791,135 @@ SHAMapStoreImp::clearPrior(LedgerIndex lastRotated)
"AccountTransactions",
[&db]() -> std::optional<LedgerIndex> { return db.getAccountTransactionsMinLedgerSeq(); },
[&db](LedgerIndex min) -> void { db.deleteAccountTransactionsBeforeLedgerSeq(min); });
if (healthWait() == HealthResult::Stopping)
if (healthWait() != HealthResult::KeepGoing)
return;
}
SHAMapStoreImp::HealthResult
SHAMapStoreImp::healthWait()
{
auto age = ledgerMaster_->getValidatedLedgerAge();
OperatingMode mode = netOPs_->getOperatingMode();
std::unique_lock lock(mutex_);
while (!stop_ && (mode != OperatingMode::FULL || age > ageThreshold_))
{
lock.unlock();
JLOG(journal_.warn()) << "Waiting " << recoveryWaitTime_.count()
<< "s for node to stabilize. state: "
<< app_.getOPs().strOperatingMode(mode, false) << ". age "
<< age.count() << 's';
std::this_thread::sleep_for(recoveryWaitTime_);
// Gets the current status of the server from ledgerMaster_ and netOPs_. Must be called
// while mutex_ is unlocked to avoid unlikely, but possible, deadlock with ledgerMaster_'s
// completeLock_.
// Releasing the lock may mean that status will be slightly out of date when the lock is
// reacquired, but it's close enough. In a normal rotation, healthWait() is called frequently,
// so a false positive will be detected on the next call, and a false negative will be detected
// in the next loop iteration. Database rotation is important, but not timely, so an extra
// delay is fine.
auto readServerStatus = [this](
LedgerIndex& index,
bool& buildingIndex,
std::chrono::seconds& age,
OperatingMode& mode,
std::size_t& numMissing,
LedgerIndex const lowerBound,
ScopeUnlock<decltype(mutex_)> const&) {
index = ledgerMaster_->getValidLedgerIndex();
bool const haveIndex = ledgerMaster_->haveLedger(index);
age = ledgerMaster_->getValidatedLedgerAge();
mode = netOPs_->getOperatingMode();
lock.lock();
numMissing =
lowerBound == 0 ? 0 : ledgerMaster_->missingFromCompleteLedgerRange(lowerBound, index);
buildingIndex = (numMissing == 1 && !haveIndex);
};
// Tracked server status properties
LedgerIndex index = 0;
bool buildingIndex = false;
std::chrono::seconds age;
OperatingMode mode = OperatingMode::DISCONNECTED;
std::size_t numMissing = 0;
std::unique_lock lock(mutex_);
auto const waitTime = recoveryWaitTime_;
auto const ageThreshold = ageThreshold_;
{
auto const lowerBound = lastGoodValidatedLedger_;
ScopeUnlock const unlock(lock);
readServerStatus(index, buildingIndex, age, mode, numMissing, lowerBound, unlock);
}
// If index gets past this point without the health check succeeding, return
// HealthWait::Expired. This depends on index being initialized, so it must be after
// readServerStatus().
auto const lastSuccess = lastSuccessfulHealthCheck_ == 0 ? index : lastSuccessfulHealthCheck_;
auto const circuitBreaker = lastSuccess + maxWaitingLedgers_;
auto healthy = [&] {
// Special case: If the server is disconnected, it's not doing any ledger I/O, because
// it's focused on trying to get peers. A disconnected state is should never be caused by
// the activity of the server. It's usually limited to hardware or connectivity issues. Take
// advantage of that to run as much rotation I/O as possible before it comes back online.
if (mode == OperatingMode::DISCONNECTED)
return true;
if (age > ageThreshold)
return false;
if (numMissing > 0)
return false;
if (mode != OperatingMode::FULL)
return false;
return true;
};
while (!stop_ && !healthy() && index < circuitBreaker)
{
// Future-proofing: this value shouldn't change while we are sleeping, but grab it while we
// have the lock in case it does.
auto const lowerBound = lastGoodValidatedLedger_;
ScopeUnlock const unlock(lock);
auto const [stream, waitMs] = std::invoke(
[mode, age, ageThreshold, buildingIndex, waitTime, index, lastSuccess, this]
-> std::pair<beast::Journal::Stream, std::chrono::milliseconds> {
if (mode != OperatingMode::FULL || age > ageThreshold ||
(index - lastSuccess > maxWaitingLedgers_ / 4))
return {journal_.warn(), waitTime};
if (buildingIndex)
{
// We expect this ledger to be built soon, so log at a lower level, and don't
// wait as long.
return {
journal_.trace(),
std::chrono::duration_cast<std::chrono::milliseconds>(waitTime) / 10};
}
return {journal_.info(), waitTime};
});
JLOG(stream) << "Waiting " << waitMs.count() << "ms for node to stabilize. state: "
<< app_.getOPs().strOperatingMode(mode, false) << ". age " << age.count()
<< "s. Missing ledgers: " << numMissing << ". Expect: " << lowerBound << "-"
<< index << ". Complete ledgers: " << ledgerMaster_->getCompleteLedgers();
std::this_thread::sleep_for(waitMs);
[[maybe_unused]]
LedgerIndex const lastLedger = index;
readServerStatus(index, buildingIndex, age, mode, numMissing, lowerBound, unlock);
SOMETIMES(
index > lastLedger, "SHAMapStoreImp::healthWait : validated ledger index changed");
}
return stop_ ? HealthResult::Stopping : HealthResult::KeepGoing;
auto const result = std::invoke([index, circuitBreaker, this]() -> HealthResult {
if (stop_)
return HealthResult::Stopping;
if (index < circuitBreaker)
return HealthResult::KeepGoing;
JLOG(journal_.error()) << "online_delete rotation has been unable to make progress for "
<< maxWaitingLedgers_ << " ledgers. "
<< "validated ledger index: " << index
<< ", last successful health check index: "
<< lastSuccessfulHealthCheck_
<< ", circuit breaker index: " << circuitBreaker;
return HealthResult::Expired;
});
XRPL_ASSERT(lock.owns_lock(), "SHAMapStoreImp::healthWait : lock held");
if (result == HealthResult::KeepGoing)
lastSuccessfulHealthCheck_ = index;
return result;
}
void

View File

@@ -10,6 +10,7 @@
#include <xrpl/nodestore/Backend.h>
#include <xrpl/nodestore/Database.h>
#include <xrpl/nodestore/DatabaseRotating.h>
#include <xrpl/nodestore/NodeObject.h>
#include <xrpl/nodestore/Scheduler.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/rdb/DatabaseCon.h>
@@ -21,6 +22,7 @@
#include <algorithm>
#include <atomic>
#include <chrono>
#include <concepts>
#include <condition_variable>
#include <cstdint>
#include <functional>
@@ -88,6 +90,13 @@ private:
std::thread thread_;
bool stop_ = false;
bool healthy_ = true;
// Used to prevent ledger gaps from forming during online deletion. Keeps
// track of the last validated ledger that was processed without gaps. There
// are no guarantees about gaps while online delete is not running. For
// that, use advisory_delete and check for gaps externally.
LedgerIndex lastGoodValidatedLedger_ = 0;
// Used to prevent the circuit breaker from tripping too quickly.
LedgerIndex lastSuccessfulHealthCheck_ = 0;
mutable std::condition_variable cond_;
mutable std::condition_variable rendezvous_;
mutable std::mutex mutex_;
@@ -102,12 +111,18 @@ private:
std::chrono::milliseconds backOff_{100};
std::chrono::seconds ageThreshold_{60};
/**
* If the node is out of sync during an online_delete healthWait()
* call, sleep the thread for this time, and continue checking until
* recovery.
* If the node is out of sync, or any recent ledgers are not
* available during an online_delete healthWait() call, sleep
* the thread for this time, and continue checking until recovery.
* See also: "recovery_wait_seconds" in xrpld-example.cfg
*/
std::chrono::seconds recoveryWaitTime_{5};
std::chrono::seconds recoveryWaitTime_{2};
/**
* If the rotation stays "unhealthy" for a very long time, the process is aborted, and tried
* again later. This value represents the number of ledgers that must be validated without
* making rotation progress before the process is aborted.
*/
std::uint32_t maxWaitingLedgers_ = deleteBatch_;
// these do not exist upon SHAMapStore creation, but do exist
// as of run() or before
@@ -163,8 +178,9 @@ public:
void
onLedgerClosed(std::shared_ptr<Ledger const> const& ledger) override;
void
rendezvous() const override;
[[nodiscard]]
bool
rendezvous(std::optional<std::chrono::milliseconds> const& timeout = {}) const override;
int
fdRequired() const override;
@@ -172,9 +188,14 @@ public:
minimumOnline() const override;
private:
// Force write a node to the writable backend during rotation so it doesn't get lost
void
rescueNode(
SHAMapTreeNode const& node,
std::optional<NodeObjectType> expectedType = std::nullopt);
// callback for visitNodes
bool
copyNode(std::uint64_t& nodeCount, SHAMapTreeNode const& node);
copyNode(std::uint64_t& nodeCount, std::uint64_t& rescuedCount, SHAMapTreeNode const& node);
void
run();
void
@@ -185,14 +206,28 @@ private:
template <class CacheInstance>
bool
freshenCache(CacheInstance& cache)
freshenCache(CacheInstance& cache, std::uint64_t& rescuedCount)
{
std::uint64_t check = 0;
for (auto const& key : cache.getKeys())
{
dbRotating_->fetchNodeObject(key, 0, node_store::FetchType::Synchronous, true);
if (!(++check % checkHealthInterval_) && healthWait() == HealthResult::Stopping)
[[maybe_unused]]
auto const obj =
dbRotating_->fetchNodeObject(key, 0, node_store::FetchType::Synchronous, true);
if constexpr (std::derived_from<typename CacheInstance::mapped_type, SHAMapTreeNode>)
{
if (!obj)
{
auto const node = cache.fetch(key);
if (node)
{
rescueNode(*node);
++rescuedCount;
}
}
}
if (!(++check % checkHealthInterval_) && healthWait() != HealthResult::KeepGoing)
return true;
}
@@ -213,18 +248,18 @@ private:
void
clearCaches(LedgerIndex validatedSeq);
void
freshenCaches();
freshenCaches(std::uint64_t& rescuedCount);
void
clearPrior(LedgerIndex lastRotated);
/**
* This is a health check for online deletion that waits until xrpld is
* stable before returning. It returns an indication of whether the server
* is stopping.
* is stopping, or if this attempt should be abandoned.
*
* @return Whether the server is stopping.
*/
enum class HealthResult { Stopping, KeepGoing };
enum class HealthResult { Stopping, Expired, KeepGoing };
[[nodiscard]] HealthResult
healthWait();

View File

@@ -267,9 +267,9 @@ saveValidatedLedger(
app.getAcceptedLedgerCache().canonicalizeReplaceClient(ledger->header().hash, aLedger);
}
}
catch (std::exception const&)
catch (std::exception const& e)
{
JLOG(j.warn()) << "An accepted ledger was missing nodes";
JLOG(j.warn()) << "An accepted ledger was missing nodes " << e.what();
app.getLedgerMaster().failedSave(seq, ledger->header().hash);
// Clients can now trust the database for information about this
// ledger sequence.