Android: stop over-fetching after local edits and on WS echoes#307
Merged
Android: stop over-fetching after local edits and on WS echoes#307
Conversation
- Add SyncCoordinator.flushPending(): flush-only path for local mutations, no server refresh. The WebSocket echo of the write drives reconciliation. - Route all repository write paths to flushPending() instead of syncOnce(); drop unused updateTasksFromWebSocket/updateLabelsFromWebSocket helpers. - Coalesce syncOnceBlocking with any in-flight sync: if a syncOnce job is already running, join it instead of queueing a duplicate full cycle. - Extend WebSocketSyncBridge to also handle label_* events, so per-ViewModel WS listeners can go away. - Drop collectWebSocketMessages from LabelViewModel entirely; trim TaskListViewModel's listener to only refresh completed tasks (the only data not covered by SyncCoordinator). Net effect: a single local edit now produces one server write + at most one background refresh (triggered by the WS echo, debounced 500ms), instead of 3 full refreshes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Reduces redundant full refreshes on Android by separating “flush outbox” from “refresh from server”, and consolidating WebSocket-triggered reconciliation through WebSocketSyncBridge + SyncCoordinator.
Changes:
- Introduces
SyncCoordinator.flushPending()and updates task/label write paths to flush-only (no immediate full refresh). - Coalesces
syncOnceBlocking()calls to avoid duplicate full sync cycles. - Expands
WebSocketSyncBridgeto trigger reconciliation for both task and label WS events; trims/removes per-ViewModel WS listeners accordingly.
Show a summary per file
| File | Description |
|---|---|
| android/app/src/main/java/com/dkhalife/tasks/viewmodel/TaskListViewModel.kt | Limits WS-driven refreshes to completed-task endpoint only. |
| android/app/src/main/java/com/dkhalife/tasks/viewmodel/LabelViewModel.kt | Removes WS listener dependency; relies on repository/DB updates. |
| android/app/src/main/java/com/dkhalife/tasks/repo/TaskRepository.kt | Switches write paths from syncOnce() to flushPending(); removes unused WS helper. |
| android/app/src/main/java/com/dkhalife/tasks/repo/LabelRepository.kt | Switches write paths from syncOnce() to flushPending(); removes unused WS helper. |
| android/app/src/main/java/com/dkhalife/tasks/data/sync/WebSocketSyncBridge.kt | Broadens event set (tasks + labels) that triggers debounced reconciliation sync. |
| android/app/src/main/java/com/dkhalife/tasks/data/sync/SyncCoordinator.kt | Adds flush-only path and modifies syncOnceBlocking() to join in-flight work. |
Copilot's findings
- Files reviewed: 6/6 changed files
- Comments generated: 3
- Track activeFullJob only for full sync cycles; syncOnceBlocking() will no longer join a flush-only job and skip its required refresh. - flushPending() always launches a coroutine that serializes on the mutex, so newly-inserted outbox rows cannot be stranded by an in-flight refresh; redundant concurrent flushes collapse into cheap no-ops. - Clarify TaskListViewModel WS comment. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.
Problem
A single local edit on Android triggers up to 3 full
GET /tasks+ 3GET /labelsround-trips:TaskRepository.updateTask(or similar) callssyncCoordinator.syncOnce(), which flushes the outbox (necessary) then full-refreshes (not necessary — we just wrote the authoritative state).WebSocketSyncBridge→ anothersyncOnceBlocking→ another full refresh.TaskListViewModel/LabelViewModelindependently listen to the same WS events and ALSO callrefreshTasks()/refreshLabels()→ yet another full refresh.The WS echo alone is sufficient to reconcile authoritative server state. The intuition: writes should only flush; refresh happens on the incoming echo.
Changes
SyncCoordinator.flushPending(): new flush-only path for locally-initiated mutations. Coalesces with any in-flight sync (the running outbox loop will drain the newly inserted row).syncCoordinator.syncOnce()→syncCoordinator.flushPending(). The unusedupdateTasksFromWebSocket/updateLabelsFromWebSockethelpers (no callers) were removed.syncOnceBlockingcoalesce: if asyncOncejob is already running, join it instead of queueing a duplicate full cycle. This also dedupes the app-start + first-screen double sync.WebSocketSyncBridge:TASK_EVENTS→SYNC_EVENTS, now also includeslabel_*events so per-ViewModel WS listeners can go away.LabelViewModel: WS listener dropped entirely; DB Flow observation covers it.WebSocketManagerdep removed.TaskListViewModel: WS listener trimmed to only refresh completed tasks on relevant events — that endpoint is not covered bySyncCoordinator.refreshAll.Net effect
A single local edit now costs: 1 write request + 1 debounced background refresh (from the WS echo, ~500 ms later). Down from up to 7 round-trips. Remote changes (other clients) still reconcile in ≤500 ms via the same WS path.