Compare commits

..

171 Commits

Author SHA1 Message Date
Ed Hennis
14be8ca4ea Merge branch 'develop' into ximinez/online-delete-gaps 2026-04-07 16:47:32 -04:00
Pratik Mankawde
6d1a5be8d2 fix: Handle WSClient write failure when server closes WebSocket (#6671)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 19:15:40 +00: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
25 changed files with 451 additions and 118 deletions

View File

@@ -1033,10 +1033,11 @@
# 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 1.
#
# Notes:
# The 'node_db' entry configures the primary, persistent storage.

View File

@@ -134,7 +134,7 @@ public:
{
lowest_layer().shutdown(plain_socket::shutdown_both);
}
catch (boost::system::system_error const& e)
catch (boost::system::system_error& e)
{
ec = e.code();
}

View File

@@ -246,7 +246,7 @@ flow(
EitherAmount stepIn(*strand[0]->cachedIn());
for (auto i = 0; i < s; ++i)
{
bool valid = false;
bool valid;
std::tie(valid, stepIn) = strand[i]->validFwd(checkSB, checkAfView, stepIn);
if (!valid)
{

View File

@@ -5,8 +5,6 @@
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/st.h>
#include <stdexcept>
namespace xrpl {
namespace detail {
@@ -376,14 +374,14 @@ ApplyStateTable::erase(ReadView const& base, std::shared_ptr<SLE> const& sle)
{
auto const iter = items_.find(sle->key());
if (iter == items_.end())
Throw<std::logic_error>("ApplyStateTable::erase: missing key");
LogicError("ApplyStateTable::erase: missing key");
auto& item = iter->second;
if (item.second != sle)
Throw<std::logic_error>("ApplyStateTable::erase: unknown SLE");
LogicError("ApplyStateTable::erase: unknown SLE");
switch (item.first)
{
case Action::erase:
Throw<std::logic_error>("ApplyStateTable::erase: double erase");
LogicError("ApplyStateTable::erase: double erase");
break;
case Action::insert:
items_.erase(iter);
@@ -407,7 +405,7 @@ ApplyStateTable::rawErase(ReadView const& base, std::shared_ptr<SLE> const& sle)
switch (item.first)
{
case Action::erase:
Throw<std::logic_error>("ApplyStateTable::rawErase: double erase");
LogicError("ApplyStateTable::rawErase: double erase");
break;
case Action::insert:
items_.erase(result.first);
@@ -438,11 +436,11 @@ ApplyStateTable::insert(ReadView const& base, std::shared_ptr<SLE> const& sle)
switch (item.first)
{
case Action::cache:
Throw<std::logic_error>("ApplyStateTable::insert: already cached");
LogicError("ApplyStateTable::insert: already cached");
case Action::insert:
Throw<std::logic_error>("ApplyStateTable::insert: already inserted");
LogicError("ApplyStateTable::insert: already inserted");
case Action::modify:
Throw<std::logic_error>("ApplyStateTable::insert: already modified");
LogicError("ApplyStateTable::insert: already modified");
case Action::erase:
break;
}
@@ -468,7 +466,7 @@ ApplyStateTable::replace(ReadView const& base, std::shared_ptr<SLE> const& sle)
switch (item.first)
{
case Action::erase:
Throw<std::logic_error>("ApplyStateTable::replace: already erased");
LogicError("ApplyStateTable::replace: already erased");
case Action::cache:
item.first = Action::modify;
break;
@@ -484,14 +482,14 @@ ApplyStateTable::update(ReadView const& base, std::shared_ptr<SLE> const& sle)
{
auto const iter = items_.find(sle->key());
if (iter == items_.end())
Throw<std::logic_error>("ApplyStateTable::update: missing key");
LogicError("ApplyStateTable::update: missing key");
auto& item = iter->second;
if (item.second != sle)
Throw<std::logic_error>("ApplyStateTable::update: unknown SLE");
LogicError("ApplyStateTable::update: unknown SLE");
switch (item.first)
{
case Action::erase:
Throw<std::logic_error>("ApplyStateTable::update: erased");
LogicError("ApplyStateTable::update: erased");
break;
case Action::cache:
item.first = Action::modify;

View File

@@ -4,7 +4,6 @@
#include <xrpl/protocol/Protocol.h>
#include <limits>
#include <stdexcept>
#include <type_traits>
namespace xrpl {
@@ -41,8 +40,10 @@ findPreviousPage(ApplyView& view, Keylet const& directory, SLE::ref start)
{
node = view.peek(keylet::page(directory, page));
if (!node)
Throw<std::logic_error>(
"Directory chain: root back-pointer broken."); // LCOV_EXCL_LINE
{ // LCOV_EXCL_START
LogicError("Directory chain: root back-pointer broken.");
// LCOV_EXCL_STOP
}
}
auto indexes = node->getFieldV256(sfIndexes);
@@ -61,20 +62,21 @@ insertKey(
if (preserveOrder)
{
if (std::find(indexes.begin(), indexes.end(), key) != indexes.end())
Throw<std::logic_error>("dirInsert: double insertion"); // LCOV_EXCL_LINE
LogicError("dirInsert: double insertion"); // LCOV_EXCL_LINE
indexes.push_back(key);
}
else
{
// We can't be sure if this page is already sorted because it may be a
// legacy page we haven't yet touched. Take the time to sort it.
// We can't be sure if this page is already sorted because
// it may be a legacy page we haven't yet touched. Take
// the time to sort it.
std::sort(indexes.begin(), indexes.end());
auto pos = std::lower_bound(indexes.begin(), indexes.end(), key);
if (pos != indexes.end() && key == *pos)
Throw<std::logic_error>("dirInsert: double insertion"); // LCOV_EXCL_LINE
LogicError("dirInsert: double insertion"); // LCOV_EXCL_LINE
indexes.insert(pos, key);
}
@@ -127,7 +129,8 @@ insertPage(
node->setFieldH256(sfRootIndex, directory.key);
node->setFieldV256(sfIndexes, indexes);
// Save some space by not specifying the value 0 since it's the default.
// Save some space by not specifying the value 0 since
// it's the default.
if (page != 1)
node->setFieldU64(sfIndexPrevious, page - 1);
XRPL_ASSERT_PARTS(!nextPage, "xrpl::directory::insertPage", "nextPage has default value");
@@ -196,24 +199,28 @@ ApplyView::emptyDirDelete(Keylet const& directory)
auto nextPage = node->getFieldU64(sfIndexNext);
if (nextPage == rootPage && prevPage != rootPage)
Throw<std::logic_error>("Directory chain: fwd link broken"); // LCOV_EXCL_LINE
LogicError("Directory chain: fwd link broken"); // LCOV_EXCL_LINE
if (prevPage == rootPage && nextPage != rootPage)
Throw<std::logic_error>("Directory chain: rev link broken"); // LCOV_EXCL_LINE
LogicError("Directory chain: rev link broken"); // LCOV_EXCL_LINE
// Older versions of the code would, in some cases, allow the last page to
// be empty. Remove such pages:
// Older versions of the code would, in some cases, allow the last
// page to be empty. Remove such pages:
if (nextPage == prevPage && nextPage != rootPage)
{
auto last = peek(keylet::page(directory, nextPage));
if (!last)
Throw<std::logic_error>("Directory chain: fwd link broken."); // LCOV_EXCL_LINE
{ // LCOV_EXCL_START
LogicError("Directory chain: fwd link broken.");
// LCOV_EXCL_STOP
}
if (!last->getFieldV256(sfIndexes).empty())
return false;
// Update the first page's linked list and mark it as updated.
// Update the first page's linked list and
// mark it as updated.
node->setFieldU64(sfIndexNext, rootPage);
node->setFieldU64(sfIndexPrevious, rootPage);
update(node);
@@ -221,7 +228,8 @@ ApplyView::emptyDirDelete(Keylet const& directory)
// And erase the empty last page:
erase(last);
// Make sure our local values reflect the updated information:
// Make sure our local values reflect the
// updated information:
nextPage = rootPage;
prevPage = rootPage;
}
@@ -261,33 +269,46 @@ ApplyView::dirRemove(Keylet const& directory, std::uint64_t page, uint256 const&
return true;
}
// The current page is now empty; check if it can be deleted, and, if so,
// whether the entire directory can now be removed.
// The current page is now empty; check if it can be
// deleted, and, if so, whether the entire directory
// can now be removed.
auto prevPage = node->getFieldU64(sfIndexPrevious);
auto nextPage = node->getFieldU64(sfIndexNext);
// The first page is the directory's root node and is treated specially: it
// can never be deleted even if it is empty, unless we plan on removing the
// entire directory.
// The first page is the directory's root node and is
// treated specially: it can never be deleted even if
// it is empty, unless we plan on removing the entire
// directory.
if (page == rootPage)
{
if (nextPage == page && prevPage != page)
Throw<std::logic_error>("Directory chain: fwd link broken"); // LCOV_EXCL_LINE
{ // LCOV_EXCL_START
LogicError("Directory chain: fwd link broken");
// LCOV_EXCL_STOP
}
if (prevPage == page && nextPage != page)
Throw<std::logic_error>("Directory chain: rev link broken"); // LCOV_EXCL_LINE
{ // LCOV_EXCL_START
LogicError("Directory chain: rev link broken");
// LCOV_EXCL_STOP
}
// Older versions of the code would, in some cases, allow the last page
// to be empty. Remove such pages if we stumble on them:
// Older versions of the code would, in some cases,
// allow the last page to be empty. Remove such
// pages if we stumble on them:
if (nextPage == prevPage && nextPage != page)
{
auto last = peek(keylet::page(directory, nextPage));
if (!last)
Throw<std::logic_error>("Directory chain: fwd link broken."); // LCOV_EXCL_LINE
{ // LCOV_EXCL_START
LogicError("Directory chain: fwd link broken.");
// LCOV_EXCL_STOP
}
if (last->getFieldV256(sfIndexes).empty())
{
// Update the first page's linked list and mark it as updated.
// Update the first page's linked list and
// mark it as updated.
node->setFieldU64(sfIndexNext, page);
node->setFieldU64(sfIndexPrevious, page);
update(node);
@@ -295,7 +316,8 @@ ApplyView::dirRemove(Keylet const& directory, std::uint64_t page, uint256 const&
// And erase the empty last page:
erase(last);
// Make sure our local values reflect the updated information:
// Make sure our local values reflect the
// updated information:
nextPage = page;
prevPage = page;
}
@@ -313,24 +335,25 @@ ApplyView::dirRemove(Keylet const& directory, std::uint64_t page, uint256 const&
// This can never happen for nodes other than the root:
if (nextPage == page)
Throw<std::logic_error>("Directory chain: fwd link broken"); // LCOV_EXCL_LINE
LogicError("Directory chain: fwd link broken"); // LCOV_EXCL_LINE
if (prevPage == page)
Throw<std::logic_error>("Directory chain: rev link broken"); // LCOV_EXCL_LINE
LogicError("Directory chain: rev link broken"); // LCOV_EXCL_LINE
// This node isn't the root, so it can either be in the middle of the list,
// or at the end. Unlink it first and then check if that leaves the list
// with only a root:
// This node isn't the root, so it can either be in the
// middle of the list, or at the end. Unlink it first
// and then check if that leaves the list with only a
// root:
auto prev = peek(keylet::page(directory, prevPage));
if (!prev)
Throw<std::logic_error>("Directory chain: fwd link broken."); // LCOV_EXCL_LINE
LogicError("Directory chain: fwd link broken."); // LCOV_EXCL_LINE
// Fix previous to point to its new next.
prev->setFieldU64(sfIndexNext, nextPage);
update(prev);
auto next = peek(keylet::page(directory, nextPage));
if (!next)
Throw<std::logic_error>("Directory chain: rev link broken."); // LCOV_EXCL_LINE
LogicError("Directory chain: rev link broken."); // LCOV_EXCL_LINE
// Fix next to point to its new previous.
next->setFieldU64(sfIndexPrevious, prevPage);
update(next);
@@ -338,12 +361,13 @@ ApplyView::dirRemove(Keylet const& directory, std::uint64_t page, uint256 const&
// The page is no longer linked. Delete it.
erase(node);
// Check whether the next page is the last page and, if so, whether it's
// empty. If it is, delete it.
// Check whether the next page is the last page and, if
// so, whether it's empty. If it is, delete it.
if (nextPage != rootPage && next->getFieldU64(sfIndexNext) == rootPage &&
next->getFieldV256(sfIndexes).empty())
{
// Since next doesn't point to the root, it can't be pointing to prev.
// Since next doesn't point to the root, it
// can't be pointing to prev.
erase(next);
// The previous page is now the last page:
@@ -353,16 +377,18 @@ ApplyView::dirRemove(Keylet const& directory, std::uint64_t page, uint256 const&
// And the root points to the last page:
auto root = peek(keylet::page(directory, rootPage));
if (!root)
Throw<std::logic_error>("Directory chain: root link broken."); // LCOV_EXCL_LINE
{ // LCOV_EXCL_START
LogicError("Directory chain: root link broken.");
// LCOV_EXCL_STOP
}
root->setFieldU64(sfIndexPrevious, prevPage);
update(root);
nextPage = rootPage;
}
// If we're not keeping the root, then check to see if it's left empty.
// If so, delete it as well.
// If we're not keeping the root, then check to see if
// it's left empty. If so, delete it as well.
if (!keepRoot && nextPage == rootPage && prevPage == rootPage)
{
if (prev->getFieldV256(sfIndexes).empty())

View File

@@ -12,7 +12,6 @@
#include <xrpl/protocol/digest.h>
#include <xrpl/protocol/jss.h>
#include <stdexcept>
#include <utility>
#include <vector>
@@ -462,14 +461,14 @@ void
Ledger::rawErase(std::shared_ptr<SLE> const& sle)
{
if (!stateMap_.delItem(sle->key()))
Throw<std::logic_error>("Ledger::rawErase: key not found");
LogicError("Ledger::rawErase: key not found");
}
void
Ledger::rawErase(uint256 const& key)
{
if (!stateMap_.delItem(key))
Throw<std::logic_error>("Ledger::rawErase: key not found");
LogicError("Ledger::rawErase: key not found");
}
void
@@ -479,7 +478,7 @@ Ledger::rawInsert(std::shared_ptr<SLE> const& sle)
sle->add(ss);
if (!stateMap_.addGiveItem(
SHAMapNodeType::tnACCOUNT_STATE, make_shamapitem(sle->key(), ss.slice())))
Throw<std::logic_error>("Ledger::rawInsert: key already exists");
LogicError("Ledger::rawInsert: key already exists");
}
void
@@ -489,7 +488,7 @@ Ledger::rawReplace(std::shared_ptr<SLE> const& sle)
sle->add(ss);
if (!stateMap_.updateGiveItem(
SHAMapNodeType::tnACCOUNT_STATE, make_shamapitem(sle->key(), ss.slice())))
Throw<std::logic_error>("Ledger::rawReplace: key not found");
LogicError("Ledger::rawReplace: key not found");
}
void
@@ -505,7 +504,7 @@ Ledger::rawTxInsert(
s.addVL(txn->peekData());
s.addVL(metaData->peekData());
if (!txMap_.addGiveItem(SHAMapNodeType::tnTRANSACTION_MD, make_shamapitem(key, s.slice())))
Throw<std::logic_error>("duplicate_tx: " + to_string(key));
LogicError("duplicate_tx: " + to_string(key));
}
uint256
@@ -523,7 +522,7 @@ Ledger::rawTxInsertWithHash(
auto item = make_shamapitem(key, s.slice());
auto hash = sha512Half(HashPrefix::txNode, item->slice(), item->key());
if (!txMap_.addGiveItem(SHAMapNodeType::tnTRANSACTION_MD, std::move(item)))
Throw<std::logic_error>("duplicate_tx: " + to_string(key));
LogicError("duplicate_tx: " + to_string(key));
return hash;
}

View File

@@ -1,8 +1,6 @@
#include <xrpl/basics/contract.h>
#include <xrpl/ledger/OpenView.h>
#include <stdexcept>
namespace xrpl {
class OpenView::txs_iter_impl : public txs_type::iter_base
@@ -249,7 +247,7 @@ OpenView::rawTxInsert(
auto const result = txs_.emplace(
std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(txn, metaData));
if (!result.second)
Throw<std::logic_error>("rawTxInsert: duplicate TX id: " + to_string(key));
LogicError("rawTxInsert: duplicate TX id: " + to_string(key));
}
} // namespace xrpl

View File

@@ -1,8 +1,6 @@
#include <xrpl/basics/contract.h>
#include <xrpl/ledger/detail/RawStateTable.h>
#include <stdexcept>
namespace xrpl {
namespace detail {
@@ -243,7 +241,7 @@ RawStateTable::erase(std::shared_ptr<SLE> const& sle)
switch (item.action)
{
case Action::erase:
Throw<std::logic_error>("RawStateTable::erase: already erased");
LogicError("RawStateTable::erase: already erased");
break;
case Action::insert:
items_.erase(result.first);
@@ -272,10 +270,10 @@ RawStateTable::insert(std::shared_ptr<SLE> const& sle)
item.sle = sle;
break;
case Action::insert:
Throw<std::logic_error>("RawStateTable::insert: already inserted");
LogicError("RawStateTable::insert: already inserted");
break;
case Action::replace:
Throw<std::logic_error>("RawStateTable::insert: already exists");
LogicError("RawStateTable::insert: already exists");
break;
}
}
@@ -293,7 +291,7 @@ RawStateTable::replace(std::shared_ptr<SLE> const& sle)
switch (item.action)
{
case Action::erase:
Throw<std::logic_error>("RawStateTable::replace: was erased");
LogicError("RawStateTable::replace: was erased");
break;
case Action::insert:
case Action::replace:

View File

@@ -8,7 +8,6 @@
#include <algorithm>
#include <limits>
#include <stdexcept>
namespace xrpl {
@@ -154,7 +153,7 @@ getPseudoAccountFields()
if (!ar)
{
// LCOV_EXCL_START
Throw<std::logic_error>(
LogicError(
"xrpl::getPseudoAccountFields : unable to find account root "
"ledger format");
// LCOV_EXCL_STOP

View File

@@ -2,6 +2,7 @@
#include <test/jtx/Env.h>
#include <xrpld/app/ledger/LedgerMaster.h>
#include <xrpld/app/misc/SHAMapStore.h>
namespace xrpl {
namespace test {
@@ -97,6 +98,73 @@ 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([](auto cfg) {
return online_delete(std::move(cfg), 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();
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(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);
// 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();
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
@@ -110,6 +178,7 @@ public:
testWithFeats(FeatureBitset features)
{
testTxnIdFromIndex(features);
testCompleteLedgerRange(features);
}
};

View File

@@ -1,6 +1,7 @@
#include <test/jtx.h>
#include <test/jtx/envconfig.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>
@@ -10,6 +11,8 @@
#include <xrpl/nodestore/detail/DatabaseRotatingImp.h>
#include <xrpl/protocol/jss.h>
#include <thread>
namespace xrpl {
namespace test {
@@ -20,10 +23,8 @@ class SHAMapStore_test : public beast::unit_test::suite
static auto
onlineDelete(std::unique_ptr<Config> cfg)
{
cfg->LEDGER_HISTORY = deleteInterval;
auto& section = cfg->section(ConfigSection::nodeDatabase());
section.set("online_delete", std::to_string(deleteInterval));
return cfg;
using namespace jtx;
return online_delete(std::move(cfg), deleteInterval);
}
static auto
@@ -563,6 +564,158 @@ 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)};
std::map<LedgerIndex, uint256> hashes;
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();
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(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);
// Close enough ledgers to rotate a few times
while (maxSeq < 20)
{
for (int t = 0; t < 3; ++t)
{
env(noop(alice));
}
env.close();
store.rendezvous();
++maxSeq;
if (maxSeq + 1 == lastRotated + deleteInterval)
{
using namespace std::chrono_literals;
// The next ledger will trigger a rotation. Delete the
// current ledger from LedgerMaster.
std::this_thread::sleep_for(100ms);
LedgerIndex const deleteSeq = maxSeq;
while (!lm.haveLedger(deleteSeq))
{
std::this_thread::sleep_for(100ms);
}
lm.clearLedger(deleteSeq);
auto expectedRange = [](auto minSeq, auto deleteSeq, auto maxSeq) {
std::stringstream expectedRange;
expectedRange << minSeq << "-" << (deleteSeq - 1);
if (deleteSeq + 1 == maxSeq)
expectedRange << "," << maxSeq;
else if (deleteSeq < maxSeq)
expectedRange << "," << (deleteSeq + 1) << "-" << maxSeq;
return expectedRange.str();
};
BEAST_EXPECTS(
lm.getCompleteLedgers() == expectedRange(minSeq, deleteSeq, maxSeq),
failureMessage(
"Complete ledgers",
expectedRange(minSeq, deleteSeq, maxSeq),
lm.getCompleteLedgers()));
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq, maxSeq) == 1);
// 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()! You'll end up with a deadlock.
++maxSeq;
// Nothing has changed
BEAST_EXPECTS(
store.getLastRotated() == lastRotated,
failureMessage("lastRotated", lastRotated, store.getLastRotated()));
BEAST_EXPECTS(
lm.getCompleteLedgers() == expectedRange(minSeq, deleteSeq, maxSeq),
failureMessage(
"Complete ledgers",
expectedRange(minSeq, deleteSeq, maxSeq),
lm.getCompleteLedgers()));
// Close 5 more ledgers, waiting one second 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();
// DO NOT CALL rendezvous()! You'll end up with a deadlock.
++maxSeq;
// Nothing has changed
BEAST_EXPECTS(
store.getLastRotated() == lastRotated,
failureMessage("lastRotated", lastRotated, store.getLastRotated()));
BEAST_EXPECTS(
lm.getCompleteLedgers() == expectedRange(minSeq, deleteSeq, maxSeq),
failureMessage(
"Complete Ledgers",
expectedRange(minSeq, deleteSeq, maxSeq),
lm.getCompleteLedgers()));
std::this_thread::sleep_for(1s);
}
// Put the missing ledger back in LedgerMaster
lm.setLedgerRangePresent(deleteSeq, deleteSeq);
// Wait for the rotation to finish
store.rendezvous();
minSeq = lastRotated;
lastRotated = deleteSeq + 1;
}
BEAST_EXPECT(maxSeq != lastRotated + deleteInterval);
BEAST_EXPECTS(
env.closed()->header().seq == maxSeq,
failureMessage("maxSeq", maxSeq, env.closed()->header().seq));
BEAST_EXPECTS(
store.getLastRotated() == lastRotated,
failureMessage("lastRotated", lastRotated, store.getLastRotated()));
std::stringstream expectedRange;
expectedRange << minSeq << "-" << maxSeq;
BEAST_EXPECTS(
lm.getCompleteLedgers() == expectedRange.str(),
failureMessage("CompleteLedgers", 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);
}
}
void
run() override
{
@@ -570,6 +723,7 @@ public:
testAutomatic();
testCanDelete();
testRotate();
testLedgerGaps();
}
};

View File

@@ -507,7 +507,7 @@ port_wss_admin
{
c.loadFromString(boost::str(configTemplate % validationSeed % token));
}
catch (std::runtime_error const& e)
catch (std::runtime_error& e)
{
error = e.what();
}
@@ -528,7 +528,7 @@ port_wss_admin
main
)xrpldConfig");
}
catch (std::runtime_error const& e)
catch (std::runtime_error& e)
{
error = e.what();
}
@@ -541,7 +541,7 @@ main
c.loadFromString(R"xrpldConfig(
)xrpldConfig");
}
catch (std::runtime_error const& e)
catch (std::runtime_error& e)
{
error = e.what();
}
@@ -556,7 +556,7 @@ main
255
)xrpldConfig");
}
catch (std::runtime_error const& e)
catch (std::runtime_error& e)
{
error = e.what();
}
@@ -571,7 +571,7 @@ main
10000
)xrpldConfig");
}
catch (std::runtime_error const& e)
catch (std::runtime_error& e)
{
error = e.what();
}
@@ -598,7 +598,7 @@ main
Config c;
c.loadFromString(boost::str(cc % missingPath));
}
catch (std::runtime_error const& e)
catch (std::runtime_error& e)
{
error = e.what();
}
@@ -617,7 +617,7 @@ main
Config c;
c.loadFromString(boost::str(cc % invalidFile.string()));
}
catch (std::runtime_error const& e)
catch (std::runtime_error& e)
{
error = e.what();
}
@@ -725,7 +725,7 @@ trust-these-validators.gov
c.loadFromString(toLoad);
fail();
}
catch (std::runtime_error const& e)
catch (std::runtime_error& e)
{
error = e.what();
}
@@ -754,7 +754,7 @@ value = 2
c.loadFromString(toLoad);
fail();
}
catch (std::runtime_error const& e)
catch (std::runtime_error& e)
{
error = e.what();
}
@@ -802,7 +802,7 @@ trust-these-validators.gov
c.loadFromString(toLoad);
fail();
}
catch (std::runtime_error const& e)
catch (std::runtime_error& e)
{
error = e.what();
}
@@ -948,7 +948,7 @@ trust-these-validators.gov
c.loadFromString(boost::str(cc % vtg.validatorsFile()));
fail();
}
catch (std::runtime_error const& e)
catch (std::runtime_error& e)
{
error = e.what();
}
@@ -974,7 +974,7 @@ trust-these-validators.gov
Config c2;
c2.loadFromString(boost::str(cc % vtg.validatorsFile()));
}
catch (std::runtime_error const& e)
catch (std::runtime_error& e)
{
error = e.what();
}
@@ -1451,7 +1451,7 @@ r.ripple.com:51235
fail();
}
}
catch (std::runtime_error const&)
catch (std::runtime_error&)
{
if (!shouldPass)
{
@@ -1477,7 +1477,7 @@ r.ripple.com:51235
c.loadFromString("[overlay]\nmax_unknown_time=" + value);
return c.MAX_UNKNOWN_TIME;
}
catch (std::runtime_error const&)
catch (std::runtime_error&)
{
return {};
}
@@ -1511,7 +1511,7 @@ r.ripple.com:51235
c.loadFromString("[overlay]\nmax_diverged_time=" + value);
return c.MAX_DIVERGED_TIME;
}
catch (std::runtime_error const&)
catch (std::runtime_error&)
{
return {};
}

View File

@@ -57,6 +57,17 @@ 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>
online_delete(std::unique_ptr<Config> cfg, std::uint32_t deleteInterval = 8);
/// @brief adjust config so no admin ports are enabled
///
/// this is intended for use with envconfig, as in

View File

@@ -184,7 +184,14 @@ public:
jp[jss::command] = cmd;
}
auto const s = to_string(jp);
ws_.write_some(true, buffer(s));
// Use the error_code overload to avoid an unhandled exception
// when the server closes the WebSocket connection (e.g. after
// booting a client that exceeded resource thresholds).
error_code ec;
ws_.write_some(true, buffer(s), ec);
if (ec)
return {};
}
auto jv =

View File

@@ -53,6 +53,15 @@ setupConfigForUnitTests(Config& cfg)
namespace jtx {
std::unique_ptr<Config>
online_delete(std::unique_ptr<Config> cfg, std::uint32_t deleteInterval)
{
cfg->LEDGER_HISTORY = deleteInterval;
auto& section = cfg->section(ConfigSection::nodeDatabase());
section.set("online_delete", std::to_string(deleteInterval));
return cfg;
}
std::unique_ptr<Config>
no_admin(std::unique_ptr<Config> cfg)
{

View File

@@ -1345,7 +1345,7 @@ vp_enable=0
{
c.loadFromString(toLoad);
}
catch (std::runtime_error const& e)
catch (std::runtime_error& e)
{
error = e.what();
}
@@ -1389,7 +1389,7 @@ vp_base_squelch_max_selected_peers=2
{
c2.loadFromString(toLoad);
}
catch (std::runtime_error const& e)
catch (std::runtime_error& e)
{
error = e.what();
}

View File

@@ -2130,7 +2130,7 @@ class STParsedJSON_test : public beast::unit_test::suite
STParsedJSONObject const parsed("test", faultyJson);
BEAST_EXPECT(!parsed.object);
}
catch (std::runtime_error const& e)
catch (std::runtime_error& e)
{
std::string const what(e.what());
unexpected(what.find("First level children of `Template`") != 0);

View File

@@ -105,7 +105,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
This is normally called as we close the ledger.
@@ -322,7 +325,7 @@ private:
// A set of transactions to replay during the next close
std::unique_ptr<LedgerReplay> replayData;
std::recursive_mutex mCompleteLock;
std::recursive_mutex mutable mCompleteLock;
RangeSet<std::uint32_t> mCompleteLedgers;
// Publish thread is running.

View File

@@ -1529,12 +1529,34 @@ LedgerMaster::getPublishedLedger()
}
std::string
LedgerMaster::getCompleteLedgers()
LedgerMaster::getCompleteLedgers() const
{
std::lock_guard const sl(mCompleteLock);
return to_string(mCompleteLedgers);
}
std::size_t
LedgerMaster::missingFromCompleteLedgerRange(LedgerIndex first, LedgerIndex last) const
{
// Make a copy of the range to avoid holding the lock
auto const range = [&] {
std::lock_guard sl(mCompleteLock);
return mCompleteLedgers;
}();
std::size_t missing = 0;
for (LedgerIndex idx = first; idx <= last; ++idx)
{
if (!boost::icl::contains(range, idx))
{
++missing;
}
}
return missing;
}
std::optional<NetClock::time_point>
LedgerMaster::getCloseTimeBySeq(LedgerIndex ledgerIndex)
{

View File

@@ -263,6 +263,17 @@ SHAMapStoreImp::run()
bool const readyToRotate = validatedSeq >= lastRotated + deleteInterval_ &&
canDelete_ >= lastRotated - 1 && healthWait() == keepGoing;
JLOG(journal_.debug()) << "run: Setting lastGoodValidatedLedger_ to " << validatedSeq;
{
// 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_);
lastGoodValidatedLedger_ = validatedSeq;
}
// will delete up to (not including) lastRotated
if (readyToRotate)
{
@@ -270,7 +281,8 @@ SHAMapStoreImp::run()
<< lastRotated << " deleteInterval " << deleteInterval_
<< " canDelete_ " << canDelete_ << " state "
<< app_.getOPs().strOperatingMode(false) << " age "
<< ledgerMaster_->getValidatedLedgerAge().count() << 's';
<< ledgerMaster_->getValidatedLedgerAge().count()
<< "s. Complete ledgers: " << ledgerMaster_->getCompleteLedgers();
clearPrior(lastRotated);
if (healthWait() == stopping)
@@ -330,7 +342,9 @@ SHAMapStoreImp::run()
clearCaches(validatedSeq);
});
JLOG(journal_.warn()) << "finished rotation " << validatedSeq;
JLOG(journal_.warn()) << "finished rotation. validatedSeq: " << validatedSeq
<< ", lastRotated: " << lastRotated
<< ". Complete ledgers: " << ledgerMaster_->getCompleteLedgers();
}
}
}
@@ -570,22 +584,40 @@ SHAMapStoreImp::clearPrior(LedgerIndex lastRotated)
SHAMapStoreImp::HealthResult
SHAMapStoreImp::healthWait()
{
auto index = ledgerMaster_->getValidLedgerIndex();
auto age = ledgerMaster_->getValidatedLedgerAge();
OperatingMode mode = netOPs_->getOperatingMode();
std::unique_lock lock(mutex_);
while (!stop_ && (mode != OperatingMode::FULL || age > ageThreshold_))
auto numMissing =
ledgerMaster_->missingFromCompleteLedgerRange(lastGoodValidatedLedger_, index);
while (!stop_ && (mode != OperatingMode::FULL || age > ageThreshold_ || numMissing > 0))
{
// this value shouldn't change, so grab it while we have the
// lock
auto const lastGood = lastGoodValidatedLedger_;
lock.unlock();
JLOG(journal_.warn()) << "Waiting " << recoveryWaitTime_.count()
<< "s for node to stabilize. state: "
<< app_.getOPs().strOperatingMode(mode, false) << ". age "
<< age.count() << 's';
auto const stream =
mode != OperatingMode::FULL || age > ageThreshold_ ? journal_.warn() : journal_.info();
JLOG(stream) << "Waiting " << recoveryWaitTime_.count()
<< "s for node to stabilize. state: "
<< app_.getOPs().strOperatingMode(mode, false) << ". age " << age.count()
<< "s. Missing ledgers: " << numMissing << ". Expect: " << lastGood << "-"
<< index << ". Complete ledgers: " << ledgerMaster_->getCompleteLedgers();
std::this_thread::sleep_for(recoveryWaitTime_);
index = ledgerMaster_->getValidLedgerIndex();
age = ledgerMaster_->getValidatedLedgerAge();
mode = netOPs_->getOperatingMode();
numMissing = ledgerMaster_->missingFromCompleteLedgerRange(lastGood, index);
lock.lock();
}
JLOG(journal_.debug()) << "healthWait: Setting lastGoodValidatedLedger_ to " << index;
lastGoodValidatedLedger_ = index;
return stop_ ? stopping : keepGoing;
}

View File

@@ -70,6 +70,11 @@ 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;
mutable std::condition_variable cond_;
mutable std::condition_variable rendezvous_;
mutable std::mutex mutex_;
@@ -83,11 +88,11 @@ private:
std::uint32_t deleteBatch_ = 100;
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_{1};
// these do not exist upon SHAMapStore creation, but do exist
// as of run() or before
@@ -204,6 +209,8 @@ private:
enum HealthResult { stopping, keepGoing };
[[nodiscard]] HealthResult
healthWait();
bool
hasCompleteRange(LedgerIndex first, LedgerIndex last);
public:
void

View File

@@ -1632,7 +1632,7 @@ rpcClient(
// YYY We could have a command line flag for single line output for
// scripts. YYY We would intercept output here and simplify it.
}
catch (RequestNotParsable const& e)
catch (RequestNotParsable& e)
{
jvOutput = rpcError(rpcINVALID_PARAMS);
jvOutput["error_what"] = e.what();

View File

@@ -614,7 +614,7 @@ transactionPreProcessImpl(
stTx = std::make_shared<STTx>(std::move(parsed.object.value()));
}
catch (STObject::FieldErr const& err)
catch (STObject::FieldErr& err)
{
return RPC::make_error(rpcINVALID_PARAMS, err.what());
}
@@ -1286,7 +1286,7 @@ transactionSubmitMultiSigned(
{
stTx = std::make_shared<STTx>(std::move(parsedTx_json.object.value()));
}
catch (STObject::FieldErr const& err)
catch (STObject::FieldErr& err)
{
return RPC::make_error(rpcINVALID_PARAMS, err.what());
}

View File

@@ -897,7 +897,7 @@ doLedgerEntry(RPC::JsonContext& context)
return RPC::make_param_error("No ledger_entry params provided.");
}
}
catch (Json::error const& e)
catch (Json::error& e)
{
if (context.apiVersion > 1u)
{

View File

@@ -65,7 +65,7 @@ doSubscribe(RPC::JsonContext& context)
ispSub =
context.netOps.addRpcSub(strUrl, std::dynamic_pointer_cast<InfoSub>(rspSub));
}
catch (std::runtime_error const& ex)
catch (std::runtime_error& ex)
{
return RPC::make_param_error(ex.what());
}