Skip to main content
Caching makes repeat presents instant and is the foundation of FlowPilot’s offline resilience. A resolved flow is kept in memory and on disk, and the SDK resolves a placement by walking a fail-safe chain that prefers a warm cache and degrades gracefully when the network is slow or gone.

The fail-safe resolution chain

Every present and prefetch resolves a placement through the same ordered chain. Each tier is tried until one yields a presentable flow: If no tier yields a presentable flow, the resolve fails, and you handle it with the host fallback (resolveSession returns null, or the presenter fallback renders). Every analytics event carries the tier it was served from as delivery_source (network, cache, stale_cache, bundled_default), so offline and fallback renders are distinguishable in the dashboard. See Analytics.

The resolve deadline does not strand the cache

A live resolve that hits resolveTimeout degrades the current caller to a fallback tier, but it does not cancel the in-flight request. The resolve keeps running to populate the cache, so the next caller hits a warm Tier 0. A slow network therefore never hangs onboarding, and the cost of a slow first present is not wasted. Concurrent resolves for the same key are also coalesced into one round-trip, so a prefetch and a present that race share a single request.

The no-fallback second chance

There is one case where the short resolveTimeout buys nothing: a present-time resolve where Tier 0 and Tier 2 both miss and no bundled default (Tier 3) is registered. Failing fast there would just strand the user on a blank screen, so the SDK instead waits up to resolveNoFallbackTimeout (default 12 seconds, floored at resolveTimeout) for the real flow rather than giving up at 4s. The still-in-flight resolve usually lands within that window (for example a cold backend that answers in ~6s). When a stale or bundled tier is available, the short resolveTimeout still applies, so a slow network degrades instantly. Set resolveNoFallbackTimeout at or below resolveTimeout to disable this second chance. See Configuration.

Cache freshness (TTL)

A cached flow is “fresh” for a number of seconds, after which Tier 0 misses and the SDK re-resolves (falling back to the same entry as stale cache if the network fails). The TTL comes from the resolve response’s cacheTtlSeconds; when the backend omits it, the SDK uses the environment default:
Because development defaults to a 0 TTL, prefetching and “fast repeat present” have no visible effect there. Test caching behavior against staging or production. See Configuration.

Identity-scoped cache keys

Cached flows are keyed by an identity fingerprint, a hash of the per-install user ID and any targeting attributes the resolve was personalized by. This guarantees a flow cached for one user (or one A/B variant) is never served to another after an identity change. The stale-cache tier keeps a fingerprint-agnostic last-known-good alias, so a previously-seen flow can still render offline even right after the identity changes.

Enabling and disabling caching

Caching is on by default. Turn it off with cachingEnabled: false:
Disabling caching also disables prefetching (there is nothing to retain), so leave it on unless you have a specific reason not to.

Clearing the cache

clearCache() is the lever to pull during testing when you have published a new flow version and want to bypass the TTL. It returns a promise (the disk delete is async), so await it before the next resolve, or a resolve fired immediately after can still read the old Tier-0 entry. In production, prefer attaching the new version in the dashboard and letting the TTL expire.

Common mistakes

  • Testing caching against development. The 0 default TTL means nothing stays fresh. Use staging/production.
  • Disabling caching and expecting prefetch to work. Prefetch is a no-op with caching off.
  • Expecting a republished flow to appear instantly. A fresh cached copy is served until its TTL expires, and the placement must point at the new version. Clear the cache or attach the new version. See Troubleshooting.
  • Assuming offline always works. Offline rendering needs either a prior cache entry or a bundled default. Ship a bundled default for guaranteed offline onboarding.

Troubleshooting

  • Old flow still showing after republish. The cache is fresh, or the placement still points at the old version. Attach the new version, then clearCache() or wait out the TTL.
  • First present is slow on a poor network. Expected on a cold cache. Ship a bundled default and prefetch at launch so Tier 0/Tier 3 cover the first present. See Prefetching.
  • A flow renders offline that you did not expect. It came from stale cache or a bundled default. Check the delivery_source in debug logs.