-
Notifications
You must be signed in to change notification settings - Fork 21
Add Sqlite based PaginatedKVStore impl. #23
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
Conversation
|
Rebased on top of main, adjusted tests and table acc. to paginated store. |
|
Once we upstream this trait, we should be able to reuse at least the |
jkczyz
left a comment
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.
Limited mostly to a diff against upstream.
| // The maximum number of keys retrieved per page in paginated list operation. | ||
| const LIST_KEYS_MAX_PAGE_SIZE: i32 = 100; |
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.
Don't have a strong opinion, but should we consider something smaller?
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.
Since these are not mobile nodes, I think 100 is very small set of records for any server deployment.
We can change it to 50 if you had like, whatever value we choose, it is not a big deal since we can always adjust this without any api breaking changes.
A lower number also means higher RTT latency overhead.
| // The current SQLite `user_version`, which we can use if we'd ever need to do a schema migration. | ||
| const SCHEMA_USER_VERSION: u16 = 1; |
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.
Should we use 2 to match upstream? If this eventually implements both traits, it would be nice if downgrading from paginated was possible. Or at very least to avoid confusion with two different versions defined. Or if not possible, should we use 3 instead?
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.
This one uses a separate db hence the separate schema version.
I think the way to upstream/migrate would be we introduce new table in upstream and at that time change the schema version of that db. After that, we can stop relying on this store and start using paginated queries from upstream apis.
| let locked_conn = self.connection.lock().unwrap(); | ||
|
|
||
| let sql = format!( | ||
| "SELECT key FROM {} WHERE primary_namespace=:primary_namespace AND secondary_namespace=:secondary_namespace AND key > :page_token ORDER BY creation_time DESC LIMIT :page_size", |
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 is a bug in pagination that needs to be fixed, we need an order by on key as well i think.
page_token/ordering/pagination needs to account for both creation_time and key.
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.
Fixed in latest commit.
ldk-server/src/io/utils.rs
Outdated
| PrintableString(primary_namespace), PrintableString(secondary_namespace), PrintableString(key)); | ||
| "Failed to {} {}/{}/{}: primary namespace may not be empty if a non-empty secondary namespace is given.", | ||
| operation, | ||
| PrintableString(primary_namespace), PrintableString(secondary_namespace), PrintableString(key)); |
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.
Surprised rustfmt doesn't move the closing parenthesis down to a new line. Maybe that confused it?
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 think depends on whether it can fit all args in single line or not, otherwise it would have placed them on separate line each.
Moved parenthesis to new line manually.
| let sql = format!( | ||
| "SELECT key FROM {} WHERE primary_namespace=:primary_namespace AND secondary_namespace=:secondary_namespace AND key > :page_token ORDER BY creation_time DESC LIMIT :page_size", | ||
| "SELECT key, creation_time FROM {} WHERE primary_namespace=:primary_namespace AND secondary_namespace=:secondary_namespace \ | ||
| AND ( creation_time < :creation_time_token OR (creation_time = :creation_time_token AND key > :key_token) ) \ |
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.
Not sure that I understand why we need creation_time_token. Shouldn't the new ORDER BY suffice?
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.
It will not suffice, here is an example :
Input:
Data:
| Key | Creation Time |
|---|---|
| A | 1 |
| C | 2 |
| B | 3 |
| E | 4 |
| D | 5 |
Page Size: 2
Paginated Output should be:
- First Page:
D, E. , page_token:E, 4 - Second Page:
B, C, page_token:C, 2 - Third Page:
A, page_token:None
Behavior Without a Creation Time Token:
- Output:
D, E, page_token:E - Reason: Since there is no key greater than
E, no additional pages are available.
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.
Main reason this happens is because we prioritize ordering by creation_time for pagination.
And we can't just use creation_time for pagination as there might be 2 keys with the same creation_time resulting in duplicate or missed results.
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.
Ah, right. That makes sense. Thanks for explaining. Can the condition be reduced to the following?
creation_time <= :creation_time_token AND key > :key_token
Though that would unnecessarily preform a key comparison when not needed, so maybe best to avoid?
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.
No it can't be reduced to that, that too will lead to wrong pagination results in the same example.
Note: Keys after page-1 are less than E
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.
Gotcha. Yeah, I see now.
jkczyz
left a comment
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.
LGTM. Please squash.
| let sql = format!( | ||
| "SELECT key FROM {} WHERE primary_namespace=:primary_namespace AND secondary_namespace=:secondary_namespace AND key > :page_token ORDER BY creation_time DESC LIMIT :page_size", | ||
| "SELECT key, creation_time FROM {} WHERE primary_namespace=:primary_namespace AND secondary_namespace=:secondary_namespace \ | ||
| AND ( creation_time < :creation_time_token OR (creation_time = :creation_time_token AND key > :key_token) ) \ |
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.
Gotcha. Yeah, I see now.
|
Squashes fixups! |
For now we have different table and different DB for this.
Once we upstream this trait, we should be able to reuse at least the
readandremovemethods forKVStoreandPaginatedKVStore. For write and list, we can either have separate implementations in each trait or introduce minor logic deduplication.Mainly adapts the existing implementation for sql_lite in ldk-node for
PaginatedKVStorei.e. support for: