Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/config/database.js

    rd14bf1c r32a4ffb  
    771771  ensureColumn('ap_followers', 'handle', 'TEXT');  // @user@host
    772772  ensureColumn('ap_followers', 'icon', 'TEXT');    // avatar URL
    773   feedStateTriggers();
    774 }
    775 
    776 /**
    777  * Wat er met een tijdlijn gebeurd is, op één plek (shaer-n05).
    778  *
    779  * De inbox-lezing voegt vier bronnen samen. De vraag "is er iets veranderd" werd
    780  * eerst beantwoord met MAX(rowid) over die vier -- een TOEVALLIGE eigenschap van
    781  * de tabellen, geen feit dat ergens is opgeschreven. Dat gaf precies de gebreken
    782  * die je van zo'n afleiding verwacht: bewerkingen en verwijderingen bewogen hem
    783  * niet, en hij kon achteruit lopen. Dezelfde fout als reacties uitlezen uit
    784  * ap_timeline.liked (shaer-9e9).
    785  *
    786  * Nu één rij per bericht per tijdlijn, met een oplopende `rev` en `kind`. Dat
    787  * beantwoordt drie vragen die anders drie eigen oplossingen zouden krijgen:
    788  * is er iets veranderd sinds N, wát is er veranderd, en is dit bericht bewerkt.
    789  *
    790  * Bijgehouden door TRIGGERS en niet door de aanroepende code, om dezelfde reden
    791  * dat er geen gebeurtenis-emitter is: een trigger zit in de database, dus geen
    792  * enkel codepad kan hem vergeten. De prijs is onzichtbare logica -- wie alleen de
    793  * JavaScript leest ziet niet waarom deze tabel vult. Vandaar dat ze hier staan,
    794  * bij de tabel, en niet verspreid.
    795  *
    796  * Let op de `UPDATE OF`-kolomlijsten: die zijn niet decoratief. Een like schrijft
    797  * ap_timeline.liked en een 🔁 schrijft .boosted; zonder die afbakening zou je
    798  * eigen like het bericht als BEWERKT merken en elke wachtende client wekken.
    799  */
    800 function feedStateTriggers() {
    801   try {
    802     db.exec(`
    803       CREATE TABLE IF NOT EXISTS ap_feed_state (
    804         slug TEXT NOT NULL,
    805         object_uri TEXT NOT NULL,
    806         rev INTEGER NOT NULL,
    807         kind TEXT NOT NULL,             -- new | updated | deleted
    808         at DATETIME DEFAULT CURRENT_TIMESTAMP,
    809         PRIMARY KEY (slug, object_uri)
    810       );
    811       CREATE INDEX IF NOT EXISTS idx_ap_feed_state_rev ON ap_feed_state(slug, rev);
    812       -- Eén doorlopende teller voor de hele instance. Bewust niet MAX(rev) uit de
    813       -- tabel zelf: verdwijnt de hoogste rij, dan zou die teruglopen en denkt een
    814       -- client dat er niets gebeurd is.
    815       CREATE TABLE IF NOT EXISTS ap_feed_rev (n INTEGER NOT NULL);
    816     `);
    817     if (!db.prepare('SELECT COUNT(*) AS n FROM ap_feed_rev').get().n) {
    818       db.prepare('INSERT INTO ap_feed_rev (n) VALUES (0)').run();
    819     }
    820     // slug + object_uri verschillen per bron; de rest is voor alle vier gelijk.
    821     const zet = (naam, gebeurtenis, tabel, slug, uri, kind, extra = '') => `
    822       DROP TRIGGER IF EXISTS ${naam};
    823       CREATE TRIGGER ${naam} AFTER ${gebeurtenis} ON ${tabel} BEGIN
    824         UPDATE ap_feed_rev SET n = n + 1;
    825         INSERT INTO ap_feed_state (slug, object_uri, rev, kind)
    826           ${extra || `VALUES (${slug}, ${uri}, (SELECT n FROM ap_feed_rev), '${kind}')`}
    827           ON CONFLICT(slug, object_uri) DO UPDATE
    828             SET rev = excluded.rev, kind = excluded.kind, at = CURRENT_TIMESTAMP;
    829       END;`;
    830     const joinPosts = (uri, kind) => `
    831           SELECT s.slug, ${uri}, (SELECT n FROM ap_feed_rev), '${kind}'
    832             FROM posts p JOIN sites s ON s.id = p.site_id`;
    833     db.exec([
    834       zet('trg_feed_tl_ins', 'INSERT', 'ap_timeline', 'NEW.slug', 'NEW.id', 'new'),
    835       zet('trg_feed_tl_upd', 'UPDATE OF content, media_json, nsfw, cw, url, poll_json, quote_json, embed_json', 'ap_timeline', 'NEW.slug', 'NEW.id', 'updated'),
    836       zet('trg_feed_tl_del', 'DELETE', 'ap_timeline', 'OLD.slug', 'OLD.id', 'deleted'),
    837       zet('trg_feed_mn_ins', 'INSERT', 'ap_mentions', 'NEW.slug', 'NEW.object_uri', 'new'),
    838       zet('trg_feed_mn_upd', 'UPDATE OF content, media_json, quote_json, embed_json', 'ap_mentions', 'NEW.slug', 'NEW.object_uri', 'updated'),
    839       zet('trg_feed_mn_del', 'DELETE', 'ap_mentions', 'OLD.slug', 'OLD.object_uri', 'deleted'),
    840       zet('trg_feed_ob_ins', 'INSERT', 'ap_outbox', 'NEW.site_slug', 'NEW.id', 'new'),
    841       zet('trg_feed_ob_upd', 'UPDATE OF content, attachments', 'ap_outbox', 'NEW.site_slug', 'NEW.id', 'updated'),
    842       zet('trg_feed_ob_del', 'DELETE', 'ap_outbox', 'OLD.site_slug', 'OLD.id', 'deleted'),
    843       // ap_interactions draagt geen slug: die hangt aan de POST. Vandaar de join,
    844       // en vandaar dat deze drie niet in de gewone vorm passen.
    845       zet('trg_feed_ia_ins', 'INSERT', 'ap_interactions', '', '', '', `${joinPosts('NEW.object_uri', 'new')} WHERE p.id = NEW.post_id`),
    846       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`),
    847       zet('trg_feed_ia_del', 'DELETE', 'ap_interactions', '', '', '', `${joinPosts('OLD.object_uri', 'deleted')} WHERE p.id = OLD.post_id`),
    848     ].join('\n'));
    849   } catch (e) {
    850     // Niet fataal: zonder deze tabel valt het wachten terug op "altijd de tijd
    851     // volmaken", en dat is traag maar niet stuk.
    852     console.error('❌ feed-state triggers:', e.message);
    853   }
    854773}
    855774
Note: See TracChangeset for help on using the changeset viewer.