Skip to content

Conversation

@G8XSU
Copy link
Contributor

@G8XSU G8XSU commented Nov 14, 2024

  • Add Sqlite based PaginatedKVStore impl.

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 read and remove methods for KVStore and PaginatedKVStore. 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 PaginatedKVStore i.e. support for:

  • Paginated list operations using next-page-token
  • Time based descending ordering of list keys.

@G8XSU G8XSU requested a review from jkczyz November 14, 2024 18:10
@G8XSU
Copy link
Contributor Author

G8XSU commented Nov 26, 2024

Rebased on top of main, adjusted tests and table acc. to paginated store.

@G8XSU
Copy link
Contributor Author

G8XSU commented Nov 26, 2024

Once we upstream this trait, we should be able to reuse at least the read and remove methods for KVStore and PaginatedKVStore. For write and list, we can either have separate implementations in each trait or introduce minor logic deduplication.

Copy link
Contributor

@jkczyz jkczyz left a 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.

Comment on lines +18 to +19
// The maximum number of keys retrieved per page in paginated list operation.
const LIST_KEYS_MAX_PAGE_SIZE: i32 = 100;
Copy link
Contributor

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?

Copy link
Contributor Author

@G8XSU G8XSU Dec 2, 2024

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.

Comment on lines +15 to +16
// 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;
Copy link
Contributor

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?

Copy link
Contributor Author

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",
Copy link
Contributor Author

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in latest commit.

@G8XSU G8XSU requested a review from jkczyz December 3, 2024 19:56
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));
Copy link
Contributor

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?

Copy link
Contributor Author

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) ) \
Copy link
Contributor

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?

Copy link
Contributor Author

@G8XSU G8XSU Dec 4, 2024

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:

  1. First Page: D, E. , page_token: E, 4
  2. Second Page: B, C, page_token: C, 2
  3. 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.

Copy link
Contributor Author

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.

Copy link
Contributor

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?

Copy link
Contributor Author

@G8XSU G8XSU Dec 4, 2024

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

Copy link
Contributor

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.

@G8XSU G8XSU requested a review from jkczyz December 4, 2024 18:37
@G8XSU G8XSU mentioned this pull request Dec 4, 2024
9 tasks
Copy link
Contributor

@jkczyz jkczyz left a 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) ) \
Copy link
Contributor

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.

@G8XSU
Copy link
Contributor Author

G8XSU commented Dec 4, 2024

Squashes fixups!

@G8XSU G8XSU merged commit 31026d6 into lightningdevkit:main Dec 4, 2024
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants