Repository navigation
feat(mobile measurements upload): MobileUploadLoop + platform upload backstops - #24
Merged
Merged
Conversation
…d with pending uploads
… for the platform backstop (red)
…form backstops; drop flushNow
…upload backstops - uploadModule registers MobileMeasurementUploader, MobileSessionUpload (as SessionUploader) and MobileUploadLoop; initKoin starts the loop for the life of the process. - UploadBackstop: WorkManager one-off unique work with a network constraint on Android, a beginBackgroundTask drain on iOS. Scheduled on ProcessLifecycleOwner ON_STOP / scenePhase .background.
Add unit test in MobileSessionUploadTest to verify stream serialization only sends `sensor_name` and `unit_symbol`.
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
Keeps mobile sessions uploaded automatically while the process lives, and hands leftover work to a platform scheduler when the app goes to background.
MobileUploadLoop(commonMain)A reactive loop driven by local DB state, not events. It observes two SQLDelight queries:
selectSessionsWithPending— mobile sessions with rows inpending_uploadsselectAwaitingFinish— stopped mobile sessions (end_timeset) not yetis_finished_on_serverTiming rules:
flushInterval(60 s) to batch — fewer radio wakeups.drainOnce()runs a single pass with no batching wait, for the platform backstops. It shares aMutexwith the loop, so the two never upload side by side.MobileSessionUploadnow implements a newSessionUploaderfun interface, so the loop can be tested against a fake.Platform backstops (
UploadBackstop.schedule())WorkManagerUploadBackstop: unique one-off work ("mobile-upload",KEEP),NetworkType.CONNECTED, WorkManager backoff viaResult.retry(). Survives process death and reboot. Triggered fromProcessLifecycleOwnerON_STOPinAircastingApp. Replaces the legacyConnectivityReceiver.IosUploadBackstop: onedrainOnce()insidebeginBackgroundTask(~30 s), ended synchronously in the expiration handler. Triggered fromscenePhase == .backgroundiniOSApp.swift. Known limit: after a force-quit, pending uploads wait for the next launch. They are delayed but never lost, since they stay in SQLite.Wiring
uploadModule(Koin) registersMobileMeasurementUploader,MobileSessionUpload(asSessionUploader) andMobileUploadLoop.initKoinstarts the loop for the life of the process.UploadBackstop. iOS exposesscheduleUploadBackstop()fromKoinIos.kt.androidx.lifecycle:lifecycle-process.Fix
AircastingJson:explicitNulls = false, so null fields are omitted from request bodies. Non-null defaults (e.g.contribute = true) still go out. Test added inMobileSessionUploadTest.Docs
docs/API_V3.mdupload section updated:flushNow()dropped, replaced bydrainOnce()backstops; Android/iOS hooks documented.Tests
MobileUploadLoopTest: batching window, immediate upload for awaiting-finish sessions, backoff doubling/cap/reset, partial failures, stale-snapshot guard,drainOncebehavior.SessionRepositoryTest/UploadOutboxTest: the two work queries (mobile-only, distinct, finished-state filtering).MobileSessionUploadTest: null-field serialization.Test plan
./gradlew :shared:allTestsgreenmobile-uploadwork runs and the session is finished on the server🤖 Generated with Claude Code