-
Notifications
You must be signed in to change notification settings - Fork 435
Add PaginatedKVStore traits upstreamed from ldk-server #4347
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add PaginatedKVStore traits upstreamed from ldk-server #4347
Conversation
|
👋 I see @tnull was un-assigned. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #4347 +/- ##
==========================================
- Coverage 86.08% 86.03% -0.06%
==========================================
Files 156 156
Lines 102416 102717 +301
Branches 102416 102717 +301
==========================================
+ Hits 88165 88370 +205
- Misses 11759 11843 +84
- Partials 2492 2504 +12
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
lightning/src/util/persist.rs
Outdated
|
|
||
| /// A token that can be used to retrieve the next set of keys. | ||
| /// The `String` is the last key from the current page and the `i64` is | ||
| /// the associated `time` used for ordering. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think most DBs are going to support this? "sort by the last-changed as of time X" isn't possible without storing what the last-updated time was prior to X when something is updated after X.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ldk-server does it based on creation_at not updated_at, will update the docs to make that more clear
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, do all of our backend storage things have a created-at time? I guess if you want it time-ordered you have to do it that way but it adds quite a requirement :/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, presumably that needs to be upstreamed to ldk-node and maybe also vss-server? I imagine we want pagination there too?
5160a11 to
3fb869f
Compare
lightning/src/util/persist.rs
Outdated
| /// when dealing with a large number of keys that cannot be efficiently retrieved all at once. | ||
| /// | ||
| /// For an asynchronous version of this trait, see [`PaginatedKVStore`]. | ||
| pub trait PaginatedKVStoreSync: Send + Sync { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why doesn't this just extend KVStoreSync? Seems weird to duplicate the interface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am too used to having to create wrappers for ldk traits, I am actually in ldk now though! Fixed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can add extension traits in downstream crates too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can add extension traits in downstream crates too?
To give some 'historic' context here, there were discussions on whether or not to make this an extension trait (cf. lightningdevkit/ldk-server#22). In the end, the PR author decided against it, also because it was anticipated that pagination might require two separate implementations.
However, IMO, now would be a good time to at least consider rethinking and double-checking some the design choices made, as there is still time to fundamentally LDK Server's persistence model, before we cut the first release (cc @jkczyz as he was also part of the original discussions, not sure if he has an opinion now).
In particular, I want to throw out some questions to consider:
- Do we really want to maintain separate implementations for all backends and have LDK Node take different store types for 'general purpose' vs 'payment data'?
- Either way, couldn't we still make
PaginatedKVStorean extension trait? - If we will require it in LDK Node for payment data, will we be able to add pagination to all supported backends? if so, should this even be just a breaking change to
KVStore::listrather than making it an extension trait?
Somewhat relatedly, note that from my point of view our endgoal should be that also all backend implementations (incl. VSS, SQLite, Postgres) eventually live in lightning-persister, and that preferably very little or no custom logic would live on the LDK Node/Server side. IMO this top-down approach would give clear interfaces and implementations across the board and avoid risking running into edge cases when making conflicting decisions on implementation details in different crates.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really want to maintain separate implementations for all backends and have LDK Node take different store types for 'general purpose' vs 'payment data'?
Ideally not indeed.
Either way, couldn't we still make PaginatedKVStore an extension trait?
Definitely should be (it is now in this PR).
If we will require it in LDK Node for payment data, will we be able to add pagination to all supported backends?
Yea, see above discussion. Requiring that the paginated list be in created_at order means that most of our backends will have to actually store a created_at time (or have an incrementing row id at least). At least the only one in lightning-persister (filesystem store) already has it, but the SQLite/Postgres/VSS backends will all need it.
For that reason I'm really not a fan of sorting by created_at but it does certainly make the client easier. Another option would be to only require that things be listed in key-order and then make sure that when we want pagination we store a key that is ordered, but that doesn't work for VSS cause the key is encrypted garbage (so we'd have to have some crazy abstraction-break there). Dunno if anyone has any more creative ideas.
Somewhat relatedly, note that from my point of view our endgoal should be that also all backend implementations (incl. VSS, SQLite, Postgres) eventually live in lightning-persister, and that preferably very little or no custom logic would live on the LDK Node/Server side. IMO this top-down approach would give clear interfaces and implementations across the board and avoid risking running into edge cases when making conflicting decisions on implementation details in different crates.
👍
ebb2e5d to
95b82f8
Compare
ffdf5c9 to
77d2cac
Compare
|
Updated with @TheBlueMatt's suggestions, now only uses |
0105148 to
ac4e907
Compare
|
implemented the paginated kv store for the file system store |
| /// Returns a paginated list of keys that are stored under the given `secondary_namespace` in | ||
| /// `primary_namespace`, ordered from most recently created to least recently created. | ||
| /// | ||
| /// Implementations must return keys in reverse creation order (newest first). How creation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I think this might have been discussed above, but could you reiterate why we do creation time, not last update time? The latter seems like the obvious contender to me when we'd like to list payments, in particular, so it seems this might require that we go through all entries and sort them in-memory again for the most common usecase?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the problem with using updated at time is that it can be prone to race conditions, added to the docs
lightning/src/util/persist.rs
Outdated
| /// A vector of keys, ordered from most recently created to least recently created. | ||
| pub keys: Vec<String>, | ||
|
|
||
| /// The last key in this page, which can be passed to the next call to continue pagination. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While using the last key specifically matches what we do in VSS, requiring the page token to be the last key is a stricter requirement than what the VSS interface guarantees. Could we maybe just make this an opaque next_page_token field without any special semantics? Then the implementation could still use the last key if it works for it, but we wouldn't require specific semantics?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay switched to something more generic
Allows for a paginated KV store for more efficient listing of keys so you don't need to fetch all at once. Uses monotonic counter or timestamp to track the order of keys and allow for pagination. The traits are largely just copy-pasted from ldk-server. Adds some basic tests that were generated using claude code.
Test created with claude code
ac4e907 to
3a60588
Compare
| // Get file creation time, falling back to modified time if unavailable. | ||
| let metadata = entry.metadata()?; | ||
| let created_time = metadata | ||
| .created() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe in most filesystems the created time is actually gonna be the last-update time because we overwrite-on-update. Thus, I don't think its possible to implement the pagination for the filesystem store (without including additional metadata in the filenames).
Sorry I had earlier assumed this would work but I dont think so.
| assert_eq!(response.keys[0], "key_e"); | ||
| assert_eq!(response.keys[4], "key_a"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can assert is_sorted
Allows for a paginated KV store for more efficient listing of keys so you don't need to fetch all at once. Uses monotonic counter or timestamp to track the order of keys and allow for pagination. The traits are largely just copy-pasted from ldk-server.
This also adds a PaginatedKVStoreSyncAdapter/PaginatedKVStoreAdapter so you can use a paginated kv store in contexts that expect a regular key value store.
Adds some basic tests that were generated using claude code.