Chapter 08 · Offline Persistence, Local Cache, Synchronization, Conflict Behavior, and Offline Indexes
Memory vs Persistent Cache Options, Multi-Tab / Process Considerations, and Sensitive-Data Risk
Choose Firestore memory versus persistent cache, Web single/multi-tab behavior, sign-out/clearing policy and sensitive-data controls.
Learning outcomes
Choose memory or persistent cache from privacy, restart and offline requirements rather than platform folklore.
Configure Web single-tab versus multi-tab persistence and explain which state is coordinated across tabs.
Explain why sign-out is not equivalent to clearing Firestore cached data and design a test/reset or shared-device policy.
Distinguish browser-tab coordination from mobile multi-process assumptions and keep one explicit owner for Firestore lifecycle.
Use the Emulator Suite, a Firebase demo project, or an isolated test project for destructive, security-sensitive, billing-sensitive, migration, backup/restore, or write-heavy exercises unless the lesson explicitly marks managed verification as required. Treat shown output as expected evidence unless it is explicitly identified as captured output, and re-check current Firebase/Google Cloud edition, mode, quota, pricing, and security documentation before production execution.
AtlasMart continues the same environment used in Chapters
01–07: project ID demo-atlasmart-firestore,
Standard edition / Native mode /
(default) database for the mandatory lab,
Firestore emulator 127.0.0.1:8080, Authentication
emulator 127.0.0.1:9099, Emulator UI
127.0.0.1:4000, Firebase CLI
15.30.0, Firebase JavaScript SDK
12.19.0, Firebase Admin Node SDK
14.4.0 (bundling
@google-cloud/firestore 9.1.0), and Node.js 22+.
The emulator has no production location or billing contract;
any production region, pricing, SLA, IAM and managed-service
latency remain explicitly unproven by this lab.
The mandatory exercises run locally and deliberately manipulate client network state. They prove client-cache, queued-write, listener, Rules and synchronization behavior for the pinned SDK/emulator combination. They do not prove production WAN p95/p99 latency, device-OS background behavior, billing, data-recovery guarantees, real multi-region propagation, every browser storage policy, or Enterprise Pipeline behavior. Pipeline operations do not support offline persistence; Enterprise offline exercises must use Core operations.
1. AtlasMart problem: a shared kiosk remembers the previous shopper
A shopper signs out of AtlasMart on a family laptop. Authentication state is cleared, but a persistent Firestore cache can still hold documents from the previous session. Security Rules protect backend requests; they do not retroactively encrypt or securely erase bytes already stored in the browser profile. The cache mode is therefore part of the privacy architecture.
| Choice | Benefit | Risk / operational cost |
|---|---|---|
| memory cache | ephemeral process-lifetime state; simplest shared-device story | reload/restart loses cache; weaker offline restart UX |
| persistent single-tab | survives restart and supports offline continuity | one persistence owner; stale/sensitive data persists |
| persistent multi-tab | tabs/windows coordinate queries and mutations through shared persistence | more lifecycle complexity; all tabs share the local data surface |
| unlimited/large cache | more data remains available offline | larger privacy/storage footprint and longer local scans without indexes |
2. Web: choose persistence only after a trust decision
import { initializeApp } from "https://www.gstatic.com/firebasejs/12.19.0/firebase-app.js";import { initializeFirestore, memoryLocalCache, persistentLocalCache, persistentSingleTabManager, persistentMultipleTabManager, connectFirestoreEmulator} from "https://www.gstatic.com/firebasejs/12.19.0/firebase-firestore.js";const app=initializeApp({projectId:"demo-atlasmart-firestore",apiKey:"demo-key",appId:"demo-app"});const trustedDevice = localStorage.getItem("atlasmart-trusted-device") === "yes";const multiTab = new URL(location.href).searchParams.get("tabs") === "multi";const localCache = trustedDevice ? persistentLocalCache({ cacheSizeBytes: 40 * 1024 * 1024, tabManager: multiTab ? persistentMultipleTabManager() : persistentSingleTabManager({}) }) : memoryLocalCache();const db=initializeFirestore(app,{localCache});connectFirestoreEmulator(db,"127.0.0.1",8080);
The Web SDK’s current persistentLocalCache() uses
IndexedDB and accepts a cache-size threshold plus a tab manager.
The threshold is a garbage-collection target, not a hard storage
cap. The default persistent-cache threshold is 40 MB; do not
turn off garbage collection casually. For Chrome/Safari/Firefox
support, still test the actual deployment browser and storage
policy.
3. Android and Apple defaults do not remove the privacy decision
// Android: persistent disk cache is default; choose memory only when required.val settings = firestoreSettings { setLocalCacheSettings(memoryCacheSettings { }) // privacy-sensitive/shared-device mode}Firebase.firestore.firestoreSettings = settings// Apple: persistent cache is default; choose memory explicitly for ephemeral mode.let settings = FirestoreSettings()settings.cacheSettings = MemoryCacheSettings()let db = Firestore.firestore()db.settings = settings
Android and Apple persistent cache is enabled by default, so a security review must explicitly decide whether that is acceptable for the application’s data classification. Device sandboxing is useful but does not make every cached field appropriate for persistence. Avoid caching secrets, privileged server-only fields, or data that should never exist on an untrusted client in the first place.
4. Multi-tab is a Web feature; do not invent multi-process guarantees
persistentMultipleTabManager() coordinates
Firestore activity among browser tabs/windows using the same
origin. It does not imply that arbitrary Android/iOS processes,
WebViews, extensions or separate browser profiles share one
coherent cache. Treat each application process/profile as a
distinct client unless the platform SDK explicitly documents
otherwise, and avoid multiple competing Firestore owners in a
mobile multi-process architecture.
Every listener, pending-write indicator, Auth user and cache policy belongs to a user/session/application lifecycle. “The SDK caches it” is not a lifecycle specification.
5. Sign-out is not cache clearing
Firebase Authentication sign-out changes authentication state.
It does not promise to erase Firestore’s persistent cache. For
test isolation, the Web SDK exposes
clearIndexedDbPersistence(), but it must be called
when Firestore is not running and it is explicitly not a
secure-overwrite mechanism.
import { terminate, clearIndexedDbPersistence } from "https://www.gstatic.com/firebasejs/12.19.0/firebase-firestore.js";// Test/reset workflow: stop listeners first, then terminate the Firestore instance.await terminate(db);await clearIndexedDbPersistence(db);// Create a fresh Firebase/Firestore instance before using Firestore again.// clearIndexedDbPersistence is test-oriented and is NOT a secure erase primitive.
For sensitive cross-session disclosure, the official guidance is stronger: consider not enabling persistent cache at all. A product requirement such as “no previous user data remains recoverable after sign-out” should not be implemented by assuming a test cleanup API is secure deletion.
6. Reproducible trust-device matrix
{ "firestore": { "rules": "firestore.rules", "indexes": "firestore.indexes.json", "edition": "standard" }, "emulators": { "firestore": { "port": 8080, "edition": "standard" }, "auth": { "port": 9099 }, "ui": { "enabled": true, "port": 4000 } }}
rules_version = '2';service cloud.firestore { match /databases/{database}/documents { match /profiles/{uid} { allow read, create, update: if request.auth != null && request.auth.uid == uid; allow delete: if false; } match /carts/{uid} { allow read, create, update: if request.auth != null && request.auth.uid == uid; allow delete: if false; match /items/{productId} { allow read, create, update, delete: if request.auth != null && request.auth.uid == uid; } } match /catalogItems/{productId} { allow read: if true; allow write: if false; } match /{document=**} { allow read, write: if false; } }}
mkdir atlasmart-offline && cd atlasmart-offlinenpm init -ynpm install firebase-admin@14.4.0# Save firebase.json and firestore.rules from this lesson.printf '{"indexes":[],"fieldOverrides":[]}' > firestore.indexes.json# terminal Anpx firebase-tools@15.30.0 emulators:start --project demo-atlasmart-firestore --only firestore,auth# terminal B: serve browser filespython -m http.server 4173# then open http://127.0.0.1:4173/ in a supported browser
| Run | Cache | Tabs | After reload | After sign-out |
|---|---|---|---|---|
| A | memory | single | expect cache to be rebuilt | Auth clears; no persistent Firestore cache from prior page process |
| B | persistent | single | cached documents remain available | Auth clears but Firestore cache can remain |
| C | persistent | multi | two tabs can coordinate local persistence | both tabs participate in the same local data surface |
Use only synthetic profile/cart data. Record what survives reload and what survives sign-out. Then terminate/clear between test cases so a stale cache does not masquerade as emulator data after the emulator has been reset.
7. Wrong approach: “Rules deny the next read, so cached data is gone”
Rules govern backend access. An already-cached document may still be present on the device and can participate in local query/listener behavior until local state changes or is cleared. This is why data minimization and cache-mode selection matter even when Rules are correct.
Production judgment
Classify client-visible data, decide whether persistent cache is allowed per device type, publish a user-visible “trusted device” policy when appropriate, and test shared-device logout. Add telemetry for persistence initialization failures/fallbacks without logging document contents. Lesson 4 turns to performance: persistent caches can need local query indexes as they grow.
Knowledge check
- Does Firebase Auth sign-out securely erase Firestore cache?
- What is Web multi-tab persistence for?
- Is the persistent Web cache-size setting a hard cap?
- When should a privacy-sensitive app prefer memory cache?
-
Is
clearIndexedDbPersistence()a secure erase primitive?
Review the answers
1. No. Authentication state and Firestore local persistence are separate lifecycles.
2. It coordinates persistent Firestore cache/query/mutation activity among tabs/windows sharing the same origin.
3. No. It is an approximate garbage-collection threshold.
4. When persistent local disclosure across sessions/devices is not acceptable or not needed.
5. No. It is primarily a test/reset utility and does not securely overwrite cached data.
Summary and next step
Cache configuration is a privacy and lifecycle decision. Lesson 4 shows how persistent local query indexes change offline query performance without changing server indexes.
Authoritative references
- Access data offline — platform defaults, cache configuration, cache-size behavior and sensitive-data guidance.
- PersistentCacheSettings — Web cache-size threshold and tab-manager configuration.
- JavaScript Firestore API — persistent/memory cache and clear-persistence lifecycle.
- Android FirebaseFirestore reference — clearPersistence lifecycle and test-oriented warning.
- Apple PersistentCacheSettings — persistent cache defaults and size configuration.