Changeset 6fab3f3 in Klonkt


Ignore:
Timestamp:
08/08/2026 10:42:20 PM (4 weeks ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
cae7bfc
Parents:
060d4a5
git-author:
Robin <roboburr@…> (08/08/2026 10:42:18 PM)
git-committer:
roboburr <roboburr@…> (08/08/2026 10:42:20 PM)
Message:

Een lange poll voor alles wat de Guardian te verwerken krijgt (Barts opdracht, 9-8)

Het paneel tikte elke 45 seconden, ongeacht of er iets gebeurd was. Nu hangt er
een verzoek open tot er iets IS. Dat is niet alleen zuiniger -- het is vooral
sneller: een hulproep stond eerst tot drie kwart minuut te wachten op de klok.

WAKKER OP ALLES, en dat is de kern van de opdracht. De guardianship-module zendt
veertien soorten gebeurtenissen uit; die gingen tot nu toe alleen naar push, en
push kiest bewust een handvol. De tien soorten zonder pushtekst -- een stem op
een lapse, een uitkomst, een review -- zag je pas bij de volgende tik. Wie maak
je WAKKER en wat moet een openstaand scherm WETEN zijn twee verschillende
vragen, en ze hadden hetzelfde antwoord.

Naast die bus wekt ook de tijdlijn (onNews): de berichten van je wards staan in
ditzelfde scherm. En de twee paden die niet via notify() lopen -- een
hulpmarkering van een mede-guardian, en een poortverzoek van een kind -- wekken
nu zelf.

EERST KIJKEN, DAN WACHTEN. Veranderde er iets tussen het vorige antwoord en dit
verzoek, dan is de merksteen al anders en gaat het antwoord meteen de deur uit.
Zou je eerst gaan wachten, dan blijft nieuws dat net in dat gaatje viel 25
seconden liggen -- en bij een hulpvraag is dat de verkeerde vertraging.

Het tikje blijft als vangnet, maar op vijf minuten in plaats van 45 seconden:
valt er een wekker weg (een pad dat niet wekt, een herstart midden in een
verzoek), dan mag het scherm niet voorgoed stilstaan. Met de ETag kost zo'n tik
meestal een lege 304.

EEN NAAD DIE IK EERST NIET KON RAKEN. Het wekken zat verstopt in de deps-literal,
en de mutatie die het weghaalde bleef groen -- mijn toetsen dekten alleen de bus.
onGuardianshipEvent is nu een functie met een naam, precies zoals
guardianEventPush dat al was, en de mutatie is nu rood. Een lange poll die niet
wekt is niet te onderscheiden van een trage server, en dat is het soort fout dat
niemand meldt.

Tien toetsen. Suite 770/770.

Files:
1 added
3 edited

Legend:

Unmodified
Added
Removed
  • src/assets/js/guardian.js

    r060d4a5 r6fab3f3  
    967967  }
    968968
     969  // ── De lange poll (Barts opdracht, 9-8) ──────────────────────────────
     970  //
     971  // Het paneel tikte elke 45 seconden, ongeacht of er iets gebeurd was. Nu hangt
     972  // er een verzoek open tot er iets is dat je moet verwerken -- een aanbod, een
     973  // volgverzoek, een gate-voorstel, een hulpvraag. Dat is niet alleen zuiniger,
     974  // het is ook SNELLER: een hulproep stond eerst tot drie kwartier van een
     975  // minuut op het scherm te wachten.
     976  //
     977  // 304 = stilte, en dan meteen opnieuw wachten. Alleen bij 200 wordt er iets
     978  // opnieuw getekend.
     979  var pollBezig = false;
     980  function longPoll() {
     981    if (pollBezig || document.hidden) return;
     982    pollBezig = true;
     983    fetch('/guardian/api/state?wait=25&site=' + encodeURIComponent(S.site))
     984      .then(function (r) {
     985        pollBezig = false;
     986        if (r.status === 304) return null;          // stilte
     987        return r.json().then(function (s) {
     988          if (s && !s.error) { S = s; T = s.strings || T; renderAll(); }
     989          return loadFeed().then(loadFollowReqs);
     990        });
     991      })
     992      .then(function () { setTimeout(longPoll, 200); })
     993      .catch(function () {
     994        // Netwerk weg of server herstart: niet meteen opnieuw beuken.
     995        pollBezig = false;
     996        setTimeout(longPoll, 5000);
     997      });
     998  }
     999
    9691000  // ── 2. Adopt ───────────────────────────────────────────────────────────
    9701001  var form = document.getElementById('adopt-form');
     
    10721103  // kijkt: dat waren verzoeken voor een tabblad op de achtergrond. Bij terugkomen
    10731104  // meteen een keer, want dan is de kans op nieuws het grootst.
    1074   setInterval(function () { if (!document.hidden) refresh(); }, 45000);
    1075   document.addEventListener('visibilitychange', function () { if (!document.hidden) refresh(); });
     1105  // De lange poll doet het werk. Het tikje blijft als vangnet, maar veel trager:
     1106  // valt er een wekker weg (een pad dat niet wekt, een herstart midden in een
     1107  // verzoek), dan mag het scherm niet voorgoed stilstaan. Vijf minuten in plaats
     1108  // van vijfenveertig seconden, en met de ETag kost zo'n tik meestal een lege 304.
     1109  longPoll();
     1110  setInterval(function () { if (!document.hidden) refresh(); }, 300000);
     1111  document.addEventListener('visibilitychange', function () {
     1112    if (document.hidden) return;
     1113    refresh();
     1114    longPoll();       // bij terugkomst weer een wachter openzetten
     1115  });
    10761116  } catch (e) {
    10771117    fatal((e && e.message) || String(e));
  • src/routes/guardian.js

    r060d4a5 r6fab3f3  
    182182
    183183// ── JSON state for refreshes ─────────────────────────────────────────────
    184 router.get('/api/state', requireAuth, (req, res) => {
    185   const site = siteForUser(req);
    186   if (!site) return res.status(404).json({ error: 'no_site' });
    187   // Het paneel tikt elke 45 seconden, of er nu iets gebeurd is of niet. Met een
    188   // ETag kost stilte een lege 304 in plaats van het hele paneel (Barts punt, 9-8).
    189   return AP.sendMaybe304(req, res, dashboardState(site, resolveLang(req)), { contentType: 'application/json' });
     184/**
     185 * De staat van het paneel, desgewenst als LANGE POLL (Barts opdracht, 9-8).
     186 *
     187 * Zonder `wait` gedraagt de route zich exact zoals altijd. Met `wait` blijft het
     188 * antwoord hangen tot er iets gebeurt dat de guardian moet verwerken, of tot de
     189 * tijd om is -- dan een lege 304.
     190 *
     191 * EERST KIJKEN, DAN WACHTEN. Veranderde er iets tussen het vorige antwoord en
     192 * dit verzoek, dan is de merksteen nu al anders en gaat het antwoord METEEN de
     193 * deur uit. Zou je eerst gaan wachten, dan blijft nieuws dat net in dat gaatje
     194 * viel vijfentwintig seconden liggen -- en juist bij een hulpvraag is dat de
     195 * verkeerde vertraging.
     196 *
     197 * WAKKER OP ALLES. De guardianship-module zendt veertien soorten gebeurtenissen
     198 * uit en die wekken allemaal (wakeGuardian); daarnaast wekt de tijdlijn (onNews),
     199 * want de berichten van je wards staan in ditzelfde scherm.
     200 */
     201router.get('/api/state', requireAuth, async (req, res) => {
     202  const site = siteForUser(req);
     203  if (!site) return res.status(404).json({ error: 'no_site' });
     204  const stuur = () => AP.sendMaybe304(req, res, dashboardState(site, resolveLang(req)), { contentType: 'application/json' });
     205
     206  const wachtS = Math.min(Math.max(parseInt(req.query.wait, 10) || 0, 0), 50);
     207  const merk = req.headers['if-none-match'];
     208  if (!wachtS || !merk) return stuur();
     209
     210  // Is er nu al iets anders? Dan niet wachten.
     211  const nu = AP.etagFor(JSON.stringify(dashboardState(site, resolveLang(req))));
     212  if (nu !== merk) return stuur();
     213
     214  await new Promise((klaar) => {
     215    let af = false;
     216    const eind = () => { if (af) return; af = true; clearTimeout(t); offG(); offN(); klaar(); };
     217    const offG = AP.onGuardian(site.slug, eind);
     218    const offN = AP.onNews(site.slug, eind);
     219    const t = setTimeout(eind, wachtS * 1000);
     220    // Hing de client op, dan houdt niemand dit antwoord meer vast.
     221    res.on('close', eind);
     222  });
     223  if (res.writableEnded) return undefined;
     224  return stuur();
    190225});
    191226
  • src/services/ActivityPubService.js

    r060d4a5 r6fab3f3  
    18601860  return () => { set.delete(cb); if (!set.size) _newsWaiters.delete(slug); };
    18611861}
     1862/**
     1863 * Wachters op het Guardian-paneel (Barts opdracht, 9-8).
     1864 *
     1865 * APART VAN onNews, en dat is met opzet. `news` gaat over de tijdlijn; dit gaat
     1866 * over alles wat een guardian te VERWERKEN krijgt -- een aanbod, een
     1867 * volgverzoek, een gate-voorstel, een hulpvraag, een lapse. De guardianship-
     1868 * module zendt daar al veertien soorten voor uit; die gingen alleen naar push,
     1869 * en push kiest bewust maar een handvol. Het paneel moet ze allemaal weten.
     1870 *
     1871 * Een wachter wordt EEN keer gewekt en daarna vergeten: het antwoord dat volgt
     1872 * is de nieuwe waarheid, en de client komt terug met een nieuwe wachter.
     1873 */
     1874const _guardWaiters = new Map();   // slug -> Set<cb>
     1875export function onGuardian(slug, cb) {
     1876  let set = _guardWaiters.get(slug);
     1877  if (!set) { set = new Set(); _guardWaiters.set(slug, set); }
     1878  set.add(cb);
     1879  return () => { set.delete(cb); if (!set.size) _guardWaiters.delete(slug); };
     1880}
     1881export function wakeGuardian(slug) {
     1882  const set = _guardWaiters.get(slug);
     1883  if (!set || !set.size) return;
     1884  const cbs = [...set];
     1885  set.clear();
     1886  _guardWaiters.delete(slug);
     1887  for (const cb of cbs) { try { cb(); } catch { /* een wachter mag de rest nooit breken */ } }
     1888}
     1889
    18621890export function wakeNews(slug) {
    18631891  const set = _newsWaiters.get(slug);
     
    23872415        const ai = actorInfo(await resolveActor(actorUri).catch(() => null), actorUri);
    23882416        Guardianship.help.record(mark.noteUri, actorUri, mark.kind, ai && ai.handle);
     2417        wakeGuardian(slug);   // een mede-guardian pakte iets op: het paneel hoort het meteen
    23892418        console.log('[AP] help', mark.kind, actorUri, '→', mark.noteUri);
    23902419      }
     
    24292458              if (!mijn) { console.warn('[AP] gate request from someone who is not our ward, ignored:', actorUri, '→', slug); continue; }
    24302459              Guardianship.gatereq.record(slug, actorUri, req.feature, o.id);
     2460              wakeGuardian(slug);   // het kind vroeg om een poort
    24312461              console.log('[AP] gate request', req.feature, actorUri, '→', slug);
    24322462            }
     
    59706000  // De labels hangen aan dezelfde sleutels als het Guardian-paneel, zodat een
    59716001  // melding en het scherm waar hij heen wijst hetzelfde woord gebruiken.
    5972   onEvent: (slug, ev) => {
    5973     const p = guardianEventPush(slug, ev);
    5974     if (p) pushEvent(slug, p);
    5975   },
     6002  onEvent: (slug, ev) => onGuardianshipEvent(slug, ev),
    59766003});
     6004
     6005/**
     6006 * Wat er gebeurt als de guardianship-module iets uitzendt.
     6007 *
     6008 * TWEE VERSCHILLENDE VRAGEN, en ze horen niet dezelfde te zijn: wie maak je
     6009 * WAKKER (push kiest bewust een handvol soorten), en wat moet een scherm dat
     6010 * openstaat WETEN (alles). Het paneel werd daarom voorheen niet gewekt door de
     6011 * tien soorten zonder pushtekst -- die zag je pas bij de volgende tik.
     6012 *
     6013 * Apart en met een naam, zodat een toets erbij kan. Verstopt in de deps-literal
     6014 * was hij onbereikbaar, en een mutatie die het wekken weghaalde bleef groen.
     6015 */
     6016export function onGuardianshipEvent(slug, ev) {
     6017  wakeGuardian(slug);
     6018  const p = guardianEventPush(slug, ev);
     6019  if (p) pushEvent(slug, p);
     6020  return p;
     6021}
    59776022
    59786023/**
     
    60666111  getNotifications, listBlocks, isBlockedAny, blockTarget, unblock,
    60676112  deliverWithRetry, enqueueDelivery, processDeliveryQueue, startDeliveryWorker,
    6068   sendMaybe304, etagFor, proposeGate, getReplyUris, getThread, filterThreadToCircle, gateAttachments, stripEmojiTags, markNotificationsSeen, countUnseenNotifications, hasPlayableAudio,
     6113  sendMaybe304, etagFor, onGuardian, wakeGuardian, onGuardianshipEvent, proposeGate, getReplyUris, getThread, filterThreadToCircle, gateAttachments, stripEmojiTags, markNotificationsSeen, countUnseenNotifications, hasPlayableAudio,
    60696114  linkifyBody, bakePostContent, bakePostContentWithMentions, listFollowers, removeFollower, listConnections,
    60706115  noteVisibility, belongsInTimeline, playerUrlFor, isRejectedObject, rejectInteraction, interactionReportTarget,
Note: See TracChangeset for help on using the changeset viewer.