How to remove the desync between the convex backend and frontend
I send that _creationTime of a document to the frontend. In the frontend, I display how much time has passed since that document was created, by subtracting the now date in frontend - the date from the backend. The problem is that for the first 1-2 seconds, the timer is negative, like this document was created -1 seconds ago, but in after a sec, it goes back to positive. How do I properly sync the times between the convex db and the frontend date.now() ? Is there a trick? I am thinking of just displaying a false timer in the frontend, by checking if the time difference is less then zero and manually shifting it to be positive and continue from there. Is there any solution thats more elegant though?
14 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!
To add: This is not as problematic at this case, since its not really important to know when something was created to the very second and i can lie in the frontend, but it becomes necessary when i for example set a schedule for 30 seconds from now and want to display to the user when that schedule will fire. With this desync, it will fire when the countdown is at 2 seconds left, which is kind of misleading and bad of a ux imo
I believe the creation time on convex is at "UTC-0" so you'll need to take that into consideration, and i suggest you make the time differences in consideration
And new Date().now() returns the same thing
(Between brackets)
You can round it to the next second, so instead of saying 984ms do since 1s or something like that
Sorry i dont get it. You can do new Date().now()? Isnt it either new Date() OR Date.now()? Thats what im using.
I haven't had this problem happen to me before though I'd suggest you run it on the backend instead?
I tried that, but sometimes the desync is 2 or more seconds
Can you show me your formula real quick
wait imma paste the code
Just the date code, and the convex function where the creationtime is coming from
In the backend I have the following code:
export const queueInfo = query({
handler: async (ctx) => {
const user = await getLoggedInUserHelper(ctx);
if (!user) {
throw new Error("User not authenticated");
}
const userEntryInQueue = await ctx.db
.query("matchQueue")
.filter((q) => q.eq(q.field("userId"), user._id))
.first();
if (!userEntryInQueue) {
return { isInQueue: false, queueEntryTime: undefined };
}
return {
isInQueue: true,
queueEntryTime: userEntryInQueue._creationTime,
};
},
});
In the frontend i have this component (the queueInfo is the object returned by useQuery for the above function):
<Container className="flex-col-center gap-4">
{/* We know that queueEmtryTime is defined as isInQueue is true */}
<Timer startTime={new Date(queueInfo.queueEntryTime! )} />
<Button onClick={() => leaveMatchmaking()} variant="outline">
Cancel
</Button>
</Container>
And this is the Timer component
"use client";
import { useCallback, useEffect, useState } from "react";
import { formatDigitalTime } from "../lib/utils";
const Timer = ({ startTime }: { startTime: Date }) => {
const [, setTick] = useState(0);
const updateTimer = useCallback(() => {
setTick((t) => t + 1);
}, []);
// Rerender every second
useEffect(() => {
const interval = setInterval(updateTimer, 1000);
return () => clearInterval(interval);
}, [updateTimer]);
return (
<div className="w-fit mx-auto text-center">
<p className="mb-2">Waiting for other people</p>
<span
className="text-2xl mt-2"
role="timer"
aria-live="polite"
aria-label="Elapsed time"
>
{formatDigitalTime(Date.now() - startTime.getTime())}
</span>
</div>
);
};
export default Timer;
(The formatDigitalTime just returns a timer like 00:01:12 and doesnt change the time)
Try using withIndex instead of filter
Probably that will reduce time
Yea missed that i even had prepared the index but forgot to use lmao. I doubt its that tho couse the table only has 1 entry at any time usually
I'm 50% sure it would reduce time
Otherwise do the frontend logic on your backend
Give me time to test this on my machine, i have bad Internet atm 😅
Thought of that but I would have to call the backend every second which is overkill for a simple timer. Anyways ill see if i can find any solution
Yea sure i dont need this fix urgently, and thanks for the help : )
I changed it to this, and it seems to have solved the problem.
Explanation: I set the timer the display timer to 0 if its negative, and just increment every second.
And this is the countdown component for anyone who has the same issue in the future
This doesnt sove the original question of why the times are not synced though 😞