Skip to content

Queue async monitor update writes synchronously#4671

Open
tnull wants to merge 1 commit into
lightningdevkit:mainfrom
tnull:2026-06-async-monitor-update-ordering
Open

Queue async monitor update writes synchronously#4671
tnull wants to merge 1 commit into
lightningdevkit:mainfrom
tnull:2026-06-async-monitor-update-ordering

Conversation

@tnull

@tnull tnull commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Monitor update persistence relies on KVStore write calls being queued in call order. The incremental async update path deferred that write until executor polling, allowing async scheduling to reorder persistence operations relative to later updates or cleanup.

Add a regression test that builds an incremental update future without polling it and verifies that the write has already been queued.

Co-Authored-By: HAL 9000

This finding was discovered by Project Loupe

@tnull tnull requested a review from TheBlueMatt June 10, 2026 11:37
@ldk-reviews-bot

ldk-reviews-bot commented Jun 10, 2026

Copy link
Copy Markdown

👋 Thanks for assigning @TheBlueMatt as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@ldk-claude-review-bot

ldk-claude-review-bot commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

The code is correct and unchanged from my prior review. The new persist_monitor_update helper at persist.rs:1559 preserves the synchronous queueing semantics: it calls the sync kv_store.write and boxes the returned future, matching the original argument order (primary namespace, monitor_key as secondary, update_name as key, encoded as value). res_a is queued before any await and completed at line 1633.

No issues found.

@tnull tnull self-assigned this Jun 11, 2026
@tnull tnull moved this to Goal: Merge in Weekly Goals Jun 11, 2026
@tnull tnull force-pushed the 2026-06-async-monitor-update-ordering branch from a3967b5 to 8ac52cf Compare June 11, 2026 16:43
@tnull

tnull commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Updated to line-wrap commit messages.

Comment thread lightning/src/util/persist.rs Outdated
});
let write_fut: Pin<
Box<dyn MaybeSendableFuture<Output = Result<(), io::Error>> + 'static>,
> = Box::pin(self.kv_store.write(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't actually think this is a bug, the comment is just wrong. Its not crazy to fix this, however, but we should do it like the code in the else block, not by Box::pining.

@tnull tnull Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, we'd previously create the future in the async move block, which would delay the write by one poll interval of the runtime scheduler, AFAIU?

Now updated to align the approach with the else clause, though still Box::pinning.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but that isn't problematic in this case - we're writing the monitor update, there will never be a new monitor update with the same ID. Thinking about this more I believe the previous code was deliberate - it avoids doing the initial poll right away which moves some work out of the main thread.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoids doing the initial poll right away which moves some work out of the main thread.

Huh, that seems like a very steep assumption to make. I don't think we have any influence on when the scheduler will poll this again, let alone on which thread it happens?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I just meant that this will avoid a tiny bit of work on the thread that's doing message processing (in the general case) or the thread hitting send on a payment (in payment cases). That seems like a marginal win, or at least not a loss worth fixing.

@ldk-reviews-bot

Copy link
Copy Markdown

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

Incremental monitor update persistence had its KVStore write call inside the spawned future body, unlike full-monitor writes. Move the write creation into the synchronous setup path so the implementation matches the async KVStore call-order comments.

Extend the native async persistence test to assert monitor update writes are visible before the queued persistence futures are polled.

Co-Authored-By: HAL 9000
@tnull tnull force-pushed the 2026-06-async-monitor-update-ordering branch from 8ac52cf to d0b0dd6 Compare June 16, 2026 12:55
&self, monitor_key: &str, update_name: &str, encoded: Vec<u8>,
) -> Pin<Box<dyn MaybeSendableFuture<Output = Result<(), io::Error>> + 'static>> {
let primary = CHANNEL_MONITOR_UPDATE_PERSISTENCE_PRIMARY_NAMESPACE;
Box::pin(self.kv_store.write(primary, monitor_key, update_name, encoded))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still ended up with a Box here, can we not avoid it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIU, we can once we bump MSRV, but not before.

@tnull tnull requested a review from TheBlueMatt June 17, 2026 09:18

@TheBlueMatt TheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remain not entirely convinced that the change here is better than the existing code. IIRC the way this was written was deliberate - #4671 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Goal: Merge

Development

Successfully merging this pull request may close these issues.

4 participants