#time is 0
19 messages · Page 1 of 1 (latest)
The code you've shared is a Convex internal mutation function that updates a project in the database and measures the execution time. This is a good practice for performance monitoring in Convex applications.
Let me explain what this code does:
-
It defines an internal mutation (only callable from other Convex functions, not directly from clients) The Zen of Convex
-
It measures execution time using
Date.now()at the start and end of the function, then logs the total time taken -
It looks up a project by its
projectIdusing an index calledby_project_id -
If the project exists, it updates it with the provided elements after removing undefined keys
-
It returns a boolean indicating success or failure
The timing logs are particularly useful for performance debugging. In Convex, mutations should ideally complete quickly (under 100ms) for optimal application responsiveness, as mentioned in the The Zen of Convex documentation:
Keep sync engine functions light & fast. In general, your mutations and queries should be working with less than a few hundred records and should aim to finish in less than 100ms.
If you're seeing performance issues with this mutation, you might want to:
- Check if the index lookup is efficient
- Examine what's in the
removeUndefinedKeysfunction (not shown in your code) - Consider if the patch operation is updating too many fields at once
For more complex operations that might take longer, you could consider using an action instead of a mutation, especially if you're integrating with external services.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@pine raft Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@loud axle the start and end are both the same value, like the mutation took 0ms to execute... it's strange
Let me check the knowledge base for you again. One moment! :books:
You're right, it is strange that the start and end timestamps are showing the same value, making it appear like the mutation took 0ms to execute. This behavior is actually explained by how Convex handles time in mutations.
According to the Convex documentation, the system time during Convex function execution is "frozen" when the function begins:
To ensure the logic within your function is reproducible, the system time used globally (outside of any function) is "frozen" at deploy time, while the system time during Convex function execution is "frozen" when the function begins.
Date.now()will return the same result for the entirety of your function's execution.
Convex Runtimes Documentation explains this behavior:
export const updateSomething = mutation({
args: {},
handler: () => {
const now1 = Date.now(); // `now1` is the time when the function execution started.
// implementation
const now2 = Date.now(); // `now2` === `now1`
},
});
This is a deliberate design choice to ensure determinism in Convex mutations and queries. Since Convex uses optimistic concurrency control and may need to retry mutations, having deterministic behavior (including time functions) is essential.
If you want to measure execution time within your Convex functions, you could use:
console.time()andconsole.timeEnd()which are supported in the Convex runtime- A counter or other mechanism that doesn't rely on system time
For example:
export const _internal = internalMutation({
args,
handler: async (ctx, args): Promise<boolean> => {
console.time("updateProject");
// Your function logic here
console.timeEnd("updateProject");
return true;
},
})
This will give you an accurate measurement of execution time in your logs.
Hope this helps explain the behavior you're seeing!
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@pine raft Kapa.ai is still learning and improving, please let me know how I did by reacting below.
It looks like the problem comes from useMutation in the client. It takes around 2600s to run it, when the mutation only took 50ms to run on the server, why that
@loud axle It looks like the problem comes from useMutation in the client. It takes around 2600s to run it, when the mutation only took 50ms to run on the server, why that
Let me check the knowledge base for you again. One moment! :books:
Based on the information in the knowledge sources, there are several reasons why a mutation might take significantly longer to complete from the client perspective (2600ms) compared to the server execution time (50ms).
Network Latency and Cold Starts
One major factor could be network latency between your client and the Convex backend. As mentioned in one of the Discord support threads:
The network latency is negligible, but AWS Lambda cold starts are not.
Discord support thread shows that if you're using Node.js actions, there can be significant cold start times that add to the overall latency.
Mutation Execution Process
When you call a mutation from the client using useMutation, there's a complete round-trip process that happens:
- The client sends a message over WebSocket to the Convex sync worker
- The sync worker passes this to the function runner
- The function runner executes the mutation
- The transaction is forwarded to the committer
- The committer appends writes to the transaction log
- The result is sent back to the client
As described in How Convex Works, this entire process involves multiple steps beyond just the function execution time.
Potential Solutions
1. Optimistic Updates
If UI responsiveness is a concern, consider implementing optimistic updates:
const myMutation = useMutation(api.myModule.myMutation).withOptimisticUpdate(
(localStore, args) => {
const existingData = localStore.getQuery(api.myModule.myQuery, queryArgs);
if (existingData !== undefined) {
localStore.setQuery(api.myModule.myQuery, queryArgs, updatedData);
}
}
);
This will make your UI feel more responsive while the actual mutation completes in the background, as explained in Optimistic Updates.
2. Performance Debugging
You can add timing logs on the client side to identify where the bottleneck is occurring:
const handleAction = async () => {
const startTime = performance.now();
await myMutation(args);
console.log(`Total time: ${performance.now() - startTime}ms`);
};
3. Check for Heavy Indexes
As mentioned in one of the Discord threads, queries with large indexes can cause performance issues. If your mutation is calling queries internally, this could be a factor.
4. Deployment Tier
One Discord thread mentioned:
this is on a convex starter instance; wondering if pro helps significantly
If you're on a starter instance, upgrading to a Pro tier might improve performance for production workloads.
If you continue to experience high latency, I'd recommend adding more detailed timing logs both on the client and server to pinpoint exactly where the delay is occurring in the request lifecycle.
Would you like more specific guidance on implementing any of these solutions?
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@pine raft Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@loud axle the server is correctly running my mutation in under a few ms.
But the client takes 2.6s to make it run
Let me check the knowledge base for you again. One moment! :books:
Based on the knowledge sources, there are several potential reasons why your client is taking 2.6s to run a mutation that only takes a few milliseconds on the server.
Schema Validation Overhead
One of the most common causes of client-side latency in Convex is schema validation:
At the moment, queries do parse schemas, yes
Discord support thread shows that large schemas (especially with nested discriminated unions) can significantly impact performance. Some users experienced similar issues with queries taking 1.6+ seconds despite fast server execution.
The Convex team mentioned they're working on optimizing this:
the good news is there are some paths like this that we're working on optimizing that are not intrinsic--they happen to be O(project size) instead of O(function call), but there's no fundamental reason for this
Request Lifecycle Overhead
When a mutation is called from the client, it goes through several steps:
- Client sends request via WebSocket
- Sync worker processes request
- Function runner executes the mutation
- Committer handles transaction
- Result is sent back to client
This entire process is described in How Convex Works and can add overhead beyond just the function execution time.
Potential Solutions
1. Optimistic Updates
Implement optimistic updates to make your UI feel more responsive while the actual mutation completes:
const myMutation = useMutation(api.myModule.myMutation).withOptimisticUpdate(
(localStore, args) => {
// Update local state immediately
const currentData = localStore.getQuery(api.myModule.myQuery, queryParams);
if (currentData !== undefined) {
localStore.setQuery(api.myModule.myQuery, queryParams, updatedData);
}
}
);
As explained in Optimistic Updates, this approach can significantly improve perceived performance.
2. Single-Flighting for Frequent Updates
If you're making frequent mutation calls, consider implementing single-flighting to batch updates:
const myMutation = useMutation(api.my.mutation);
const tryUpdate = useSingleFlight(withLocalStore);
const withLocalStore = useCallback((data) => {
setLocalState(data);
return tryUpdate(data);
}, [setLocalState, updatePresence]);
This pattern is described in Throttling Requests by Single-Flighting and can help reduce the number of mutation calls.
3. Check with Convex Support
The Convex team has been actively working on performance improvements for users with similar issues. You might want to reach out to their support with details about your schema size and deployment, as they've been able to help other users:
Seems like as of now, our dev and staging deployments have been sped up by 5-10x but our production deployment still has pretty high latency.
I recommend implementing optimistic updates as an immediate solution while also considering reaching out to Convex support if the issue persists.