Paginated Queries correctly when multiple concurrent mutations
I need help implementing Paginated Queries correctly when multiple concurrent mutations (create, delete, and update operations) affect the record count and pagination cursor.
Consider a ChatGPT-like interface with a list of messages. I'm using paginated queries to fetch the N most recent messages and display them to the user with an option to "Load earlier messages."
When a user edits a message, the conversation restarts from that point. For example, in a chat with 100 messages where the last 20 are displayed due to pagination, editing message #85 would trigger two actions: the backend would delete all messages after #85, and the AI would generate a new response to the edited message. This would result in a chat containing 86 messages.
Currently, my "update message" mutation performs the following:
1. Update the edited message
2. Add a placeholder message for the AI response
3. Trigger (schedule) an action to delete all messages after the edited message and before the placeholder AI message.
4. Trigger (schedule) an action to connect to OpenAI API and incrementally update the placeholder AI message as the response streams in.
My pagination is messed up! As these updates are happening,
usePaginatedQuery
keeps getting triggered, and I constantly see a "hit error" saying "InvalidCursor: Tried to run a query starting from a cursor, but it looks like this cursor is from a different query."7 Replies
Thanks for posting in <#1088161997662724167>.
Reminder: If you have a Convex Pro account, use the Convex Dashboard to file support tickets.
- Provide context: What are you trying to achieve, what is the end-user interaction, what are you seeing? (full error message, command output, etc.)
- Use search.convex.dev to search Docs, Stack, and Discord all at once.
- Additionally, you can post your questions in the Convex Community's <#1228095053885476985> channel to receive a response from AI.
- Avoid tagging staff unless specifically instructed.
Thank you!
This sounds pretty reasonable. Can you share the code for the paginated query?
I wouldn't expect the query as you described it to be throwing InvalidCursor errors
Sure! This is the helper function that my query calls:
This is the actual query that calls the above helper:
I'm using this custom hook on the frontend:
Cool so InvalidCursor indicates that the paginated query's bounds are changing. And the reason is the
Date.now()
, which is different every time the query runs.
To fix, you can conditionally remove that part of the filter
Thanks, Lee! This partially fixed my problem. I don't have InvalidCursor anymore but updates to messages (e.g., updating the AI placeholder message as the AI response streams) does not work; looks like the React state does not change. I don't know why but I'm going to try to figure it out.
For anyone looking at this thread, I thought I should provide the following information. This assignment doesn't work:
q = q.lt("_creationTime", beforeThisCreationTime);
. So I ended up doing this:
I think the IndexRangeBuilder
works by updating the type parameter FieldNum
so to enforce that we proceed through the fields in index order. I don't know if there is a more elegant way of doing this (excpet going back to chaining).
Going back to chaining the queries, I thought I can use Number.MAX_SAFE_INTEGER
instead of Date.now()
and it seems to work:
@lee if you have any thoughts on why updating any of the messages does not update the React state, please let me know. I'll try to figure it out too, and if I find the problem, will report here for reference for others. Thanks again.
PS. by update does not work I mean, let's say I have a list of messages. If I even directly update a message in the database, I don't see the update in the app. The "reactivity" which was creating the realtime feature is lost! And I'm not sure if the problem is the paginated query or something else.Good job figuring out the typescript types there
I don't see why reactivity isn't working. Things to check: When the data changes, do you see queries rerun in the convex dashboard logs page? Do you see the
useEffect(..., [results])
in your custom hook rerunning?Thanks Lee! Yes, the problem was somewhere else in my code and it is resolved now (irrelevant to this discussion). So, I can confirm for anyone reading this thread, the with paginate query was due to using
Date.now()