Repository navigation
feat: [SDK-5379] persistent session requests through the operation repo - #1768
Conversation
There was a problem hiding this comment.
Multi-model review (Opus 5.5, GPT 5.6 Sol, Grok 4.7)
The follow-up already lands the #1766 items: clear the queue on app-id change, skip OneSignalClient retries, drop never-ready IV creates, keep coalesce key + failedAttempts, rebuild serverSessionIds on load, create key = sessionId, honor Retry-After on the last attempt, monotonic notBefore, and processNext after load.
enqueueCreate / enqueueUpdate are not called from OSSessionService yet. The queue only runs via retryNow (focus / pin refresh) and restore. The notes below assume that wiring.
Act on
None remaining as a 3/3 production-path blocker.
Consider
sessionUserCanBeCreatedtreats a missing identity model as anonymous (Opus). Cached IV.onplusprocessNextafterload()can drop a persisted create before User restores the model.retryNowcancels failure backoff on everyonFocus/refreshPinnedIds(Opus). Five foregrounds during 5xx with noRetry-Afterburn the attempt limit.- First PATCH after create is immediate; 404 is
.drop(Sol).OP_REPO_POST_CREATE_DELAY_SECONDSand Android #2777 wait for replication. - In-flight update is left out of the duration max (Grok). A later lower update or end can shrink
duration_seconds. - Unchanged-body coalesce copies only
serverSessionId(Grok) and drops incoming pin IDs, so an earlier session can stall. resetAndClearStoredQueue/handlevsisRetired(3/3 race, 2/3 late completion): delete happens afterreset(), and a late success still writes through the newsharedservice.
Noted
Consent 0 retries forever (documented on #1763). Flag-off does not stop a saved queue. Uncapped Retry-After. One decode failure drops the whole blob.
Dismissed
App-id reload as a likely race on today's main-thread handleAppIdChange / onFocus path.
Sent by Cursor Automation: PR Reviews
|
Pushed bfed2d5 for the review above:
Not changed: |
bfed2d5 to
65000d0
Compare
nan-li
left a comment
There was a problem hiding this comment.
I think everything is correct, the trouble I am having with this review is the implementation doesn't follow the SDK’s existing request pattern. Calling out complexity in this separate system that is quite different. The other user executors persist the request object itself, retry the same ways, and run through the operation repo. This queue persists its own record type as JSON in standard defaults, retries in the same run with backoff, retry after and an attempt cap, its own network monitoring and background tasks, and never goes through the operation repo. Which becomes harder to grasp the SDK.
Yeah mb, Ill follow existing patterns and rewrite the pr. |
|
@nan-li reworked in 29e1a4e. Session requests are now |
eca3dd6 to
eaaec6c
Compare
abdulraqeeb33
left a comment
There was a problem hiding this comment.
Requesting changes.
- After a create succeeds, nothing re-enters the queue. The updates wait for the next repo flush, and that flush holds no background task.
- createdSessions and endedSessions are rebuilt only from what is still in the request queue, so a restart can drop an update whose create already succeeded, or send one after an end that already left the queue.
- typeLimit nil keeps the current user's session requests forever.
Session requests are persisted and sent by OSSessionOperationExecutor, with ordering, coalescing, and a size cap. Retries use the shared OSRequestRetryBackoff and retry trigger from SDK-5481 instead of their own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…wiftLint file length Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…'s sent IDs An end leaves the queue once sent or dropped, so remember ended sessions and discard later updates for them. Persist the queue after sending a create, so a replay after a kill reuses the IDs it was sent with. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An update built while its create was in flight has no server session ID. If the process is killed after the create succeeds, the in-memory map was lost and the update was discarded. Persist it, capped at the newest 100. Session requests for the current user now age out after 30 days. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7bc7aea to
18af6ea
Compare
nan-li
left a comment
There was a problem hiding this comment.
Nothing major blocking, just a couple of suggestions
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…place The executor's persisted per-session state is now the only record of a session's create and end. Updates read the created IDs from it when sent, and the session record no longer stores the server session ID. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nan-li
left a comment
There was a problem hiding this comment.
approving from my side, AR still has changes requested so we can wait to merge until he has taken a look
abdulraqeeb33
left a comment
There was a problem hiding this comment.
The persisted session state and the 30-day limit on a queued request both look right. Three leftovers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
abdulraqeeb33
left a comment
There was a problem hiding this comment.
Aging is fixed in 527919e. The request takes the delta timestamp, and a stale one is dropped before admit.
The other three can stay. The 3s window matches the other executors, an end is only enqueued once, and a 204 update was accepted.
abdulraqeeb33
left a comment
There was a problem hiding this comment.
The timestamp fix looks right. The request takes the delta's time, and a stale one is dropped before admit, so it cannot evict a queued request.
The other three can stay. One correction on the 404 note: the post-create delay is 3s, and the missing-record retry window is 60s.
…quest Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>


Description
One Line Summary
Send sessions API requests through the operation repo with an
OSSessionOperationExecutor, persisted like the other executors, with ordering, coalescing, a size cap, and retries through the shared backoff from #1775.Details
Motivation
Session requests need to survive restarts, process death, and offline periods, and be retried without double counting. Built on the existing executor pattern so it reads like the rest of the SDK. Now that #1775 (SDK-5481) gives every executor same-run retry, this PR uses that instead of its own retry code.
Scope
OSRequestCreateSession/OSRequestUpdateSessionareOSUserRequests (NSCoding), persisted withsaveCodeableDatalike the other executors' requests. They replace the OSCore sessions backend service from feat: [SDK-5375] add sessions API client #1763. The create's idempotency key is the local session ID; an update's is generated once and persisted.OSSessionOperationExecutoris registered withOSOperationRepo. The User manager enqueuesOS_CREATE_SESSION_DELTA/OS_UPDATE_SESSION_DELTADeltas (enqueueSessionCreate/enqueueSessionUpdate), which do nothing unlessrecord.usesSessionsApi.onesignal_idand the pushsubscription_id. An update waits for its create's server session ID, then the post-create delay throughOSNewRecordsState.onesignal_idandsubscription_idare set on the first send, and every update carries exactly those, never a fallback. The executor keeps one persisted state per session, the create's IDs and server session ID plus whether it ended, and updates read the IDs from it when sent.OSRequestRetryBackoffand the User manager'sOSOperationRetryTriggerfrom fix: [SDK-5481] retry failed operations within the same run #1775. No session-specific backoff, attempt limit, or trigger. A 404 is retried while the record it depends on was created in the last 60 seconds (OSNewRecordsState.isInMissingRetryWindow): the create'sonesignal_idorsubscription_id, or the update's server session ID. It may be backend replication lag. After that window a 404 drops, like other 4xx. A create answered with a 2xx but nosession_idis retried 3 times, then dropped with its updates.OneSignalClient's own retries apply, as for other executors.409always drops. This matches Android #2794.onesignal_id, which will never be created, along with their updates.OSRequestAginglimits apply.Testing
Unit testing
OSSessionOperationExecutorTestscovers the request bodies, create waiting for its user, update waiting for its create and the post-create delay, updates carrying the create's IDs after the push subscription changes, coalescing, in-flight durations, orphan discard, the cap, backoff through the sharedOSRequestRetryBackoff,Retry-After,retryNow, a replaced update keeping its backoff, 4xx drops, 404 retries inside the new-record window and drops after it, 409 drops, the missingsession_idcap, persistence across a restart with the same keys, an update built before its create succeeded surviving a kill, an update after a restart using the earlier create, an update after a sent end discarded across a restart, the 30-day age limit, other-app drops, anonymous requests under Identity Verification, no sends before privacy consent, and the flag-off enqueue.OSOperationRepoIdentityVerificationTestscovers the repo exemption.UserJwtLifecycleTests.testUpdateUserJwtSendsTheCreateUserThatARejectedTokenHeldhad a race (it swapped the mock response before the 401 was delivered) that the extra executor exposed; it now waits for the 401 to be handled. Other full-suite runs passed, with occasional timing flakes inOneSignalUserTestsandSwitchUserIntegrationTests.Manual testing
Not done. Nothing enqueues requests until the session lifecycle is wired up.
Affected code checklist
Checklist
Overview
Testing
Final pass
Redone on top of #1775 as one commit. The previous head was eca3dd6. Earlier review is on #1766.
🤖 Generated with Claude Code