feat: disk-based-persistence [android]#17
Merged
choudlet merged 22 commits intoApr 7, 2026
Conversation
Prepare EventQueue for PersistableEventQueue subclass by making the class, its backing ArrayDeque, and all public methods open. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Versioned JSON envelope wrapping List<EnrichedEventPayload> for disk queue snapshots. Version field enables forward-compatible schema migration. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reads/writes versioned JSON queue snapshots to noBackupFilesDir. Atomic write-then-rename prevents partial writes. Unrecognized schema versions and corrupted files are deleted with warning logs. Implements resilient per-event deserialization: individual events that fail to deserialize are skipped while valid events are preserved. This addresses the cross-platform contract requirement for graceful handling of partially corrupt snapshots. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Write-behind queue that keeps all enqueue/drain operations in memory. Disk flushes are full-overwrite snapshots triggered by lifecycle events or flush threshold. Rehydrates once per process. Enforces combined 2000-event/5MB capacity cap with drop-oldest overflow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SDK now creates PersistableEventQueue by default, rehydrates from disk on first init, flushes to disk on background and app terminate (best-effort via onTrimMemory), checks flush threshold after each enqueue. EventDiskStore is injectable for testing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Covers full session lifecycle, drain-before-flush correctness, no-disk-touch in normal sessions, multi-cycle persistence, and flush-overwrite deduplication. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Validates that enqueue remains memory-only and performant with 10,000 1KB events. Also benchmarks flushToDisk for 2,000 events. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
iOS parity: empty queues should not write to disk. Deletes any existing snapshot to avoid stale data on next rehydration. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
iOS parity: events older than eventTTLMs (default 7 days) are dropped during rehydrate() before capacity trimming. Fail-open on unparseable timestamps. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…hreshold iOS parity: events now route through Dispatcher.offer() which checks autoFlushThreshold (20 events) and triggers immediate flush. Previously events were enqueued directly to the queue, bypassing the threshold. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevents immediate retry loop when both circuit breaker delay and Retry-After header are 0 on first failure. Aligns with iOS SDK behavior which uses max(100, delay). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
choudlet
added a commit
that referenced
this pull request
Apr 10, 2026
* refactor: make EventQueue open for subclassing Prepare EventQueue for PersistableEventQueue subclass by making the class, its backing ArrayDeque, and all public methods open. * feat: add QueueSnapshot serializable envelope for disk persistence Versioned JSON envelope wrapping List<EnrichedEventPayload> for disk queue snapshots. Version field enables forward-compatible schema migration. * feat: add EventDiskStore for queue snapshot file I/O Reads/writes versioned JSON queue snapshots to noBackupFilesDir. Atomic write-then-rename prevents partial writes. Unrecognized schema versions and corrupted files are deleted with warning logs. Implements resilient per-event deserialization: individual events that fail to deserialize are skipped while valid events are preserved. This addresses the cross-platform contract requirement for graceful handling of partially corrupt snapshots. * feat: add PersistableEventQueue with disk-backed persistence Write-behind queue that keeps all enqueue/drain operations in memory. Disk flushes are full-overwrite snapshots triggered by lifecycle events or flush threshold. Rehydrates once per process. Enforces combined 2000-event/5MB capacity cap with drop-oldest overflow. * feat: wire PersistableEventQueue into MetaRouterAnalyticsClient SDK now creates PersistableEventQueue by default, rehydrates from disk on first init, flushes to disk on background and app terminate (best-effort via onTrimMemory), checks flush threshold after each enqueue. EventDiskStore is injectable for testing. * test: add end-to-end persistence lifecycle tests Covers full session lifecycle, drain-before-flush correctness, no-disk-touch in normal sessions, multi-cycle persistence, and flush-overwrite deduplication. * test: add enqueue performance benchmark for PersistableEventQueue Validates that enqueue remains memory-only and performant with 10,000 1KB events. Also benchmarks flushToDisk for 2,000 events. * fix: flushToDisk skips write and deletes snapshot when queue is empty iOS parity: empty queues should not write to disk. Deletes any existing snapshot to avoid stale data on next rehydration. * feat: add 7-day event TTL filter during rehydration iOS parity: events older than eventTTLMs (default 7 days) are dropped during rehydrate() before capacity trimming. Fail-open on unparseable timestamps. * fix: wire event processor through dispatcher.offer() for auto-flush threshold iOS parity: events now route through Dispatcher.offer() which checks autoFlushThreshold (20 events) and triggers immediate flush. Previously events were enqueued directly to the queue, bypassing the threshold. * fix: improving logging, removing verbosity * fix: dispatcher bugfix * docs: updating readme * fix: add 100ms minimum retry delay floor for 5xx/408 server errors Prevents immediate retry loop when both circuit breaker delay and Retry-After header are 0 on first failure. Aligns with iOS SDK behavior which uses max(100, delay). * fix: path errors with testing files * fix: proxy bounding issue * fix: dispatcher error handling * fix: matching iOS data contracts * fix: cleaning up circuit state bug * fix: fixing error with API version mismatch * fix: inheritance of EventQueue * fix: docs and queue byte enforcement
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements disk-backed event persistence for the Android SDK. Events are kept in memory as the primary queue; disk acts as a safety net for app backgrounding, termination, and buffer overflow.
EventQueue(inheritance) adding disk persistence with write-behind modelnoBackupFilesDir@Serializableenvelope withversion+eventsfieldsAtomicBooleangate, disk file deleted after loadTicket
https://app.shortcut.com/metarouter/story/36793
New Files
storage/QueueSnapshot.kt— Serializable snapshot envelopestorage/EventDiskStore.kt— File I/O for queue snapshotsqueue/PersistableEventQueue.kt— Persistence-aware queueModified Files
queue/EventQueue.kt— Made class and methodsopenfor subclassingMetaRouterAnalyticsClient.kt— Wired PersistableEventQueue with lifecycle hooksCross-Platform Contract
Aligns with iOS and React Native implementations per the shared contract. Addresses the Android-specific gap: resilient per-event deserialization using manual
JsonArrayiteration instead of atomicdecodeFromString.Test plan
QueueSnapshotTest— serialization roundtrip, version handling (5 tests)EventDiskStoreTest— read/write/delete, corrupt file handling, per-event resilience (11 tests)PersistableEventQueueTest— enqueue, drain, flush, rehydrate, capacity, threading, e2e lifecycle (29 tests)PersistableEventQueueBenchmarkTest— 10K enqueue + flushToDisk performance (2 tests)EventQueueTesttests pass unchanged (regression check)MetaRouterAnalyticsClientTesttests pass unchanged (wiring regression check)🤖 Generated with Claude Code