Convex caches the result of every query, keyed on its arguments and on everything the handler reads—including real wall-clock time. A query that observes time (directly viaDocumentation Index
Fetch the complete documentation index at: https://confect.dev/llms.txt
Use this file to discover all available pages before exploring further.
Date.now(), or indirectly through APIs that read it) is evicted from the cache much more aggressively and is re-executed on subsequent calls, even when its inputs are unchanged.
Effect programs touch the clock far more often than ordinary JavaScript: forking a fiber, emitting a log, or opening a span all read the current time internally. Without intervention, every Confect-wrapped query would defeat the cache.
To keep queries cacheable by default, Confect stubs the clock for the span of a query handler:
Date.now()returns0.- The
unsafe*methods on Effect’sClockservice (used internally by logging, span events, the default scheduler, andFiberconstruction) return constants (0and0n). - The user-facing
Clock.currentTimeMillisandClock.currentTimeNanoseffects are the only way to read real time, and they are an explicit opt-in to cache invalidation.
What stays cached
The following do not observe real time from within a query handler and so do not interfere with caching:Effect.log/Effect.logInfo/ etc.Effect.withSpanand other tracing primitives- Forking fibers, the default scheduler, and other Effect internals
- A bare
Date.now()call inside the handler (returns0)
Opting in to real time
Use Effect’sClock service when you need the real timestamp:
Clock.currentTimeMillis or Clock.currentTimeNanos will be evicted from Convex’s cache and re-executed on every subscription update, matching Convex’s behavior for any query that observes real time.
Preferring cacheable alternatives
If you don’t actually need sub-second accuracy inside the query, follow Convex’s recommended patterns to preserve caching:- Pass a coarse timestamp (e.g. rounded to the minute or hour) as a query argument from the client.
- Maintain a boolean or status field on the document, updated by a scheduled function or mutation.
- Compute time-dependent values in a mutation or action and persist the result.
Caveats
StubbingDate.now() inside the handler means library code that calls it during a query—logging timestamps, cache keys, ID generation—will see 0 rather than the real time. This is almost always what you want for a deterministic, cacheable query, but it can surprise code that was written assuming a real clock. If such code must run inside a query, route it through Clock.currentTimeMillis instead and accept the cache trade-off.