Flash Persistence - Troubleshooting
Common issues, solutions, and production checklist
🔧 Flash Persistence - Troubleshooting
Last Updated: November 4, 2025
Status: ✅ All Critical Issues Resolved
Production Ready: Yes, with all fixes applied
🎯 Quick Reference
| Issue | Symptom | Solution | Status |
|---|---|---|---|
| Array not persisting | Arrays reset on reload | Use intelligent serialization | ✅ Fixed |
| Stale data on reload | Old data appears after refresh | Micro-cache bypass for flash keys | ✅ Fixed |
| Write verification failures | Warnings about persistence | Property order differences (harmless) | ✅ Fixed |
| Race conditions | Rapid updates cause data loss | Debouncing with shared pending object | ✅ Fixed |
✅ Issue #1: Array Support (RESOLVED)
What Was Fixed
Flash Persistence now includes intelligent serialization that properly handles all data types, including arrays.
Problem
Arrays were not persisting correctly due to reference issues and improper serialization.
Solution
Intelligent serialization with deep cloning:
// ✅ Arrays work perfectly now!
protected favorites = autoFlashState('favorites', ['apple', 'banana']);
// All array operations work correctly
toggleFavorite(item: string) {
const current = this.favorites();
if (current.includes(item)) {
this.favorites.set(current.filter(f => f !== item)); // ✅ Works!
} else {
this.favorites.set([...current, item]); // ✅ Works!
}
}
Features:
- ✅ Dynamic arrays that grow/shrink
- ✅ Arrays of objects
- ✅ Nested arrays
- ✅ All array methods (map, filter, reduce, etc.)
- ✅ Deep cloning prevents reference issues
- ✅ Automatic array detection and preservation
- ✅ Corrupted data recovery
- ✅ Type-safe serialization/deserialization
✅ Issue #2: Micro-Cache Serving Stale Data (RESOLVED)
Problem Statement
Flash Persistence keys were being cached by GgStorage's micro-cache, causing stale data to be served even after fresh data was written to persistent storage.
Symptoms:
- Add data → State updates correctly during session
- Refresh page → Old data appears (new data lost!)
- Logs show:
✅ WRITE VERIFICATION: flash:xyz successfully persisted - But reload shows:
📦 Micro-cache hit: flash:xyzreturning STALE data
Root Causes
1. Race Condition from Rapid Updates
- No debouncing meant every state update triggered immediate persistence
- Multiple rapid updates created race conditions
- Earlier operations could complete AFTER later ones, overwriting correct data
2. JavaScript Closure Capture Bug
- Each
setTimeoutcaptured value in its closure - When timeouts were cancelled, value reference was lost
- Solution: Store values in shared pending object that gets updated
3. Micro-Cache Serving Stale Data
- GgStorage's aggressive micro-cache (localStorage-based) was serving stale data
- Cache invalidation attempts failed because micro-cache property was private
- Even after making it public, cache invalidation was too complex and brittle
Solution: Complete Micro-Cache Bypass
Tell GgStorage to SKIP the micro-cache entirely for flash-persistence keys.
Implementation (in gg-storage.ts):
// CRITICAL: This fix MUST be in 5 locations in gg-storage.ts!
// Location 1: async get() - Micro-Cache Read
const shouldUseMicroCache = !key.toString().startsWith('flash:');
if (shouldUseMicroCache) {
const fromMicroCache = this.microCache.get<T>(key);
// ... use micro-cache
}
// Location 2: getSync() - Synchronous Micro-Cache Read
const shouldUseMicroCache = !key.toString().startsWith('flash:');
if (shouldUseMicroCache) {
// ... use micro-cache
}
// Location 3: async put() - Micro-Cache Write
const shouldUpdateMicroCache = !key.toString().startsWith('flash:');
if (shouldUpdateMicroCache) {
// ... update micro-cache
}
// Location 4: remove() - Micro-Cache Deletion
const shouldRemoveFromMicroCache = !key.toString().startsWith('flash:');
if (shouldRemoveFromMicroCache) {
// ... remove from micro-cache
}
// Location 5: clear() - Selective Micro-Cache Clear
// Only clear micro-cache for non-flash keys
Result:
- ✅ Flash keys always read from persistent storage (IndexedDB/OPFS)
- ✅ No stale data issues
- ✅ Still fast (5-10ms from persistent storage)
- ✅ Other keys still benefit from micro-cache
✅ Issue #3: Write Verification False Positives (RESOLVED)
Problem
Write verification logged warnings about property order differences:
⚠️ WRITE VERIFICATION: Persisted data structure differs from source:
Expected: {"id":"123","name":"Test"}
Found: {"name":"Test","id":"123"}
Solution
This is NOT a bug! JavaScript object property order can vary during serialization/deserialization. The data is correct - only the order differs.
Updated Verification (in flash-state.service.ts):
// Deep comparison that ignores property order
if (JSON.stringify(sortKeys(savedData)) === JSON.stringify(sortKeys(currentValue))) {
console.log(`✅ WRITE VERIFICATION: ${storageKey} successfully persisted`);
} else {
// Only log if actual values differ, not just property order
console.warn(`⚠️ WRITE VERIFICATION: Possible persistence issue for ${storageKey}`);
}
Note: Property order differences are harmless and expected in JavaScript.
🔧 Common Issues & Solutions
Issue: State Not Loading on First Visit
Problem: State is undefined on first page load.
Solution: Always provide a default value:
// ❌ BAD: No default value
protected state = autoFlashState('my-state');
// ✅ GOOD: Always provide default
protected state = autoFlashState('my-state', { count: 0 });
Issue: State Updating Too Frequently
Problem: Rapid updates causing performance issues.
Solution: Debouncing is built-in (300ms), but you can also debounce in your component:
private updateDebounced = debounceTime(300);
updateState(value: any) {
this.updateDebounced(() => {
this.state.set(value);
});
}
Issue: Large State Causing Slow Loads
Problem: Storing large amounts of data (>1MB) causes slow initial load.
Solution: Split into multiple smaller states:
// ❌ BAD: One giant state
protected appState = autoFlashState('app', {
users: [...1000 users...],
products: [...10000 products...],
orders: [...5000 orders...]
});
// ✅ GOOD: Split into focused states
protected users = autoFlashState('users', []);
protected products = autoFlashState('products', []);
protected orders = autoFlashState('orders', []);
Issue: Cross-Tab Synchronization Not Working
Problem: Changes in one tab don't appear in another tab.
Solution: Flash Persistence automatically syncs across tabs via storage events. If not working:
- Check if both tabs are on same origin (protocol + domain + port)
- Verify browser supports storage events (all modern browsers do)
- Check browser console for errors
Issue: State Persisting When It Shouldn't
Problem: Don't want certain data to persist (e.g., sensitive data).
Solution: Use regular signals for temporary data:
// ✅ Use autoFlashState for data that should persist
protected userPreferences = autoFlashState('prefs', { theme: 'light' });
// ✅ Use regular signal for temporary data
protected sessionToken = signal<string | null>(null); // Won't persist
Issue: "localStorage is full" Error
Problem: Browser throws quota exceeded error.
Solution: Flash Persistence automatically switches to IndexedDB when localStorage is full. If still having issues:
Clear old data:
// Clear specific state this.storage.remove('flash:old-state-key'); // Clear all flash states (use carefully!) this.storage.clearFlashStates();Use compression for large states (coming in Q1 2025)
Issue: TypeScript Type Errors
Problem: TypeScript complains about state types.
Solution: Explicitly type your state:
interface UserSettings {
theme: 'light' | 'dark';
lang: string;
notifications: boolean;
}
// ✅ Explicitly typed
protected settings = autoFlashState<UserSettings>('settings', {
theme: 'light',
lang: 'en',
notifications: true
});
🚨 Production Checklist
Before deploying Flash Persistence to production, verify:
1. Micro-Cache Bypass Applied
- All 5 locations in
gg-storage.tshave micro-cache bypass - Test: Add data, reload page, verify fresh data loads
2. Intelligent Serialization Enabled
- Arrays persist correctly
- Objects with nested data persist correctly
- No reference issues with complex data structures
3. Debouncing Working
- Rapid updates don't cause race conditions
- Write verification succeeds consistently
- No data loss on rapid state changes
4. Cross-Tab Sync Working
- Changes in one tab appear in other tabs
- Storage events fire correctly
- No conflicts between tabs
5. Error Handling
- Corrupted data recovery works
- Quota exceeded handled gracefully
- Network failures don't break state
6. Performance
- Initial load time acceptable (<100ms for typical state)
- Updates feel instant (<10ms)
- No memory leaks in long-running sessions
7. Security
- Sensitive data (passwords, tokens) NOT in flash state
- User data properly scoped per user/tenant
- No data leakage between users
8. Testing
- Unit tests for state mutations
- Integration tests for persistence
- E2E tests for cross-tab sync
- Load tests for large states
🔍 Debugging Tips
Enable Verbose Logging
// In your app config
provideIntelligentStorage({
debug: true, // Enable verbose logging
logLevel: 'verbose'
});
Logs to Look For:
✅ WRITE VERIFICATION: ...- Successful write📦 Micro-cache hit: ...- Should NOT appear for flash: keys🎯 flash:xxx loaded: ...- State loaded from storage💾 Flash state saved: ...- State persisted
Inspect Storage
Open browser DevTools:
Application Tab → Storage:
- localStorage: Should see
flash:prefixed keys - IndexedDB: Fallback storage if localStorage full
Test Scenarios
- Happy Path: Add data → Reload → Data persists ✅
- Rapid Updates: Click button 10x fast → Reload → Correct count ✅
- Cross-Tab: Open 2 tabs → Change in tab 1 → Appears in tab 2 ✅
- Large Data: Add 1000 items → Reload → All items present ✅
- Clear Data: Clear storage → Reload → Default values ✅
📚 Related Documentation
- Quick Start - Get started in 5 minutes
- API Reference - Complete API documentation
- Best Practices - Patterns and optimization
- Advanced Usage - Complex scenarios
🆘 Need More Help?
If you encounter an issue not covered here:
- Check Logs: Enable debug mode and check console
- Verify Fix Applied: Ensure all 5 micro-cache bypass locations are updated
- Review Checklist: Go through production checklist above
- Search Documentation: Use ⌘K in docs to search
- Create Issue: Document the problem with logs and reproduction steps
Last Updated: November 4, 2025
All Critical Issues: ✅ Resolved
Production Status: ✅ Ready