Skip to content

TODO: a check-off on the phone waits behind the reminder scan (218-220) - #118

Open
Timtam wants to merge 5 commits into
mainfrom
docs/todo-slow-check-off
Open

Timtam wants to merge 5 commits into
mainfrom
docs/todo-slow-check-off

Conversation

@Timtam

@Timtam Timtam commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner

Records Toni's report of 2026-10-06 and the three decisions it led to. Docs only; nothing is implemented yet.

The report

Completing a Vikunja task with the rotor action took more than a second to confirm on the iPhone.

The log shows:

  • The write itself is quick. It takes about 0.2 s for a one-off task and about 0.4 s for a recurring one. The confirmation waits for it on purpose.
  • The rest was a queue wait. upcomingRemindersJson runs on Expo's shared serial default queue. A scan mostly takes 3.4 to 4 s. updateTaskJson and the settings reads queued behind it.
  • The first check-off had started that scan itself (09:33:57 to 09:34:01 UTC, 4.0 s). Its list reload flushed through cacheObserver.ts, which restarted the 2.5 s reminder debounce. The second check-off's PATCH came 32 ms after the scan ended.

TODO B13

  • 218: the scan gets its own native queue on iOS and Android. rescheduleReminders runs again after a scan instead of dropping a request that arrives during one. A log line records when the check-off fired.
  • 219: decided against a 5-minute cache on the phone; the scan stays live.
  • 220: TasksFeature::update_task takes the previous row, so adapters send only what changed. Vikunja then skips the assignee PUT and the GET /tasks/{id} behind reconcile_parent. This is a deliberate behaviour change: a newer change from another device is no longer overwritten. The row has to cross the plugin interface, which today carries a bare Task, so this includes the ABI question.
  • Open: a later scan stood still for 17 s and ended with every socket dead, as after each return from the background. The app was most likely suspended. Whether such a break also happens in the foreground is open.

First check (ee3e5e8)

Two checkers (code, log) and one skeptic per finding. Seven findings were confirmed and fixed:

  • The trigger chain. The scan starts 2.5 s after the cacheObserver flush, not 2.5 s after the check-off. That is about 3.8 s after the write.
  • Scan times. They are now stated as measured: mostly 3.4 to 4 s, once 5.2 s, once 21 s.
  • The plugin interface in 220. Every external adapter is reached only through it, and ffi_update_task decodes a bare Task.
  • 219. The phone has no push, so a change from another device arrives at the next scan, not at once.
  • Wording. The reminder scan is now called "Erinnerungs-Durchlauf" throughout, so it no longer shares the word "Abgleich" with sync. The GET is named as the task's own read.

Three were refuted.

Second check (b543594)

Five findings were confirmed and fixed:

  • Which scan. The scan the second check-off waited on is now named with its time and length, so it cannot be read as the 21 s one.
  • The 17 s silence. The Vikunja server answered in 51 ms; then the whole log was silent. Every socket was dead afterwards, including ones to servers the scan was not using, as at each return from the background. The flag now says the app was most likely suspended.
  • The plugin interface in 220. The device adapter is reached directly, not through the plugin interface. Only six plugins have ffi_update_task.

Two were refuted.

Third check (8bdb03d)

Three findings were confirmed and fixed:

  • Built-in adapters. The local store is the second task adapter that both hosts call directly, besides the device adapter.
  • The 17 s silence. The Vikunja server sent its headers in 51 ms, but the body never arrived.
  • Grammar. One genitive was wrong.

220 now also says that the host must read the cached row before every write.

Fourth check (528fec9)

Three findings were confirmed and fixed:

  • Where the cache is read today. Both hosts already read the cached list after every external task write (write_through_task), and again after completing a recurring task. Neither read comes before the write.
  • The 90 ms. They were measured on the iPhone. The two saved requests apply to both hosts.
  • Who calls whom. The sentence on the built-in adapters now says plainly that the hosts call them.

One was refuted.

Fifth check (528fec9)

No findings. Three wording suggestions were refuted: each sentence is correct as written.

Docs

TODO only. DESIGN describes no behaviour that changes here.

🤖 Generated with Claude Code

Timtam and others added 5 commits October 6, 2026 13:49
Toni's report: completing a Vikunja task with the rotor action took more
than a second to confirm on the iPhone. The write itself takes about 0.2 s
(0.4 s for a recurring task); the rest was the reminder scan holding Expo's
shared serial queue. Recorded under B13 with the three decisions: the scan
gets its own native queue plus a rerun flag and a log line for the
check-off (218), the phone keeps reading live with no 5-minute cache (219),
and update_task takes the previous row so adapters send only what changed
(220). The 17 s hang on an unreadable Vikunja page stays open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d one word for the scan

The scan starts 2.5 s after the cacheObserver flush, which restarts the
debounce, not 2.5 s after the check-off. Scan times as measured: mostly
3.4-4 s, once 5.2 s, once 21 s with 17 s on one Vikunja page. 220 names
the plugin interface, which every external adapter is reached through.
219 no longer promises that a change from another device arrives at once.
The reminder scan is called Erinnerungs-Durchlauf throughout, so it no
longer shares a word with sync.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s silence

The scan the second check-off waited on is named with its time and length
(09:33:57-09:34:01 UTC, 4.0 s), so it is not read as the 21 s one. That
21 s scan was silent for 17 s and ended with every socket dead, as after
each return from the background: the app was most likely suspended, and
the Vikunja server had answered in 51 ms. 220 names the device adapter as
the one task adapter outside the plugin interface, and the six task
plugins instead of every plugin crate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…me, and one sentence

The local store, like the device adapter, is called directly by the hosts,
not through the plugin interface. The Vikunja server sent its headers in
51 ms, but the body never arrived. 220 says the host reads the cached row
before every write; today it reads the list only when a recurring task is
completed. One genitive fixed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d on the phone, who calls whom

Both hosts already read the cached list after every external task write
(write_through_task), and again after completing a recurring task; never
before the write. The 90 ms were measured on the iPhone; two requests per
change are saved on both hosts. The sentence on the built-in adapters now
says plainly that the hosts call them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant