Changeset ba76bf5 in Klonkt


Ignore:
Timestamp:
08/07/2026 05:33:24 PM (5 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
ed5e7ab
Parents:
952baf3
git-author:
Robin <roboburr@…> (08/07/2026 05:32:58 PM)
git-committer:
Robin <roboburr@…> (08/07/2026 05:33:24 PM)
Message:

Drie gaten uit de 1.7.0-review: index, wekkers, en de exporter-tijdzone

Drie losstaande reparaties uit de review van de 66 commits. Elk mechanisch, elk met
een tegenproef dat de test zonder de fix ook echt faalt.

  1. De permalink-lookup van reacties had geen index. canonicalReactionUri zoekt op

(slug, url) en viel terug op idx_ap_timeline_slug, dus een scan van elke rij van die
slug. Dat gebeurt PER REACTIE in getInteractions, en de reactie-migratie erft het in
haar re-key-join die synchroon voor listen draait: de opstartkosten waren reacties
maal tijdlijnrijen. Nagemeten met EXPLAIN QUERY PLAN, nu een indexzoek.

  1. De feed-wekkers gingen af op likes en boosts. ap_interactions draagt naast replies

ook likes en announces, en die schrijven een LEGE object_uri (regel 2310, tegenover
o.id bij replies). Zonder WHEN bumpte elke inkomende like de rev, werd elke
long-poll-wachter gewekt, en kreeg die de hele collectie opnieuw terwijl er niets aan
veranderd was: precies de kosten die de 304 moest wegnemen. Bovendien belandde er een
rij op de lege string in ap_feed_state, die feedChangesSince als sleutel uitdeelt. Bij
ap_timeline was hier wel op gelet (de UPDATE OF sluit liked en boosted uit) en een
tabel verder vergeten.

  1. De exporter schreef lokale tijd als UTC. Kale Date.parse op SQLite's

'YYYY-MM-DD HH:MM:SS' leest dat als lokale tijd, dus op UTC+2 ging er twee uur van
elke stempel af voordat hij het archief in ging. Die verschuiving wordt bij het
exporteren INGEBAKKEN en valt niet weg bij het importeren. Het raakte de stempels die
de database zelf zet (concepten, ingeplande posts, gearchiveerde antwoorden), niet die
uit de editor, dus de schade was stil en gedeeltelijk. Dezelfde correctie stond al
twee keer in deze codebase, in isoStamp() en stampMs(); de exporter had de les gemist.
EXPORT-FORMAT.md schreef altijd al "ISO 8601, UTC" voor.

Changed files:
src/config/database.js

  • index idx_ap_timeline_url op (slug, url)
  • zet() kreeg een optionele wanneer-parameter voor een WHEN op de trigger
  • de drie ap_interactions-triggers filteren nu op kind='reply' (OLD bij delete)

src/services/ArchiveExportService.js

  • toISO leest SQL-notatie expliciet als UTC

New file:
test/feed-state-triggers.test.js

  • like en boost wekken niets, reply wel, verwijderde reply ook
  • er staat nooit een rij op de lege sleutel
  • en de permalink-lookup gebruikt de index, via EXPLAIN QUERY PLAN

remarks: tegenproef gedaan op alle drie. Zonder de database-fixes 4 van 6 rood; zonder
de exporter-fix valt de archief-test om in Europe/Amsterdam en niet in UTC. Volledige
suite 587 groen in beide tijdzones. De vierde vondst, de guardian-controle op
hulpmarkeringen, zit hier NIET in: die vraagt de guardian-set van de ward federatief
op te halen via shaer:guardians en dat is ontwerpwerk, geen mechanische fix.

-robo
Co-Authored-By: Claude Opus 4.8 <noreply@…>

Files:
1 added
2 edited

Legend:

Unmodified
Added
Removed
  • src/config/database.js

    r952baf3 rba76bf5  
    499499    );
    500500    CREATE INDEX IF NOT EXISTS idx_ap_timeline_slug ON ap_timeline(slug, published);
     501    -- canonicalReactionUri herleidt een permalink naar het object-id door op (slug, url)
     502    -- te zoeken. Zonder deze index viel dat terug op idx_ap_timeline_slug, dus een scan
     503    -- van elke rij van die slug. Dat gebeurt PER REACTIE in getInteractions, en de
     504    -- reactie-migratie erft het in haar re-key-join, die synchroon vóór listen draait:
     505    -- de opstartkosten waren reacties maal tijdlijnrijen.
     506    CREATE INDEX IF NOT EXISTS idx_ap_timeline_url ON ap_timeline(slug, url);
    501507    CREATE TABLE IF NOT EXISTS ap_blocks (
    502508      id INTEGER PRIMARY KEY AUTOINCREMENT,
     
    850856    }
    851857    // slug + object_uri verschillen per bron; de rest is voor alle vier gelijk.
    852     const zet = (naam, gebeurtenis, tabel, slug, uri, kind, extra = '') => `
     858    const zet = (naam, gebeurtenis, tabel, slug, uri, kind, extra = '', wanneer = '') => `
    853859      DROP TRIGGER IF EXISTS ${naam};
    854       CREATE TRIGGER ${naam} AFTER ${gebeurtenis} ON ${tabel} BEGIN
     860      CREATE TRIGGER ${naam} AFTER ${gebeurtenis} ON ${tabel}${wanneer ? ` WHEN ${wanneer}` : ''} BEGIN
    855861        UPDATE ap_feed_rev SET n = n + 1;
    856862        INSERT INTO ap_feed_state (slug, object_uri, rev, kind)
     
    874880      // ap_interactions draagt geen slug: die hangt aan de POST. Vandaar de join,
    875881      // en vandaar dat deze drie niet in de gewone vorm passen.
    876       zet('trg_feed_ia_ins', 'INSERT', 'ap_interactions', '', '', '', `${joinPosts('NEW.object_uri', 'new')} WHERE p.id = NEW.post_id`),
    877       zet('trg_feed_ia_upd', 'UPDATE OF content, media_json, quote_json, embed_json', 'ap_interactions', '', '', '', `${joinPosts('NEW.object_uri', 'updated')} WHERE p.id = NEW.post_id`),
    878       zet('trg_feed_ia_del', 'DELETE', 'ap_interactions', '', '', '', `${joinPosts('OLD.object_uri', 'deleted')} WHERE p.id = OLD.post_id`),
     882      //
     883      // De WHEN op kind='reply' is nodig omdat deze tabel ook likes en announces
     884      // draagt, en die schrijven object_uri = '' (zie recordInteraction). Zonder de
     885      // WHEN bumpte elke inkomende like de rev, werd elke wachter gewekt en kreeg
     886      // die de hele collectie opnieuw terwijl er niets aan veranderd was: precies de
     887      // kosten die de 304 moest wegnemen. Bovendien belandde er dan een rij op de
     888      // lege string in ap_feed_state, die feedChangesSince vervolgens uitdeelt.
     889      // De oude cursor filterde hier wel op kind; bij ap_timeline is dit ook gedaan
     890      // (de UPDATE OF sluit liked/boosted uit) en één tabel verder vergeten.
     891      zet('trg_feed_ia_ins', 'INSERT', 'ap_interactions', '', '', '', `${joinPosts('NEW.object_uri', 'new')} WHERE p.id = NEW.post_id`, "NEW.kind = 'reply'"),
     892      zet('trg_feed_ia_upd', 'UPDATE OF content, media_json, quote_json, embed_json', 'ap_interactions', '', '', '', `${joinPosts('NEW.object_uri', 'updated')} WHERE p.id = NEW.post_id`, "NEW.kind = 'reply'"),
     893      zet('trg_feed_ia_del', 'DELETE', 'ap_interactions', '', '', '', `${joinPosts('OLD.object_uri', 'deleted')} WHERE p.id = OLD.post_id`, "OLD.kind = 'reply'"),
    879894    ].join('\n'));
    880895  } catch (e) {
  • src/services/ArchiveExportService.js

    r952baf3 rba76bf5  
    3838
    3939const sha256 = (buf) => crypto.createHash('sha256').update(buf).digest('hex');
    40 const toISO = (d) => { const t = Date.parse(d); return isNaN(t) ? null : new Date(t).toISOString(); };
     40// SQLite's CURRENT_TIMESTAMP schrijft 'YYYY-MM-DD HH:MM:SS' in UTC, zonder marker.
     41// Kale Date.parse leest dat als LOKALE tijd, dus op een server op UTC+2 ging er twee
     42// uur van elke stempel af voordat hij het archief in ging. Die verschuiving wordt bij
     43// het exporteren ingebakken en valt niet weg bij het importeren: exporteer je in
     44// Amsterdam, dan is die post overal permanent twee uur te vroeg, en in zomer- en
     45// wintertijd verschillend. Het raakte de stempels die de database zelf zet (concepten,
     46// ingeplande posts, gearchiveerde antwoorden), niet die uit de editor, dus de schade
     47// was stil en gedeeltelijk. EXPORT-FORMAT.md schreef altijd al "ISO 8601, UTC" voor.
     48// Zelfde regel als isoStamp() in ActivityPubService en stampMs() in guardianship/offers.
     49const SQL_STAMP = /^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}(\.\d+)?$/;
     50const toISO = (d) => {
     51  const s = String(d == null ? '' : d);
     52  const t = Date.parse(SQL_STAMP.test(s) ? `${s.replace(' ', 'T')}Z` : s);
     53  return isNaN(t) ? null : new Date(t).toISOString();
     54};
    4155
    4256const MIME_BY_EXT = {
Note: See TracChangeset for help on using the changeset viewer.