Skip to content

Implement lazy deletes for VssStore - #689

Merged
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2025-10-bump-to-ldk-2.0rc1
Nov 17, 2025
Merged

Implement lazy deletes for VssStore#689
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2025-10-bump-to-ldk-2.0rc1

Conversation

@tnull

@tnulltnull commented Oct 31, 2025

Copy link
Copy Markdown
Collaborator

Now based on #691

We bump the LDK depedencies to the just-released 2.0 release candidate and account for minor last-minute API changes.

Furthermore, we implement lazy deletion for VssStore by tracking pending lazy deletes and supplying them as delete_items on the next put operation.

@ldk-reviews-bot

ldk-reviews-bot commented Oct 31, 2025

Copy link
Copy Markdown

👋 Thanks for assigning @joostjager 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.

@tnull
tnull requested a review from joostjagerOctober 31, 2025 10:52
@tnull
tnullforce-pushed the 2025-10-bump-to-ldk-2.0rc1 branch from a64be1d to 0a26053CompareOctober 31, 2025 11:05
Comment threadsrc/io/vss_store.rs
.pending_lazy_deletes
.try_lock()
.ok()
.and_then(|mut guard| guard.take())

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Hmm, we might lose some lazy deletes if the write below would fail. I do wonder if we should go out of our way to restore the pending items in such a case, or if we're fine just leaning into the 'may or may not succeed' API contract here.

@joostjager Any opinion?

Similiarly, I do wonder if we should spawn-and-forget some tasks on Drop to attempt cleaning up the pending deletes on shutdown?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good question how loose we can get away with. Those keys are then never deleted anymore, which isn't great? I am not sure.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What do you both think about re-adding the delete_items back to pending_lazy_deletes if write fails? We get to retry deleting them on a subsequent write attempt.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

What do you both think about re-adding the delete_items back to pending_lazy_deletes if write fails? We get to retry deleting them on a subsequent write attempt.

Yeah, that's the straightforward approach. Technically, we wouldn't even need to do this according to the API contract, but it's probably best to keep it best-effort. Blocking on re-acquiring the pending_lazy_deletes mutex in write isn't too great as it suddenly creates yet another interdependency between remove and write, but it should be safe here.

Went ahead and did that now.

@tnulltnull added this to the 0.7 milestone Oct 31, 2025

@joostjagerjoostjager left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I wonder if lazy delete is needed for VSS? Maybe we can just ignore the flag and delete sync always.

@tnull

tnull commented Oct 31, 2025

Copy link
Copy Markdown
CollaboratorAuthor

I wonder if lazy delete is needed for VSS? Maybe we can just ignore the flag and delete sync always.

Well, esp. now that we're on MonitorUpdatingPersister, it should make a difference in performance on cleanup.

@tnulltnull mentioned this pull request Oct 31, 2025
@joostjager

Copy link
Copy Markdown
Contributor

I just don't know if it is worth it for end-user nodes to worry about this. Even persisting full monitors always apparently was good enough performance?

In this PR the delete semantics are again different, because a write is needed before deletes are executed. It doesn't make it easier to understand.

@tnull

tnull commented Oct 31, 2025

Copy link
Copy Markdown
CollaboratorAuthor

I just don't know if it is worth it for end-user nodes to worry about this. Even persisting full monitors always apparently was good enough performance?

In this PR the delete semantics are again different, because a write is needed before deletes are executed. It doesn't make it easier to understand.

I'm not sure the 'end user' would ever need to worry about, as there are very little lazy deletes to begin with?

As you know I was open to dropping the lazy flag, but now that we have it (again), we should also implement it.

@joostjagerjoostjager left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code looks good. Just not sure about the implications of lingering data if something goes wrong / a write never happens.

In particular for the incremental channel updates, it would be good if that is always followed by a write somehow without much delay. Idk if that's indeed the case?

