Bis hierher holte jeder Durchgang die volle Terminliste: ctag abfragen, Aenderung sehen, alles neu laden. Eine verschobene Besprechung kostete damit so viel wie ein Neuanfang -- bei einem Jahr Termine, alle fuenfzehn Minuten, auf Akku und Datenvolumen des Telefons. Kein zweiter Zaehler: Das ctag **ist** bereits eine Zahl je Kalender, die bei jedem Schreiben um eins steigt. Jedes Objekt merkt sich in sync_seq den Stand, bei dem es zuletzt angefasst wurde (Migration 24). Grabsteine (cal_tombstones) halten geloeschte UIDs fest -- ohne sie bliebe ein geloeschter Termin auf jedem Geraet stehen, das gerade nicht verbunden war. Sie stehen 180 Tage; beim Raeumen wandert sync_floor mit, und ein Token darunter bekommt 403 <valid-sync-token/> statt einer Antwort mit fehlenden Loeschungen. go-webdav kennt fuer REPORT nur calendar-query und calendar-multiget, und weder sync-token noch supported-report-set. Beide gehen denselben Weg wie getctag und calendar-color. Ohne supported-report-set probiert DAVx5 sync-collection gar nicht erst aus. Zwei Fehler noch vor dem Ausliefern gefunden: * Die Anfuehrungszeichen am ETag standen **innerhalb** der Maskierung -- xml.EscapeText machte daraus ". Gueltig, aber genug Clients vergleichen ETags roh und saehen bei jedem Durchgang eine Aenderung. * pruneTombstones nahm die erste Zeile eines DELETE ... RETURNING als Maximum. Das hat keine Reihenfolge -- der sync_floor waere zu niedrig gesetzt worden, und ein Client dazwischen haette Loeschungen verpasst. caldav-selbsttest.py: 62 -> 86 Pruefungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
39 lines
2.0 KiB
SQL
39 lines
2.0 KiB
SQL
-- Abgleich nur der Aenderungen (RFC 6578, sync-collection).
|
|
--
|
|
-- Bis hierher holte jeder Durchgang die **volle** Terminliste: Der Client
|
|
-- fragt das ctag ab, sieht es veraendert und laedt alles neu. Bei einem Jahr
|
|
-- Termine ist das jedes Mal dieselbe Menge Daten fuer eine Aenderung.
|
|
--
|
|
-- Was dafuer fehlt, ist eine Reihenfolge: "was hat sich seit Stand N getan".
|
|
-- Ein **zweiter** Zaehler waere dafuer nicht noetig -- das ctag ist bereits
|
|
-- genau das: eine Zahl je Kalender, die bei jedem Schreiben um eins steigt.
|
|
-- Sie wird deshalb hier zur Sync-Sequenz; jedes Objekt merkt sich den Stand,
|
|
-- bei dem es zuletzt angefasst wurde.
|
|
ALTER TABLE cal_objects ADD COLUMN IF NOT EXISTS sync_seq BIGINT NOT NULL DEFAULT 0;
|
|
|
|
-- Bestehende Objekte auf den heutigen Stand ihres Kalenders setzen. Ein
|
|
-- Client hat noch kein Token (die Eigenschaft gab es bis jetzt nicht), holt
|
|
-- also ohnehin einmal alles und bekommt dabei sein erstes Token.
|
|
UPDATE cal_objects o SET sync_seq = c.ctag
|
|
FROM cal_calendars c WHERE c.id = o.calendar_id;
|
|
|
|
CREATE INDEX IF NOT EXISTS cal_objects_sync ON cal_objects (calendar_id, sync_seq);
|
|
|
|
-- Grabsteine: Eine geloeschte Zeile kann nicht mehr erzaehlen, dass es sie
|
|
-- gab. Ohne sie bliebe ein geloeschter Termin auf jedem Geraet stehen, das
|
|
-- waehrend des Loeschens nicht verbunden war -- der teuerste Fehler dieser
|
|
-- Art, weil er unsichtbar ist.
|
|
CREATE TABLE IF NOT EXISTS cal_tombstones (
|
|
calendar_id UUID NOT NULL REFERENCES cal_calendars(id) ON DELETE CASCADE,
|
|
uid TEXT NOT NULL,
|
|
sync_seq BIGINT NOT NULL,
|
|
deleted_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
PRIMARY KEY (calendar_id, uid)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS cal_tombstones_sync ON cal_tombstones (calendar_id, sync_seq);
|
|
|
|
-- Bis zu diesem Stand sind Grabsteine weggeraeumt. Ein Client mit einem
|
|
-- aelteren Token bekommt eine klare Absage (<valid-sync-token/>) und macht
|
|
-- einen Vollabgleich -- besser als eine Antwort, in der Loeschungen fehlen.
|
|
ALTER TABLE cal_calendars ADD COLUMN IF NOT EXISTS sync_floor BIGINT NOT NULL DEFAULT 0;
|