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:


✅ 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:

  1. Add data → State updates correctly during session
  2. Refresh page → Old data appears (new data lost!)
  3. Logs show: ✅ WRITE VERIFICATION: flash:xyz successfully persisted
  4. But reload shows: 📦 Micro-cache hit: flash:xyz returning STALE data

Root Causes

1. Race Condition from Rapid Updates

2. JavaScript Closure Capture Bug

3. Micro-Cache Serving Stale Data

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:


✅ 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:

  1. Check if both tabs are on same origin (protocol + domain + port)
  2. Verify browser supports storage events (all modern browsers do)
  3. 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:

  1. Clear old data:

    // Clear specific state
    this.storage.remove('flash:old-state-key');
    
    // Clear all flash states (use carefully!)
    this.storage.clearFlashStates();
    
  2. 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

2. Intelligent Serialization Enabled

3. Debouncing Working

4. Cross-Tab Sync Working

5. Error Handling

6. Performance

7. Security

8. Testing


🔍 Debugging Tips

Enable Verbose Logging

// In your app config
provideIntelligentStorage({
  debug: true,  // Enable verbose logging
  logLevel: 'verbose'
});

Logs to Look For:

Inspect Storage

Open browser DevTools:

Application Tab → Storage:

Test Scenarios

  1. Happy Path: Add data → Reload → Data persists ✅
  2. Rapid Updates: Click button 10x fast → Reload → Correct count ✅
  3. Cross-Tab: Open 2 tabs → Change in tab 1 → Appears in tab 2 ✅
  4. Large Data: Add 1000 items → Reload → All items present ✅
  5. Clear Data: Clear storage → Reload → Default values ✅

📚 Related Documentation


🆘 Need More Help?

If you encounter an issue not covered here:

  1. Check Logs: Enable debug mode and check console
  2. Verify Fix Applied: Ensure all 5 micro-cache bypass locations are updated
  3. Review Checklist: Go through production checklist above
  4. Search Documentation: Use ⌘K in docs to search
  5. Create Issue: Document the problem with logs and reproduction steps

Last Updated: November 4, 2025
All Critical Issues: ✅ Resolved
Production Status: ✅ Ready