get-convex / get-convex/convex-react-query

Decouple subscription lifetime from cache retention (stale-while-revalidate)

Open
#30 2 comments 1 reaction 0 assignees View on GitHub
Dominant language
TypeScript
Stars
41
Forks
7
PR merge metrics
No merged PRs in 30d

Description

## Problem

If I understand the implementation correctly, `gcTime` currently controls both how long the Convex WebSocket subscription stays alive **and** how long cached data is retained after unmount. When `gcTime` expires, the `"removed"` event fires, `unsubscribe()` is called, and the data is evicted from TanStack Query's cache simultaneously.

This means there doesn't seem to be a way to get a stale-while-revalidate pattern:

- **Short `gcTime`** = saves bandwidth but loses cached data → loading flash on remount
- **Long `gcTime`** = instant remount but keeps subscription open → unnecessary bandwidth for unmounted components

## Proposed behavior

Use `staleTime` and `gcTime` with their natural TanStack Query semantics:

| Setting | Current behavior | Proposed behavior |
|---|---|---|
| `staleTime` | Set to `Infinity` (data is never stale while subscribed) | Controls how long the **subscription** stays alive after all observers unmount. While subscribed = fresh. |
| `gcTime` | Controls both subscription **and** cache lifetime | Controls how long **stale cached data** is retained after the subscription is dropped |

### Example

```ts
convexQuery(api.messages.list, { channel: "general" }, {
staleTime: 10_000, // drop subscription 10s after unmount
gcTime: 5 * 60_000, // keep last-known data in cache for 5 min
})
```

### Remount behavior

1. **0–10s after unmount**: subscription alive, data fresh, instant remount
2. **10s–5min after unmount**: subscription dropped (no bandwidth cost), stale data shown instantly on remount, new subscription opened in background, UI updates when first message arrives
3. **After 5min**: cache evicted, remount = loading state + new subscription

## Why this matters

Convex subscriptions have a real bandwidth cost — the server keeps pushing updates over WebSocket even when no component is consuming the data. For apps with many routes/views, keeping subscriptions alive for 5 minutes after navigating away adds up.

The stale-while-revalidate pattern would be the sweet spot: no bandwidth waste while unmounted, but no loading flash on remount either.

## Possible implementation sketch

1. On observer count dropping to 0, start a `staleTime` timer (instead of relying solely on TanStack's `gcTime`-based `"removed"` event)
2. When `staleTime` timer fires, call `unsubscribe()` but **leave the data in TanStack's cache** (mark as stale)
3. On re-observe (component remounts), TanStack sees stale data → shows it immediately → adapter re-subscribes → pushes fresh data into cache when first update arrives
4. `gcTime` continues to control when the cache entry itself is evicted

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.