Comment threadsrc/io/vss_store.rs
.pending_lazy_deletes
.try_lock()
.ok()
.and_then(|mut guard| guard.take())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good question how loose we can get away with. Those keys are then never deleted anymore, which isn't great? I am not sure.

Comment threadsrc/io/vss_store.rs
Comment threadsrc/io/vss_store.rs
let obfuscated_key =
self.build_obfuscated_key(&primary_namespace, &secondary_namespace, &key);

let key_value = KeyValue { key: obfuscated_key, version: -1, value: vec![] };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can't we store just keys here?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Currently we might get away with it, but going forward we might utilize the version field there, so I'd prefer to do the future-safer thing here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You say 'might'. Do you mean that just using keys can already be a problem without the version field?

Even if we use the version field in the future, value still isn't needed?

In general, I would avoid dead code (or fields in this case). It just raises questions with devs. At least it did with me. Or otherwise clearly document.

Final question - is a version needed for lazy deletes?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

You say 'might'. Do you mean that just using keys can already be a problem without the version field?

No, but it's something we could easily overlook when making use of the version going forward.

Even if we use the version field in the future, value still isn't needed?

Well, whether the API design of KeyValue is good or not is debatable (I didn't invent it), but it is the type that we use to reference entries in the store. I.e., it is the thing used in remove/DeleteObjectRequest.

Final question - is a version needed for lazy deletes?

Well, if we issue a delete on a certain version, the service could in the future ignore a delete if it already has a higher version, no? I.e., it would thereby detect that the value has been updated since the delete was issued.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We could map the key to KeyValue at a later point, and not use it internally? Not a blocker.

Well, if we issue a delete on a certain version, the service could in the future ignore a delete if it already has a higher version, no? I.e., it would thereby detect that the value has been updated since the delete was issued.

I thought we didn't want rewrites of the same key after it has been deleted lazily, but not 100% sure anymore. There's no version currently anyway, so a worry for later.

@tnulltnull changed the title Bump to LDK 2.0.0-rc1 and implement lazy deletes for VssStoreImplement lazy deletes for VssStoreOct 31, 2025
@tnulltnull mentioned this pull request Nov 10, 2025
8 tasks
@tnulltnull self-assigned this Nov 13, 2025
@tnulltnull moved this to Goal: Merge in Weekly GoalsNov 13, 2025
@tnull

Copy link
Copy Markdown
CollaboratorAuthor

Excuse the delay here. I now added a fixup that has us restore any delete_items on failed write. I think that should be sufficient for now.

We implement `lazy` deletion for `VssStore` by tracking pending lazy deletes
and supplying them as `delete_items` on the next `put` operation.
@tnull
tnullforce-pushed the 2025-10-bump-to-ldk-2.0rc1 branch from eb42617 to 0dbec9eCompareNovember 17, 2025 09:19
We add a testcase that ensures we only delete a lazily-deleted key
after the next write operation succeeds.
Co-authored by Claude AI
@tnull
tnullforce-pushed the 2025-10-bump-to-ldk-2.0rc1 branch from 0dbec9e to c2b6b18CompareNovember 17, 2025 09:20
Comment threadsrc/io/vss_store.rs
Comment threadsrc/io/vss_store.rs
let obfuscated_key =
self.build_obfuscated_key(&primary_namespace, &secondary_namespace, &key);

let key_value = KeyValue { key: obfuscated_key, version: -1, value: vec![] };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You say 'might'. Do you mean that just using keys can already be a problem without the version field?

Even if we use the version field in the future, value still isn't needed?

In general, I would avoid dead code (or fields in this case). It just raises questions with devs. At least it did with me. Or otherwise clearly document.

Final question - is a version needed for lazy deletes?

Comment threadsrc/io/vss_store.rs
@tnull
tnull merged commit be3cb48 into lightningdevkit:mainNov 17, 2025
17 checks passed
@github-project-automationgithub-project-automationBot moved this from Goal: Merge to Done in Weekly GoalsNov 17, 2025
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants

@tnull@ldk-reviews-bot@joostjager@enigbe