Repository navigation
feat: [SDK-5378] persist and retry sessions API requests through OperationRepo - #2794
fadi-george merged 8 commits into
Conversation
📊 Diff Coverage ReportDiff Coverage Report (Changed Lines Only)Gate: aggregate coverage on changed executable lines must be ≥ 80% (JaCoCo line data for lines touched in the diff). Changed Files Coverage
Overall (aggregate gate)281/289 touched executable lines covered (97.2% — requires ≥ 80%) |
There was a problem hiding this comment.
Multi-model review
Models: Claude Opus 5.5, GPT 5.6 Sol, Grok 4.7
Persist-and-retry via OperationRepo is the right shape, and the queue/executor tests cover the in-queue rules. Three integration gaps will drop or evict session work once lifecycle wiring starts enqueueing these ops.
Act on
- Offline (
HttpClientstatus-1) isFAIL_NORETRYhere; every other executor treats it as retryable viaNetworkUtils. - The 100-op cap runs on every enqueue and adds
+1even for non-session ops, so a tag/login at the cap evicts the oldest session. - Coalesce / drop-after-end only see
queue. In-flight ends andFAIL_RETRY/ startup requeues skip the policy, so an update can still send after an end.
Consider
- Update 404 retry uses in-memory
NewRecordsStateonly — a kill during the 202 window permanently drops the restored update. - Create 404 does not retry in the user/subscription missing window.
- Coalescing a retrying update resets backoff and replaces the idempotency key.
Noted: the whole-queue 5s post-create delay is the documented tradeoff.
Sent by Cursor Automation: PR Reviews
|
Also: merging updates now carries over retries and retryAt, so new updates no longer skip backoff. Keeping the fresh idempotency key since the merged update carries the max duration. |
abdulraqeeb33
left a comment
There was a problem hiding this comment.
Requesting changes.
- A session create with requiresJwt false survives the anonymous suppress and the hydrate purge. If its ids are still local, it never starts, and it stays queued until the cap.
- A 2xx with no session_id drops the create and its updates. Retry that response on the same idempotency key.
- An update whose session id is still local is accepted when no create is left.
- loadSavedOperations calls internalEnqueue, so a restored end is not recorded in endedSessions. Once that end is taken out to send, a later update is accepted. Record restored ends at load.
- The local-id check and the serverSessionId write take the model lock separately. A focus between them can stamp the next session with the previous server id.
|
Thanks, 4 is fixed. restored ends are now recorded as closed sessions at load |
abdulraqeeb33
left a comment
There was a problem hiding this comment.
The four items from the last review are fixed in 12eedf7. ab34f50 only adjusts two tests.
- A create with a local onesignal id or subscription id is dropped at enqueue and at hydrate, with its updates.
- A 202 with no session_id retries on the same idempotency key, three times, then the create and its updates are dropped.
- An update with a local session id is rejected unless its create is known, including one restored from the store.
- Restored ends are recorded at load. The server session id compare and write run in one runOnSerialIO block.
closedSessions drops the oldest id after 50, and an end that already left the store is not restored.
…ationRepo Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eue rules Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
c2c61e7 to
95f3c22
Compare
… place SessionQueuePolicy's creates and closed sessions merge into one persisted SessionStateModelStore, so they survive a restart instead of being rebuilt from the queue. The executor no longer writes the server session ID to SessionModel, which drops that field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>


Description
One Line Summary
Sends sessions API create/update requests as persisted OperationRepo operations, so they survive app kills and are retried with the same idempotency key.
Details
Motivation
Session requests need to survive process death, offline periods and server errors without double counting. Replaces the earlier approach in #2777 now that OperationRepo retries failed operations per operation (#2790).
Scope
CreateSessionOperation/UpdateSessionOperation, persisted viaOperationModelStore. Each stores an idempotency key at creation that is reused on every retry.SessionOperationExecutor: retries on network errors, 408, 429 (honors Retry-After) and 5xx; drops other 4xx. Update 404 is retried within the post-create window since create is async (202). No retry logic of its own; backoff comes from OperationRepo.SessionQueuePolicy, hooked into OperationRepo enqueue/drop:SessionStateModelStoreis the only record of a session's create IDs, server session ID and whether it is closed, so updates after a restart still get them and an update after an end that already left the queue is still dropped. Capped at 100 sessions, never dropping one with operations still queued.SessionModel.localSessionId, set on each new session.SessionModel.serverSessionIdis removed, since nothing reads it.requiresJwt = falseare no longer suppressed for anonymous users under identity verification, otherwise IV apps would send no sessions.Known tradeoff: a successful create returns ID translations, so OperationRepo's existing post-create delay (5s default) pauses the queue once per session. This also gives the backend time to process the create before an update.
Not included: wiring session lifecycle to enqueue these operations (downstream PRs).
Testing
Unit testing
SessionOperationExecutorTests: request bodies, ms to seconds, retry vs drop status mapping, Retry-After, 404 in missing window, idempotency keys surviving persistence.SessionOperationQueueTests(through OperationRepo): coalescing, end replacing update, update after end dropped, cap, create before update ordering, late local-ID update, dropped create drops updates, replay after restart with same key, IV exemption.SessionQueuePolicyunit cases cover the create IDs, the backend session ID and a closed session surviving a restart, and the state cap keeping queued sessions.testDebugUnitTest, spotless, detekt, apiCheck andassembleReleasepass locally.Manual testing
Not tested on device; nothing enqueues these operations until the lifecycle wiring lands.
Affected code checklist
Checklist
Overview
Testing
Final pass
🤖 Generated with Claude Code