From ad3806dd88ea39d82b9b16d71299e60fd3735777 Mon Sep 17 00:00:00 2001 From: Toni Barth Date: Tue, 6 Oct 2026 13:49:00 +0200 Subject: [PATCH 1/5] TODO: a check-off on the phone waits behind the reminder scan (218-220) 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 --- TODO.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/TODO.md b/TODO.md index c10b5235..2a19e47c 100644 --- a/TODO.md +++ b/TODO.md @@ -3204,6 +3204,59 @@ an dem `check-ffi-bridges.mjs` die Schlüssel beider Brücken prüft), das Proto nennt, wann sie den Status entschieden hat. ✓ im Einsatz (Build vom 2026-10-04, von Toni getestet). +### B13 · Abhaken am Handy wartet auf den Erinnerungs-Abgleich (218-220) `[ ]` + +Gemeldet am 2026-10-06: Eine Vikunja-Aufgabe per Rotor-Aktion als erledigt zu +markieren, dauerte am iPhone über eine Sekunde bis zur Ansage. Laut Protokoll +dauert das Schreiben selbst etwa 0,2 s bei einer einmaligen Aufgabe (PATCH, +`PUT assignees/bulk`, `GET` für `reconcile_parent`) und etwa 0,4 s bei einer +wiederkehrenden (dazu die nächste Runde anlegen und in ihre Kanban-Spalte +legen). Auf dieses Schreiben wartet die Ansage bewusst, sie ist nicht +optimistisch. Der Rest war Warten in der Schlange: `upcomingRemindersJson`, der +Erinnerungs-Abgleich, läuft auf Expos einer gemeinsamen seriellen +Standard-Warteschlange (`CalFfiModule.swift` und `.kt`, ohne `runOnQueue`). Er +liest live alle Konten (3,4 bis 4 s, einmal 17 s) und hält währenddessen +`updateTaskJson` und die Einstellungs-Lesungen davor auf. Das erste Abhaken +hatte den Abgleich selbst ausgelöst (`scheduleBackgroundPush` → +`refreshRemindersSoon`, 2,5 s später); das PATCH des zweiten, wiederkehrenden, +kam 32 ms nach seinem Ende. Die Aufteilung der Warteschlangen (49f7a9c) hatte +diese Funktion ausgelassen. Der Desktop ist nicht betroffen (eigener +tokio-Arbeiter). Die Lücke von etwa 0,9 s nach dem Nachladen der Liste ist +gewollt (700 ms Zusammenfassen in `cacheObserver.ts`) und kommt nach der Ansage. + +- [ ] **218 · Eigene Warteschlange für den Abgleich (iOS und Android).** + `upcomingRemindersJson` bekommt eine eigene serielle Warteschlange: unter iOS + eine `DispatchQueue` wie `accessQueue`, unter Android einen eigenen Thread + wie `slowScope`. Nicht `slowQueue`, sonst warten Senden und Abgleichen auf + ihn und er auf sie. Dazu `rescheduleReminders` + (`mobile/src/reminders/scheduler.ts`): Heute verwirft es eine Anfrage, die + während eines Abgleichs kommt (`if (inFlight) return;`). Künftig läuft danach + noch einer, sonst kann ein Abgleich, der vor dem Schreiben gelesen hat, eine + Erinnerung für eine schon erledigte Aufgabe stehen lassen; mit eigener + Warteschlange wird das häufiger. Und eine Protokollzeile, wann das Abhaken + ausgelöst wurde, damit die nächste Messung die Zeit vom Tippen bis zum PATCH + direkt zeigt. Braucht einen Handy-Build. +- [-] **219 · Kein 5-Minuten-Speicher am Handy.** Entschieden: Der Abgleich am + Handy liest weiter bei jedem Anlass live, anders als der Desktop, der die + Konten 5 Minuten aufbewahrt. So erreicht eine Änderung von einem anderen Gerät + die Benachrichtigungen sofort. Der Preis: im Protokoll etwa 6 volle + Durchläufe pro Minute Benutzung; nach 218 halten sie nichts mehr auf. +- [ ] **220 · Nur Geändertes senden.** `TasksFeature::update_task` + (`cal-core`) bekommt die vorige Zeile als optionalen, allgemeinen Parameter; + beide Hosts geben die Zeile aus dem Cache von vor dem Schreiben mit, die der + Host-Kern für wiederkehrende Aufgaben schon liest. Jeder Adapter darf damit + nur senden, was sich geändert hat. Vikunja spart so `PUT assignees/bulk` und + den `GET` für die Eltern-Aufgabe, wenn Zuweisungen und Eltern gleich bleiben + (etwa 90 ms je Änderung, Desktop und Handy). Gewollte Verhaltensänderung: Hat + ein anderes Gerät Zuweisungen oder Eltern seit dem letzten Laden geändert, + überschreibt Aperio das nicht mehr mit seinem alten Stand. Berührt alle + Adapter (Standard: den Parameter nicht beachten) und beide Hosts. + +🚩 **Offen:** Ein Abgleich hing 17 s an einer Vikunja-Seite („error decoding +response body“, 09:34:25 UTC); warum die Antwort nicht lesbar war, ist +ungeklärt. Bis 218 hält so ein Hänger jedes Schreiben am Handy auf, danach nur +noch den Abgleich. + ## 🟡 C. Bewusste Deferrals (dokumentiert, niedrigere Priorität) ### C1 · Task-Recurrence in EWS & Todoist (§9.1) From ee3e5e8f366e1e362081273eb3799afc713525c6 Mon Sep 17 00:00:00 2001 From: Toni Barth Date: Tue, 6 Oct 2026 13:59:45 +0200 Subject: [PATCH 2/5] First check of #118: the trigger chain, scan times, the plugin ABI and 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 --- TODO.md | 108 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 64 insertions(+), 44 deletions(-) diff --git a/TODO.md b/TODO.md index 2a19e47c..fb30717f 100644 --- a/TODO.md +++ b/TODO.md @@ -3204,58 +3204,78 @@ an dem `check-ffi-bridges.mjs` die Schlüssel beider Brücken prüft), das Proto nennt, wann sie den Status entschieden hat. ✓ im Einsatz (Build vom 2026-10-04, von Toni getestet). -### B13 · Abhaken am Handy wartet auf den Erinnerungs-Abgleich (218-220) `[ ]` +### B13 · Abhaken am Handy wartet auf den Erinnerungs-Durchlauf (218-220) `[ ]` Gemeldet am 2026-10-06: Eine Vikunja-Aufgabe per Rotor-Aktion als erledigt zu -markieren, dauerte am iPhone über eine Sekunde bis zur Ansage. Laut Protokoll -dauert das Schreiben selbst etwa 0,2 s bei einer einmaligen Aufgabe (PATCH, -`PUT assignees/bulk`, `GET` für `reconcile_parent`) und etwa 0,4 s bei einer -wiederkehrenden (dazu die nächste Runde anlegen und in ihre Kanban-Spalte -legen). Auf dieses Schreiben wartet die Ansage bewusst, sie ist nicht -optimistisch. Der Rest war Warten in der Schlange: `upcomingRemindersJson`, der -Erinnerungs-Abgleich, läuft auf Expos einer gemeinsamen seriellen +markieren, dauerte am iPhone über eine Sekunde bis zur Ansage, laut Toni beim +zweiten von zwei Abhaken, einer wiederkehrenden Aufgabe. Laut Protokoll dauert +das Schreiben selbst etwa 0,2 s bei einer einmaligen Aufgabe (PATCH, +`PUT assignees/bulk`, dann `GET /tasks/{id}`, mit dem `reconcile_parent` die +Eltern-Beziehung liest) und etwa 0,4 s bei einer wiederkehrenden (dazu die +nächste Runde anlegen und in ihre Kanban-Spalte legen). Auf dieses Schreiben +wartet die Ansage bewusst, sie ist nicht optimistisch. + +Der Rest war Warten in der Schlange. `upcomingRemindersJson`, der +Erinnerungs-Durchlauf, läuft auf Expos einer gemeinsamen seriellen Standard-Warteschlange (`CalFfiModule.swift` und `.kt`, ohne `runOnQueue`). Er -liest live alle Konten (3,4 bis 4 s, einmal 17 s) und hält währenddessen -`updateTaskJson` und die Einstellungs-Lesungen davor auf. Das erste Abhaken -hatte den Abgleich selbst ausgelöst (`scheduleBackgroundPush` → -`refreshRemindersSoon`, 2,5 s später); das PATCH des zweiten, wiederkehrenden, -kam 32 ms nach seinem Ende. Die Aufteilung der Warteschlangen (49f7a9c) hatte -diese Funktion ausgelassen. Der Desktop ist nicht betroffen (eigener -tokio-Arbeiter). Die Lücke von etwa 0,9 s nach dem Nachladen der Liste ist -gewollt (700 ms Zusammenfassen in `cacheObserver.ts`) und kommt nach der Ansage. - -- [ ] **218 · Eigene Warteschlange für den Abgleich (iOS und Android).** - `upcomingRemindersJson` bekommt eine eigene serielle Warteschlange: unter iOS - eine `DispatchQueue` wie `accessQueue`, unter Android einen eigenen Thread - wie `slowScope`. Nicht `slowQueue`, sonst warten Senden und Abgleichen auf - ihn und er auf sie. Dazu `rescheduleReminders` - (`mobile/src/reminders/scheduler.ts`): Heute verwirft es eine Anfrage, die - während eines Abgleichs kommt (`if (inFlight) return;`). Künftig läuft danach - noch einer, sonst kann ein Abgleich, der vor dem Schreiben gelesen hat, eine - Erinnerung für eine schon erledigte Aufgabe stehen lassen; mit eigener - Warteschlange wird das häufiger. Und eine Protokollzeile, wann das Abhaken - ausgelöst wurde, damit die nächste Messung die Zeit vom Tippen bis zum PATCH - direkt zeigt. Braucht einen Handy-Build. -- [-] **219 · Kein 5-Minuten-Speicher am Handy.** Entschieden: Der Abgleich am - Handy liest weiter bei jedem Anlass live, anders als der Desktop, der die - Konten 5 Minuten aufbewahrt. So erreicht eine Änderung von einem anderen Gerät - die Benachrichtigungen sofort. Der Preis: im Protokoll etwa 6 volle - Durchläufe pro Minute Benutzung; nach 218 halten sie nichts mehr auf. +liest live alle Konten, meist in 3,4 bis 4 s, einmal in 5,2 s und einmal in +21 s, davon 17 s Warten auf eine einzige Vikunja-Seite. Solange er läuft, +warten `updateTaskJson` und die Einstellungs-Lesungen, die das Abhaken vor dem +Schreiben macht. Das erste Abhaken hatte diesen Durchlauf selbst ausgelöst: +Nach dem Schreiben lädt die Liste neu, `cacheObserver.ts` fasst die +Cache-Meldungen 700 ms zusammen und ruft dann `refreshRemindersSoon` auf. Das +startet seine 2,5 s bei jedem Aufruf neu und überholt so den früheren Aufruf +aus `scheduleBackgroundPush`; der Durchlauf begann etwa 3,8 s nach dem +Schreiben. Das PATCH des zweiten Abhakens kam 32 ms nach dem Ende dieses +Durchlaufs. Die Aufteilung der Warteschlangen (49f7a9c) hatte diese Funktion +ausgelassen. Der Desktop ist nicht betroffen (eigener tokio-Arbeiter). Die +Lücke von etwa 0,9 s nach dem Nachladen der Liste ist gewollt (dieselben +700 ms) und kommt nach der Ansage. + +- [ ] **218 · Eigene Warteschlange für den Erinnerungs-Durchlauf (iOS und + Android).** `upcomingRemindersJson` bekommt eine eigene serielle + Warteschlange: unter iOS eine `DispatchQueue` wie `accessQueue`, unter + Android einen eigenen Thread wie `slowScope`. Nicht `slowQueue`, sonst warten + Senden und Synchronisieren (`pushNow`, `syncNowJson`) auf den Durchlauf und + er auf sie. Dazu `rescheduleReminders` (`mobile/src/reminders/scheduler.ts`): + Heute verwirft es eine Anfrage, die während eines Durchlaufs kommt + (`if (inFlight) return;`). Künftig läuft danach noch einer, sonst kann ein + Durchlauf, der vor dem Schreiben gelesen hat, eine Erinnerung für eine schon + erledigte Aufgabe stehen lassen; mit eigener Warteschlange wird das häufiger. + Und eine Protokollzeile, wann das Abhaken ausgelöst wurde, damit die nächste + Messung die Zeit vom Tippen bis zum PATCH direkt zeigt; heute ist sie nur + erschlossen. Braucht einen Handy-Build. +- [-] **219 · Kein 5-Minuten-Speicher am Handy.** Entschieden: Der + Erinnerungs-Durchlauf am Handy liest die Konten weiter bei jedem Anlass live. + Der Desktop dagegen bewahrt die Erinnerungen, die er aus den Konten gelesen + hat, 5 Minuten auf. So sieht jeder Durchlauf am Handy den aktuellen Stand: + Eine Änderung von einem anderen Gerät erreicht die Benachrichtigungen beim + nächsten Anlass (Start, Rückkehr in den Vordergrund, Nachladen, + Synchronisieren), ohne zusätzlich auf einen bis zu 5 Minuten alten Speicher + zu warten. Der Preis: im Protokoll etwa 6 volle Durchläufe in einer Minute + Benutzung; nach 218 halten sie nichts mehr auf. - [ ] **220 · Nur Geändertes senden.** `TasksFeature::update_task` (`cal-core`) bekommt die vorige Zeile als optionalen, allgemeinen Parameter; beide Hosts geben die Zeile aus dem Cache von vor dem Schreiben mit, die der Host-Kern für wiederkehrende Aufgaben schon liest. Jeder Adapter darf damit nur senden, was sich geändert hat. Vikunja spart so `PUT assignees/bulk` und - den `GET` für die Eltern-Aufgabe, wenn Zuweisungen und Eltern gleich bleiben - (etwa 90 ms je Änderung, Desktop und Handy). Gewollte Verhaltensänderung: Hat - ein anderes Gerät Zuweisungen oder Eltern seit dem letzten Laden geändert, - überschreibt Aperio das nicht mehr mit seinem alten Stand. Berührt alle - Adapter (Standard: den Parameter nicht beachten) und beide Hosts. - -🚩 **Offen:** Ein Abgleich hing 17 s an einer Vikunja-Seite („error decoding -response body“, 09:34:25 UTC); warum die Antwort nicht lesbar war, ist -ungeklärt. Bis 218 hält so ein Hänger jedes Schreiben am Handy auf, danach nur -noch den Abgleich. + den `GET /tasks/{id}` für `reconcile_parent`, wenn Zuweisungen und Eltern + gleich bleiben (etwa 90 ms je Änderung, Desktop und Handy). Gewollte + Verhaltensänderung: Hat ein anderes Gerät Zuweisungen oder Eltern seit dem + letzten Laden geändert, überschreibt Aperio das nicht mehr mit seinem alten + Stand. Berührt `cal-core`, die Plugin-Schnittstelle, alle Adapter (Standard: + den Parameter nicht beachten) und beide Hosts. Die Plugin-Schnittstelle ist + der eigentliche Weg: Jeder externe Adapter, Vikunja eingeschlossen, wird nur + über sie erreicht, und heute geht dort ein nacktes `Task` hinüber (Shim in + `plugin-core`, `ffi_update_task` jedes `*-plugin`-Crates). Ohne neue + Argumentform oder eigenen Eintrag in der Vtable käme die vorige Zeile nie an; + dazu gehört die Frage nach der ABI-Version (`ABI_VERSION` heute 4, + `ABI_VERSION_MIN` 3). + +🚩 **Offen:** Ein Durchlauf hing 17 s an einer einzigen Vikunja-Seite („error +decoding response body“, 09:34:25 UTC); warum die Antwort nicht lesbar war, +ist ungeklärt. Bis 218 hält so ein Hänger jedes Schreiben am Handy auf, danach +nur noch den Durchlauf. ## 🟡 C. Bewusste Deferrals (dokumentiert, niedrigere Priorität) From b543594a60570834d9454f0c02958c04583ba78f Mon Sep 17 00:00:00 2001 From: Toni Barth Date: Tue, 6 Oct 2026 14:10:43 +0200 Subject: [PATCH 3/5] Second check of #118: which scan the check-off waited on, and the 17 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 --- TODO.md | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/TODO.md b/TODO.md index fb30717f..19647c9f 100644 --- a/TODO.md +++ b/TODO.md @@ -3218,16 +3218,16 @@ wartet die Ansage bewusst, sie ist nicht optimistisch. Der Rest war Warten in der Schlange. `upcomingRemindersJson`, der Erinnerungs-Durchlauf, läuft auf Expos einer gemeinsamen seriellen Standard-Warteschlange (`CalFfiModule.swift` und `.kt`, ohne `runOnQueue`). Er -liest live alle Konten, meist in 3,4 bis 4 s, einmal in 5,2 s und einmal in -21 s, davon 17 s Warten auf eine einzige Vikunja-Seite. Solange er läuft, -warten `updateTaskJson` und die Einstellungs-Lesungen, die das Abhaken vor dem -Schreiben macht. Das erste Abhaken hatte diesen Durchlauf selbst ausgelöst: +liest live alle Konten, meist in 3,4 bis 4 s, einmal in 5,2 s. Solange er +läuft, warten `updateTaskJson` und die Einstellungs-Lesungen, die das Abhaken +vor dem Schreiben macht. Den Durchlauf, auf den das zweite Abhaken wartete +(09:33:57 bis 09:34:01 UTC, 4,0 s), hatte das erste Abhaken selbst ausgelöst: Nach dem Schreiben lädt die Liste neu, `cacheObserver.ts` fasst die Cache-Meldungen 700 ms zusammen und ruft dann `refreshRemindersSoon` auf. Das -startet seine 2,5 s bei jedem Aufruf neu und überholt so den früheren Aufruf +startet seine 2,5 s bei jedem Aufruf neu und verschiebt so den früheren Aufruf aus `scheduleBackgroundPush`; der Durchlauf begann etwa 3,8 s nach dem -Schreiben. Das PATCH des zweiten Abhakens kam 32 ms nach dem Ende dieses -Durchlaufs. Die Aufteilung der Warteschlangen (49f7a9c) hatte diese Funktion +Schreiben. Das PATCH des zweiten Abhakens kam 32 ms nach der letzten Antwort +dieses Durchlaufs. Die Aufteilung der Warteschlangen (49f7a9c) hatte diese Funktion ausgelassen. Der Desktop ist nicht betroffen (eigener tokio-Arbeiter). Die Lücke von etwa 0,9 s nach dem Nachladen der Liste ist gewollt (dieselben 700 ms) und kommt nach der Ansage. @@ -3265,17 +3265,24 @@ Lücke von etwa 0,9 s nach dem Nachladen der Liste ist gewollt (dieselben letzten Laden geändert, überschreibt Aperio das nicht mehr mit seinem alten Stand. Berührt `cal-core`, die Plugin-Schnittstelle, alle Adapter (Standard: den Parameter nicht beachten) und beide Hosts. Die Plugin-Schnittstelle ist - der eigentliche Weg: Jeder externe Adapter, Vikunja eingeschlossen, wird nur - über sie erreicht, und heute geht dort ein nacktes `Task` hinüber (Shim in - `plugin-core`, `ffi_update_task` jedes `*-plugin`-Crates). Ohne neue - Argumentform oder eigenen Eintrag in der Vtable käme die vorige Zeile nie an; - dazu gehört die Frage nach der ABI-Version (`ABI_VERSION` heute 4, - `ABI_VERSION_MIN` 3). - -🚩 **Offen:** Ein Durchlauf hing 17 s an einer einzigen Vikunja-Seite („error -decoding response body“, 09:34:25 UTC); warum die Antwort nicht lesbar war, -ist ungeklärt. Bis 218 hält so ein Hänger jedes Schreiben am Handy auf, danach -nur noch den Durchlauf. + der eigentliche Weg: Jeder Aufgaben-Adapter außer dem Geräte-Adapter (den + der Handy-Host direkt einhängt), Vikunja eingeschlossen, wird nur über sie + erreicht, und heute geht dort ein nacktes `Task` hinüber (Shim in + `plugin-core`, `ffi_update_task` der sechs Aufgaben-Plugins: CalDAV, EWS, + Google, Graph, Todoist, Vikunja). Ohne neue Argumentform oder eigenen + Eintrag in der Vtable käme die vorige Zeile dort nie an; dazu gehört die + Frage nach der ABI-Version (`ABI_VERSION` heute 4, `ABI_VERSION_MIN` 3). + +🚩 **Offen:** Ein späterer Durchlauf, nach dem zweiten Abhaken, stand 17 s +still („error decoding response body“, 09:34:25 UTC). In dieser Zeit schwieg +das Protokoll ganz; danach waren alle Verbindungen tot, auch die zu Servern, +die der Durchlauf gerade nicht nutzte, genau wie nach den Rückkehren aus dem +Hintergrund um 08:36:45, 09:05:04 und 09:33:22 UTC. Vermutlich war die App im +Hintergrund und iOS hat die Verbindungen geschlossen; der Vikunja-Server hatte +nach 51 ms geantwortet. Offen ist nur, ob so ein Abbruch auch im Vordergrund +vorkommt. Nach einer Rückkehr läuft zuerst der unterbrochene Durchlauf zu Ende +(um 09:33:22 noch 4,2 s); bis 218 wartet ein Schreiben in dieser Zeit darauf, +danach nur noch der Durchlauf. ## 🟡 C. Bewusste Deferrals (dokumentiert, niedrigere Priorität) From 8bdb03d3cb1c26054060760b58acdd32c7c4fd9b Mon Sep 17 00:00:00 2001 From: Toni Barth Date: Tue, 6 Oct 2026 14:23:18 +0200 Subject: [PATCH 4/5] Third check of #118: both built-in task adapters, the headers that came, 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 --- TODO.md | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/TODO.md b/TODO.md index 19647c9f..4ad9db5a 100644 --- a/TODO.md +++ b/TODO.md @@ -3216,8 +3216,9 @@ nächste Runde anlegen und in ihre Kanban-Spalte legen). Auf dieses Schreiben wartet die Ansage bewusst, sie ist nicht optimistisch. Der Rest war Warten in der Schlange. `upcomingRemindersJson`, der -Erinnerungs-Durchlauf, läuft auf Expos einer gemeinsamen seriellen -Standard-Warteschlange (`CalFfiModule.swift` und `.kt`, ohne `runOnQueue`). Er +Erinnerungs-Durchlauf, läuft auf der gemeinsamen seriellen +Standard-Warteschlange von Expo (`CalFfiModule.swift` und `.kt`, ohne +`runOnQueue`). Er liest live alle Konten, meist in 3,4 bis 4 s, einmal in 5,2 s. Solange er läuft, warten `updateTaskJson` und die Einstellungs-Lesungen, die das Abhaken vor dem Schreiben macht. Den Durchlauf, auf den das zweite Abhaken wartete @@ -3227,10 +3228,10 @@ Cache-Meldungen 700 ms zusammen und ruft dann `refreshRemindersSoon` auf. Das startet seine 2,5 s bei jedem Aufruf neu und verschiebt so den früheren Aufruf aus `scheduleBackgroundPush`; der Durchlauf begann etwa 3,8 s nach dem Schreiben. Das PATCH des zweiten Abhakens kam 32 ms nach der letzten Antwort -dieses Durchlaufs. Die Aufteilung der Warteschlangen (49f7a9c) hatte diese Funktion -ausgelassen. Der Desktop ist nicht betroffen (eigener tokio-Arbeiter). Die -Lücke von etwa 0,9 s nach dem Nachladen der Liste ist gewollt (dieselben -700 ms) und kommt nach der Ansage. +dieses Durchlaufs. Die Aufteilung der Warteschlangen (49f7a9c) hatte diese +Funktion ausgelassen. Der Desktop ist nicht betroffen (eigener +tokio-Arbeiter). Die Lücke von etwa 0,9 s nach dem Nachladen der Liste ist +gewollt (dieselben 700 ms) und kommt nach der Ansage. - [ ] **218 · Eigene Warteschlange für den Erinnerungs-Durchlauf (iOS und Android).** `upcomingRemindersJson` bekommt eine eigene serielle @@ -3256,8 +3257,9 @@ Lücke von etwa 0,9 s nach dem Nachladen der Liste ist gewollt (dieselben Benutzung; nach 218 halten sie nichts mehr auf. - [ ] **220 · Nur Geändertes senden.** `TasksFeature::update_task` (`cal-core`) bekommt die vorige Zeile als optionalen, allgemeinen Parameter; - beide Hosts geben die Zeile aus dem Cache von vor dem Schreiben mit, die der - Host-Kern für wiederkehrende Aufgaben schon liest. Jeder Adapter darf damit + beide Hosts lesen sie vor jedem Schreiben aus dem Cache und geben sie mit. + Heute liest der Host-Kern den Cache von vor dem Schreiben nur beim Erledigen + einer wiederkehrenden Aufgabe, als ganze Liste. Jeder Adapter darf damit nur senden, was sich geändert hat. Vikunja spart so `PUT assignees/bulk` und den `GET /tasks/{id}` für `reconcile_parent`, wenn Zuweisungen und Eltern gleich bleiben (etwa 90 ms je Änderung, Desktop und Handy). Gewollte @@ -3265,9 +3267,10 @@ Lücke von etwa 0,9 s nach dem Nachladen der Liste ist gewollt (dieselben letzten Laden geändert, überschreibt Aperio das nicht mehr mit seinem alten Stand. Berührt `cal-core`, die Plugin-Schnittstelle, alle Adapter (Standard: den Parameter nicht beachten) und beide Hosts. Die Plugin-Schnittstelle ist - der eigentliche Weg: Jeder Aufgaben-Adapter außer dem Geräte-Adapter (den - der Handy-Host direkt einhängt), Vikunja eingeschlossen, wird nur über sie - erreicht, und heute geht dort ein nacktes `Task` hinüber (Shim in + der eigentliche Weg: Jeder Aufgaben-Adapter außer den beiden eingebauten, + dem lokalen Speicher (`adapter-local`) und dem Geräte-Adapter, die die Hosts + direkt aufrufen, wird nur über sie erreicht, Vikunja eingeschlossen. Heute + geht dort ein nacktes `Task` hinüber (Shim in `plugin-core`, `ffi_update_task` der sechs Aufgaben-Plugins: CalDAV, EWS, Google, Graph, Todoist, Vikunja). Ohne neue Argumentform oder eigenen Eintrag in der Vtable käme die vorige Zeile dort nie an; dazu gehört die @@ -3278,11 +3281,12 @@ still („error decoding response body“, 09:34:25 UTC). In dieser Zeit schwieg das Protokoll ganz; danach waren alle Verbindungen tot, auch die zu Servern, die der Durchlauf gerade nicht nutzte, genau wie nach den Rückkehren aus dem Hintergrund um 08:36:45, 09:05:04 und 09:33:22 UTC. Vermutlich war die App im -Hintergrund und iOS hat die Verbindungen geschlossen; der Vikunja-Server hatte -nach 51 ms geantwortet. Offen ist nur, ob so ein Abbruch auch im Vordergrund -vorkommt. Nach einer Rückkehr läuft zuerst der unterbrochene Durchlauf zu Ende -(um 09:33:22 noch 4,2 s); bis 218 wartet ein Schreiben in dieser Zeit darauf, -danach nur noch der Durchlauf. +Hintergrund und iOS hat die Verbindungen geschlossen. Der Vikunja-Server hatte +nach 51 ms die Kopfzeilen geschickt; der Rumpf kam nicht mehr an, daher +„error decoding response body“. Offen ist nur, ob so ein Abbruch auch im +Vordergrund vorkommt. Nach einer Rückkehr läuft zuerst der unterbrochene +Durchlauf zu Ende (um 09:33:22 noch 4,2 s); bis 218 wartet ein Schreiben in +dieser Zeit darauf, danach nur noch der Durchlauf. ## 🟡 C. Bewusste Deferrals (dokumentiert, niedrigere Priorität) From 528fec9573214db6de9ef43c6b3ae5f61c86274b Mon Sep 17 00:00:00 2001 From: Toni Barth Date: Tue, 6 Oct 2026 14:33:23 +0200 Subject: [PATCH 5/5] Fourth check of #118: where the cache is read today, a timing measured 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 --- TODO.md | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/TODO.md b/TODO.md index 4ad9db5a..d7594f8f 100644 --- a/TODO.md +++ b/TODO.md @@ -3258,23 +3258,29 @@ gewollt (dieselben 700 ms) und kommt nach der Ansage. - [ ] **220 · Nur Geändertes senden.** `TasksFeature::update_task` (`cal-core`) bekommt die vorige Zeile als optionalen, allgemeinen Parameter; beide Hosts lesen sie vor jedem Schreiben aus dem Cache und geben sie mit. - Heute liest der Host-Kern den Cache von vor dem Schreiben nur beim Erledigen - einer wiederkehrenden Aufgabe, als ganze Liste. Jeder Adapter darf damit - nur senden, was sich geändert hat. Vikunja spart so `PUT assignees/bulk` und - den `GET /tasks/{id}` für `reconcile_parent`, wenn Zuweisungen und Eltern - gleich bleiben (etwa 90 ms je Änderung, Desktop und Handy). Gewollte - Verhaltensänderung: Hat ein anderes Gerät Zuweisungen oder Eltern seit dem - letzten Laden geändert, überschreibt Aperio das nicht mehr mit seinem alten - Stand. Berührt `cal-core`, die Plugin-Schnittstelle, alle Adapter (Standard: - den Parameter nicht beachten) und beide Hosts. Die Plugin-Schnittstelle ist - der eigentliche Weg: Jeder Aufgaben-Adapter außer den beiden eingebauten, - dem lokalen Speicher (`adapter-local`) und dem Geräte-Adapter, die die Hosts - direkt aufrufen, wird nur über sie erreicht, Vikunja eingeschlossen. Heute - geht dort ein nacktes `Task` hinüber (Shim in - `plugin-core`, `ffi_update_task` der sechs Aufgaben-Plugins: CalDAV, EWS, - Google, Graph, Todoist, Vikunja). Ohne neue Argumentform oder eigenen - Eintrag in der Vtable käme die vorige Zeile dort nie an; dazu gehört die - Frage nach der ABI-Version (`ABI_VERSION` heute 4, `ABI_VERSION_MIN` 3). + Heute liest der Host-Kern die Liste erst nach dem Schreiben beim Anbieter aus + dem Cache, jedes Mal als ganze Liste: bei jeder Änderung einer externen + Aufgabe (`write_through_task`, nur um zu prüfen, ob die Liste Zeilen hat) + und beim Erledigen einer wiederkehrenden Aufgabe + (`record_external_recurrence_completion`). Vor dem Schreiben liest er die + Zeile nie. Jeder Adapter darf damit nur senden, was sich geändert hat. + Vikunja spart so `PUT assignees/bulk` und den `GET /tasks/{id}` für + `reconcile_parent`, wenn Zuweisungen und Eltern gleich bleiben: zwei + Anfragen weniger je Änderung, auf Desktop und Handy, am iPhone gemessen + etwa 90 ms. Gewollte Verhaltensänderung: Hat ein anderes Gerät Zuweisungen + oder Eltern seit dem letzten Laden geändert, überschreibt Aperio das nicht + mehr mit seinem alten Stand. Berührt `cal-core`, die Plugin-Schnittstelle, + alle Adapter (Standard: den Parameter nicht beachten) und beide Hosts. + + Die Plugin-Schnittstelle ist der eigentliche Weg. Die Hosts rufen nur die + beiden eingebauten Aufgaben-Adapter direkt auf, den lokalen Speicher + (`adapter-local`) und den Geräte-Adapter. Jeder andere Aufgaben-Adapter, + Vikunja eingeschlossen, wird nur über die Plugin-Schnittstelle erreicht, und + heute geht dort ein nacktes `Task` hinüber (Shim in `plugin-core`, + `ffi_update_task` der sechs Aufgaben-Plugins: CalDAV, EWS, Google, Graph, + Todoist, Vikunja). Ohne neue Argumentform oder eigenen Eintrag in der + Vtable käme die vorige Zeile dort nie an; dazu gehört die Frage nach der + ABI-Version (`ABI_VERSION` heute 4, `ABI_VERSION_MIN` 3). 🚩 **Offen:** Ein späterer Durchlauf, nach dem zweiten Abhaken, stand 17 s still („error decoding response body“, 09:34:25 UTC). In dieser Zeit schwieg