fix: Correct relations pagination and recursion #2465

Merged
Jade merged 5 commits from Henry-Hiles/continuwuity:quad/fix/relations-pagination into main 2026-11-08 19:34:26 +00:00
Member

Ended up rewriting a fair bit, but should work better.

The only actual behavior change that might be confusing should be explained by https://github.com/matrix-org/matrix-spec/pull/2469. However, the actual code changes may not be good, as I'm not very familiar with Continuwuity's codebase, nor Rust in general. Please tell me if anything needs improving :)

Fixes #2467, fixes #2468, fixes #2469, fixes #2470, and fixes some other smaller bugs I didn't open issues for.

Pull request checklist:

  • This pull request targets the main branch, and the branch is named something other than
    main.
  • I have written an appropriate pull request title and my description is clear.
  • I understand I am responsible for the contents of this pull request.
  • I have followed the contributing guidelines:
Ended up rewriting a fair bit, but should work better. The only actual behavior change that might be confusing should be explained by https://github.com/matrix-org/matrix-spec/pull/2469. However, the actual code changes *may* not be good, as I'm not very familiar with Continuwuity's codebase, nor Rust in general. Please tell me if anything needs improving :) Fixes #2467, fixes #2468, fixes #2469, fixes #2470, and fixes some other smaller bugs I didn't open issues for. <!-- In order to help reviewers know what your pull request does at a glance, you should ensure that 1. Your PR title is a short, single sentence describing what you changed 2. You have described in more detail what you have changed, why you have changed it, what the intended effect is, and why you think this will be beneficial to the project. If you have made any potentially strange/questionable design choices, but didn't feel they'd benefit from code comments, please don't mention them here - after opening your pull request, go to "files changed", and click on the "+" symbol in the line number gutter, and attach comments to the lines that you think would benefit from some clarification. --> <!-- Example: This pull request allows us to warp through time and space ten times faster than before by double-inverting the warp drive with hyperheated jump fluid, both making the drive faster and more efficient. This resolves the common issue where we have to wait more than 10 milliseconds to engage, use, and disengage the warp drive when travelling between galaxies. --> <!-- Closes: #... --> <!-- Fixes: #... --> <!-- Uncomment the above line(s) if your pull request fixes an issue or closes another pull request by superseding it. Replace `#...` with the issue/pr number, such as `#123`. --> **Pull request checklist:** <!-- You need to complete these before your PR can be considered. If you aren't sure about some, feel free to ask for clarification in #dev:continuwuity.org. --> - [x] This pull request targets the `main` branch, and the branch is named something other than `main`. - [x] I have written an appropriate pull request title and my description is clear. - [x] I understand I am responsible for the contents of this pull request. - I have followed the [contributing guidelines][c1]: - [x] My contribution follows the [code style][c2], if applicable. - [ ] I ran [pre-commit checks][c1pc] before opening/drafting this pull request. - [x] I have [tested my contribution][c1t] (or proof-read it for documentation-only changes) myself, if applicable. This includes ensuring code compiles. - [x] My commit messages follow the [commit message format][c1cm] and are descriptive. <!-- Notes on these requirements: - While not required, we encourage you to sign your commits with GPG or SSH to attest the authenticity of your changes. - While we allow LLM-assisted contributions, we do not appreciate contributions that are low quality, which is typical of machine-generated contributions that have not had a lot of love and care from a human. Please do not open a PR if all you have done is asked ChatGPT to tidy up the codebase with a +-100,000 diff. - In the case of code style violations, reviewers may leave review comments/change requests indicating what the ideal change would look like. For example, a reviewer may suggest you lower a log level, or use `match` instead of `if/else` etc. - In the case of code style violations, pre-commit check failures, minor things like typos/spelling errors, and in some cases commit format violations, reviewers may modify your branch directly, typically by making changes and adding a commit. Particularly in the latter case, a reviewer may rebase your commits to squash "spammy" ones (like "fix", "fix", "actually fix"), and reword commit messages that don't satisfy the format. - Pull requests MUST pass the `Checks` CI workflows to be capable of being merged. This can only be bypassed in exceptional circumstances. If your CI flakes, let us know in matrix:r/dev:continuwuity.org. - Pull requests have to be based on the latest `main` commit before being merged. If the main branch changes while you're making your changes, you should make sure you rebase on main before opening a PR. Your branch will be rebased on main before it is merged if it has fallen behind. - We typically only do fast-forward merges, so your entire commit log will be included. Once in main, it's difficult to get out cleanly, so put on your best dress, smile for the cameras! --> [c1]: https://forgejo.ellis.link/continuwuation/continuwuity/src/branch/main/CONTRIBUTING.md [c2]: https://forgejo.ellis.link/continuwuation/continuwuity/src/branch/main/docs/development/code_style.mdx [c1pc]: https://forgejo.ellis.link/continuwuation/continuwuity/src/branch/main/CONTRIBUTING.md#pre-commit-checks [c1t]: https://forgejo.ellis.link/continuwuation/continuwuity/src/branch/main/CONTRIBUTING.md#running-tests-locally [c1cm]: https://forgejo.ellis.link/continuwuation/continuwuity/src/branch/main/CONTRIBUTING.md#commit-messages
Ended up rewriting a fair bit, but should work better.
chore: Make Clippy happy
Some checks failed
Documentation / Build and Deploy Documentation (pull_request) Has been skipped
Auto Labeler / Apply labels based on changed files (pull_request_target) Successful in 5s
Checks / Prek / Check changed files (pull_request) Successful in 6s
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 14s
Checks / Prek / Pre-commit & Formatting (pull_request) Successful in 2m13s
Checks / Prek / Clippy and Cargo Tests (pull_request) Failing after 40m51s
0a4aa77950
chore: Add changelog to #2465
Some checks failed
Documentation / Build and Deploy Documentation (pull_request) Has been skipped
Checks / Prek / Check changed files (pull_request) Successful in 9s
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 6s
Checks / Prek / Pre-commit & Formatting (pull_request) Successful in 21m11s
Checks / Prek / Clippy and Cargo Tests (pull_request) Failing after 40m48s
bfe81cfbd0
Owner

@Jade You've touched relations somewhat before, do you want to review this?

@Jade You've touched relations somewhat before, do you want to review this?
Aranjedeath left a comment

no new explosions (threads work better)

no new explosions (threads work better)
eleboucher requested changes 2026-11-07 08:54:45 +00:00
Dismissed
eleboucher left a comment

Looks good, but some perf issue that may arise with this patch

Looks good, but some perf issue that may arise with this patch
@ -147,28 +151,49 @@ async fn paginate_relations_with_filter(
.unwrap_or(30)
.min(100);
Contributor

limit=0 loops: has_more is true, events is empty after truncate, so next_batch == from. .clamp(1, 100)?

`limit=0` loops: has_more is true, events is empty after truncate, so next_batch == from. `.clamp(1, 100)`?
Author
Member

Uh, can you clarify?

I think this is correct behavior.

For a loop to happen, there would need to be a misbehaving client that passes limit=0 but spams the endpoint until it has events, but I mean, that's not something the server should handle? Clients shouldn't (and don't) pass limit=0 when they want events.

I don't see a reason to always return at least 1.

Uh, can you clarify? I think this is correct behavior. For a loop to happen, there would need to be a misbehaving client that passes `limit=0` but spams the endpoint until it has events, but I mean, that's not something the server should handle? Clients shouldn't (and don't) pass `limit=0` when they want events. I don't see a reason to always return at least 1.
Contributor

Fair, it only loops with a broken client. Fine as is

Fair, it only loops with a broken client. Fine as is
Henry-Hiles marked this conversation as resolved
@ -39,0 +38,4 @@
let mut replace_events = Vec::new();
let mut reference_events = Vec::new();
self.db
.get_relations(room_id, target, PduCount::max(), Direction::Backward)
Contributor

No cap anymore, so a message with 5k reactions loads 5k PDUs on every /sync and /messages it shows up in. Can we keep a cap on the raw scan, maybe a few hundred?

No cap anymore, so a message with 5k reactions loads 5k PDUs on every /sync and /messages it shows up in. Can we keep a cap on the raw scan, maybe a few hundred?
Author
Member

The cap didn't properly take effect before, it was there but basically useless. I can add a cap though, maybe a maintainer can also give me an opinion on what to cap it to.

The cap didn't properly take effect before, it was there but basically useless. I can add a cap though, maybe a maintainer can also give me an opinion on what to cap it to.
Author
Member

I think the proper fix here is actually indexing this column by relation type, so for stuff like bundled aggregations where it's only edits, we only need to search through edits.

I think a separate PR would be best for this though, and though it looks like there was a cap of 50 before, it never actually applied, so this is not a regression.

I think the proper fix here is actually indexing this column by relation type, so for stuff like bundled aggregations where it's only edits, we only need to search through edits. I think a separate PR would be best for this though, and though it looks like there was a cap of 50 before, it never actually applied, so this is not a regression.
Contributor

You're right, with max_depth 0 the old code collected everything and never hit the limit check. Not a regression then, fine to do the rel_type index in a follow-up.

You're right, with max_depth 0 the old code collected everything and never hit the limit check. Not a regression then, fine to do the rel_type index in a follow-up.
Henry-Hiles marked this conversation as resolved
@ -60,2 +57,3 @@
&self,
shortroomid: ShortRoomId,
target: ShortEventId,
target: u64,
Contributor

nit: keep ShortEventId here

nit: keep `ShortEventId` here
Author
Member

Why?

This was never a short event Id, it's the event's position in the timeline (a pdu count) which is different data. The two types are just compatible, but it doesn't make sense to use here, as far as I can tell.

Why? This was never a short event Id, it's the event's position in the timeline (a pdu count) which is different data. The two types are just compatible, but it doesn't make sense to use here, as far as I can tell.
Contributor

Right, it was always a pdu count. Ignore.

Right, it was always a pdu count. Ignore.
Henry-Hiles marked this conversation as resolved
@ -113,3 +87,3 @@
}
pdus
let scan_from = match dir {
Contributor

With recurse, this loads every PDU in the tree (3 levels) on every page, then truncates to limit. Forward always scans from min. Could the walk use counts only, then sort and load PDUs lazily until limit + 1 match?

With recurse, this loads every PDU in the tree (3 levels) on every page, then truncates to `limit`. Forward always scans from min. Could the walk use counts only, then sort and load PDUs lazily until `limit + 1` match?
Author
Member

Good idea, I will add this.

Good idea, I will add this.
Author
Member

Added. All my client's tests still pass.

Added. All my client's tests still pass.
Henry-Hiles marked this conversation as resolved
chore: Make walk use counts only
All checks were successful
Documentation / Build and Deploy Documentation (pull_request) Has been skipped
Checks / Prek / Check changed files (pull_request) Successful in 9s
Checks / Prek / Clippy and Cargo Tests (pull_request) Successful in 8m58s
Checks / Prek / Pre-commit & Formatting (pull_request) Successful in 20m55s
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 7s
eec328ecb4
eleboucher approved these changes 2026-11-07 17:05:35 +00:00
eleboucher left a comment

LGTM

LGTM
Jade approved these changes 2026-11-08 18:10:09 +00:00
Dismissed
Jade left a comment

TBH this is running into the same things I was when trying to use the existing relations database table, it's fine for now.

TBH this is running into the same things I was when trying to use the existing relations database table, it's fine for now.
@ -116,0 +99,4 @@
.await;
seen.extend(level.iter().copied());
for _ in 0..max_depth {
let next: Vec<Vec<_>> = level
Owner

Can you break out of this loop when there's nothing in level?

Can you break out of this loop when there's nothing in level?
Jade marked this conversation as resolved
Author
Member

@Jade wrote in #2465 (comment):

TBH this is running into the same things I was when trying to use the existing relations database table, it's fine for now.

I'm planning to revisit this, with a migration, Ginger has more details.

@Jade wrote in https://forgejo.ellis.link/continuwuation/continuwuity/pulls/2465#issuecomment-39080: > TBH this is running into the same things I was when trying to use the existing relations database table, it's fine for now. I'm planning to revisit this, with a migration, Ginger has more details.
chore: break out of loop when level is empty
Some checks failed
Documentation / Build and Deploy Documentation (pull_request) Has been skipped
Checks / Prek / Check changed files (pull_request) Successful in 7s
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 9s
Checks / Prek / Pre-commit & Formatting (pull_request) Successful in 1m1s
Checks / Prek / Clippy and Cargo Tests (pull_request) Failing after 40m47s
4ec8db894b
Jade approved these changes 2026-11-08 19:33:44 +00:00
Jade merged commit be5ff91eb3 into main 2026-11-08 19:34:26 +00:00
Jade referenced this pull request from a commit 2026-11-08 19:34:27 +00:00
Jade deleted branch quad/fix/relations-pagination 2026-11-08 19:34:28 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No assignees
5 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
continuwuation/continuwuity!2465
No description provided.