Integrate durable zone event delivery and beacon lifecycle handling - 4 - #5738
Integrate durable zone event delivery and beacon lifecycle handling - 4#5738Pixelobserver wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved critical and moderate delivery and lifecycle issues remain.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Integrates ZoneManager with durable zone-event delivery and beacon scanning lifecycle handling.
Changes:
- Adds ordered outbox persistence, retries, reconciliation, and stable event IDs.
- Handles application/location wakes and beacon scan handoffs.
- Expands integration coverage for persistence, recovery, failures, and expiration.
File summaries
| File | Review summary |
|---|---|
Sources/App/ZoneManager/ZoneManager.swift |
Durable delivery and scan lifecycle integration. Findings: one critical issue (1 vote) and three moderate issues (2, 1, and 1 votes) involving API resolution, delivery flushing, retry cancellation, and main-thread location access. |
Tests/App/ZoneManager/ZoneManager.test.swift |
Integration and recovery tests. One nit (1 vote): rename a test to reflect ordering rather than coalescing behavior. |
Review details
Suppressed comments (3)
Sources/App/ZoneManager/ZoneManager.swift:481
DispatchWorkItem.cancel()is cooperative, so a retry block canceled byattachor by an empty flush can still execute. This block then unconditionally clears whichever retry item has since been installed and flushes immediately, allowing a fast subsequent failure to lose its new backoff timer and retry repeatedly. Gate the closure with an identity/generation check before clearing and flushing.
let workItem = DispatchWorkItem { [weak self] in
guard let self else { return }
zoneEventRetryWorkItem = nil
flushPendingZoneEvents()
Sources/App/ZoneManager/ZoneManager.swift:101
- This reads
locationManager.monitoredRegionson the main thread, even thoughsyncNowdocuments that this property performs synchronous locationd/XPC work and must be read off-main. A slow location daemon can therefore reintroduce the field hang this change is intended to avoid. Move this read into the existing off-main sync path (and update the lifecycle test to wait for the asynchronous handoff).
collector.startForegroundBeaconScanning(
in: locationManager.monitoredRegions,
manager: locationManager
)
collector.stopBackgroundBeaconMonitoring(manager: locationManager)
Tests/App/ZoneManager/ZoneManager.test.swift:1182
- This test does not exercise coalescing: the outbox coalesces adjacent unstarted beacon events with the same event type, while this test queues
ios.zone_enteredfollowed byios.zone_exitedand expects both events to be delivered. Rename it so the test name describes the ordering behavior it actually covers.
func testCoalescedBeaconExitWaitsForInFlightEntry() async throws {
- Files reviewed: 2/2 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| guard let api = Current.api(for: server) else { | ||
| Current.Log.error("No API available to fire ZoneManager event, server: \(server)") | ||
| return | ||
| } |
| guard Current.settingsStore.locationSources.zone else { | ||
| collector.stopBackgroundBeaconMonitoring(manager: locationManager) | ||
| return | ||
| } | ||
|
|
||
| flushPendingZoneEvents() |
There was a problem hiding this comment.
🟡 Changes recommended
Disabled-location recovery, orphaned started events, and main-thread Core Location reads can stall delivery or app lifecycle handling.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (3)
Previously missed (2) — in code that hasn't changed since the last review.
Sources/App/ZoneManager/ZoneManager.swift:95
- Flush the durable outbox before this location-source guard. If tracking is disabled after a delivery fails and the app is then suspended, the retry timer may not run; subsequent foreground wakes return here without retrying, and location wakes have also been disabled. This leaves queued work stuck until process recreation, contrary to the wake-recovery behavior.
Sources/App/ZoneManager/ZoneManager.swift:99 - This reads
CLLocationManager.monitoredRegionson the main-thread app lifecycle callback. The existingsyncNowdocumentation at lines 535-537 says this getter performs synchronous XPC and was moved off-main because it caused the app's top field hang. Cache the synchronized region set or fetch it onregionSyncQueue, then perform only the collector handoff on main.
This issue also appears on line 109 of the same file.
Sources/App/ZoneManager/ZoneManager.swift:111
- This second lifecycle path also evaluates
monitoredRegionson the main thread, reintroducing the synchronous locationd XPC hang called out at lines 535-537. Use the same off-main/cached region snapshot as the foreground handoff and keep only collector mutation on main.
collector.startBackgroundBeaconMonitoring(
in: locationManager.monitoredRegions,
manager: locationManager
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Balanced
| guard let server = Current.servers.server(forServerIdentifier: pending.serverIdentifier) else { | ||
| logZoneEventDrainBlocked(pending, reason: "Server is unavailable") | ||
| scheduleZoneEventRetry() | ||
| return |
AI Policy
Select exactly one:
Summary
This is part 4 of the split requested for #5629.
Integrate ZoneManager with the atomic event outbox, persistent background webhook transport and beacon scanning lifecycle.
This does not guarantee exactly-once delivery across server processing, network failures and app termination.
Dependencies and scope
Depends on:
This branch contains only ZoneManager integration and its tests: two files, based directly on main. It does not include the foundation implementations or their commits.
It is not independently buildable without those foundations. Please merge the foundation PRs first, then update and verify this branch before merging part 4.
Screenshots
N/A — no visual changes or new settings.
Link to pull request in Documentation repository
Pending. Documentation should describe delivery/retry behavior and its limitations.
Any other notes
Completed local checks:
Pending verification:
No successful Xcode test run or measured coverage is claimed for this revision.