Version 44
Proxy Actor Cache Decorator
Public view
Proxy Actor Cache Decorator
Implement a ProxyActor for the frontend that acts as a decorator for the backend actor, utilizing a module-level singleton architecture. The objective is to manage the actor's lifecycle and data caching independently of the React render cycle.Backend
- Backend actor exposes
getCallerDataVersion()(public query) and maintains per-user version map.
- The backend actor increments the user data version whenever there are changes to data accessible to that user, taking into account visibility restrictions.
- A possible approach is to use a global version and a per-user version, which together define the final version for the user. This speeds up the update process for data that is accessible to all users.
Frontend
Singleton Architecture (Outside React)
- Implement ProxyActor as a custom hook that internally uses useActor. This way, useQueries (and other hooks) will interact with useProxyActor, which in turn delegates to useActor.
- Module-Level State: The Actor reference (
actor), identity (identity), and readiness status (_isReady) must reside as private variables within the file scope, rather than using React'suseStateoruseRef. - Imperative Lifecycle Control: Lifecycle management must be driven by explicit, imperative calls:
initProxyActor(identity): Initializes the singleton when the user authenticates or the principal changes.destroyProxyActor(): Clears references, identity, and the cache upon logout.- React Bridge (Hook): The
useProxyActorhook acts as a thin subscriber that notifies components of changes to_isReadyto trigger re-renders only when the actor's availability state changes.
- The proxy object returned by useProxyActor() must be referentially stable across renders. It must be cached (e.g. via a module-level or ref-based cache) and only reconstructed when the underlying actor instance or _isReady state actually changes. Returning a new object literal on every render — even with identical contents — causes downstream hooks and components to treat it as a changed dependency, triggering continuous remounts and redundant ProxyActor calls. The actor reference from useActor() has stable platform-managed identity and is safe to use in React Query enabled conditions and dependency arrays; the proxy object itself must never appear in those positions.
- Single access point rule: All backend calls must go exclusively through the ProxyActor singleton. No query hook, component, or page may import, reference, or depend on the raw actor or actorFetching state directly. These belong solely to the useProxyActor layer. Query hooks interact with the backend only by calling proxyActor.
() inside queryFn, and gate execution with enabled: isProxyActorReady() && isAuthenticated. Any direct use of actor or actorFetching outside of useProxyActor itself is a pattern violation.
Cache Decorator & Version Synchronization
- All backend calls are routed through the Frontend ProxyActor, which fully decorates backend services and transparently handles caching & synchronization.
- Before serving cached data, ProxyActor checks
getCallerDataVersion; if the version changed or is missing, it invalidates and refreshes the cache. - Cache keys must include backend function name + all argument values, with proper serialization for
BigInt, so calls with different arguments are cached/invalidated independently. - On authentication events (login or identity change), ProxyActor clears the entire cache; the next
getCallerUserProfilemust fetch fresh data, preventing cross-user stale data.
Deduplicaton
- Apply a 5-second staleness window to
getCallerDataVersion(calls within this window return the cached value). - Allow only one in-flight
getCallerDataVersioncall at a time; additional requests wait for it to finish and receive the same result (deduplication).
Logs
- ProxyActor logs each call:
"[ProxyActor][HIT]"when served from cache;"[ProxyActor][MISS]"when fetched from backend.
React Query Configuration
React Query Configuration for Internet Computer Applications All React Query hooks must strictly use the following options for every query:- refetchOnWindowFocus: true
- refetchOnReconnect: false
- refetchOnMount: true
- staleTime: 60_000
- placeholderData: (prev) => prev
- gcTime: 300_000
- These options must be set as global defaults in the QueryClient configuration and must not be overridden by individual hooks unless there is an explicit, documented reason.
- React Query must never trigger backend calls autonomously. All cache invalidation must be driven exclusively by explicit user actions or by the ProxyActor version check mechanism. This ensures that the number of backend calls is minimal and fully predictable.