perf(history): weekly safety-net full re-sweep for sent history
Persisting the sent-sweep watermark means a relaunch runs a windowed sweep, which can't see a change made outside the GUI (a Console z_importviewingkey, an external same-seed restore) that adds an old send below the window. This adds a periodic full re-sweep so that case is still caught within ~7 days. - sentSweepSafetyNetDue(now, lastFull) = lastFull > 0 && now - lastFull > 7d (a wallet that has never full-swept doesn't trigger one; the seeding sweep records the time). lastFullTime / lastFullHeight ride in the sent_sweep snapshot; any successful full sweep (seed, verify-mismatch or safety-net) resets the clock. On load, a valid watermark without a clock starts it now, so even watermarks seeded before this field eventually re-sweep. - When due, the snapshot forces a full sweep for that cycle. Gated on: a watermark exists, a routine (non-syncing) cycle, not the Mining page, and not in full-sweep backoff -- so a full sweep that can't finish on a slow daemon backs off instead of retrying every cycle. Degrades gracefully: ~0.4 s on a note-cache daemon; callLong(120 s) with backoff on an old one (the deferred banding would keep each cs_main hold short so it fits there too). Helper + persistence covered by tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -9220,6 +9220,7 @@ void testSentSweepPersistence()
|
||||
SentSweepState sw;
|
||||
sw.height = 500; sw.anchorHeight = 380; sw.anchorHash = "ha380";
|
||||
sw.backlog = {"bt1", "bt2"}; sw.fullFailures = 2; sw.fullRetryDelaySec = 3600; sw.daemonId = "v1.3.0|t";
|
||||
sw.lastFullTime = 1699990000; sw.lastFullHeight = 420;
|
||||
EXPECT_TRUE(cache.replace(id, 500, "tip", txs, 1700000000, {}, sw));
|
||||
auto loaded = cache.load(id, 500, "tip");
|
||||
EXPECT_TRUE(loaded.loaded);
|
||||
@@ -9231,6 +9232,8 @@ void testSentSweepPersistence()
|
||||
EXPECT_EQ(loaded.sentSweep.fullFailures, 2);
|
||||
EXPECT_EQ(loaded.sentSweep.fullRetryDelaySec, 3600L);
|
||||
EXPECT_EQ(loaded.sentSweep.daemonId, std::string("v1.3.0|t"));
|
||||
EXPECT_EQ(loaded.sentSweep.lastFullTime, static_cast<std::int64_t>(1699990000));
|
||||
EXPECT_EQ(loaded.sentSweep.lastFullHeight, 420);
|
||||
}
|
||||
{ // no watermark passed -> reads invalid (W = -1), i.e. today's full-sweep behaviour
|
||||
TransactionHistoryCache cache(dbPath.string());
|
||||
@@ -9266,6 +9269,10 @@ void testSentSweepPolicyHelpers()
|
||||
EXPECT_FALSE(sentSweepNeedsLongCall(1000, 999)); // tiny window
|
||||
EXPECT_FALSE(sentSweepNeedsLongCall(1000, -1)); // no watermark (full-sweep path)
|
||||
EXPECT_TRUE(sentSweepNeedsLongCall(10000, 10000 - kSentSweepLongCallBlocks - 1));
|
||||
EXPECT_FALSE(sentSweepSafetyNetDue(1000, 0)); // no full sweep recorded yet
|
||||
EXPECT_FALSE(sentSweepSafetyNetDue(1000 + kSentSweepSafetyNetSec, 1000)); // exactly at the threshold
|
||||
EXPECT_TRUE(sentSweepSafetyNetDue(1000 + kSentSweepSafetyNetSec + 1, 1000)); // past it -> due
|
||||
EXPECT_FALSE(sentSweepSafetyNetDue(1100, 1000)); // recent full sweep
|
||||
auto windowed = sentSweepParams("z1", 2000, 1500, sentSweepMargin(true));
|
||||
EXPECT_EQ(windowed.size(), static_cast<size_t>(4));
|
||||
EXPECT_EQ(windowed[3].get<int>(), (2000 - 1500) + sentSweepMargin(true));
|
||||
|
||||
Reference in New Issue
Block a user