source: Klonkt/scripts/backfill-reactions.mjs@ 279ca0b

main
Last change on this file since 279ca0b was 279ca0b, checked in by roboburr <roboburr@…>, 5 weeks ago

De derde reactiebron ook naar ap_my_reactions (shaer-ipb)

ap_interactions.acted_like/acted_boost -- wat JIJ deed met een reactie onder je
eigen post -- was de derde bron van 'heb ik hierop gereageerd'. shaer-9e9 trok de
andere twee samen en liet deze staan omdat hij op het interactie-rij-id
gesleuteld was en de tussentabel op de object-URI.

De betekeniskeuze die de bead eerst wilde beantwoorden: dezelfde note, geliket
als comment en als post, wordt EEN feit. Er gaat immers ook maar een Like de
fediverse in; twee knoppen die los van elkaar aan konden staan voor hetzelfde
object was eerder een bug dan een feature. Mechanisch blijkt dat al veilig:
markLiked en markBoosted zijn kale UPDATEs op ap_timeline die nul rijen raken als
de URI daar niet staat, en upsertBoostedNote draait alleen met opts.note mee.

Volgorde zoals bij shaer-9e9, en om dezelfde reden -- een leespad dat naar de
tussentabel wijst terwijl de rijen nog in de kolommen staan laat reacties STIL
verdwijnen:

schrijven fedi-react roept setReaction aan; acted_* loopt nog als afgeleide

mee, hetzelfde vangnet dat ap_timeline.liked na shaer-9e9 is. Ook de
toggle leest nu uit de tussentabel: las de knop de ene bron en de
richting de andere, dan stuurt een divergentie een Undo voor iets dat
nooit verstuurd is.

migreren REACTIONS_MIGRATION_VERSION 1 -> 2, derde stap erbij. Een bump laat

alle stappen opnieuw lopen; ze zijn alle drie idempotent. Rijen
zonder object_uri vallen af, en dat verliest per constructie niets:
fedi-react eist een object_uri voordat acted_* gezet kan worden.

lezen getInteractions doet een batch-lookup langs dezelfde sleutel als de

schrijfkant, dus geen N+1 in een drukke thread.

Het backfill-script zou anders vals alarm slaan: een reactie op een COMMENT hoort
geen tijdlijnrij te hebben, dus die telt niet meer als wees. Er is een controle
bij voor acted_* zonder tegenhanger.

Gemeten op een kopie van dev: scheefActed 1 -> 0, tussentabel 19 -> 20, wees
onveranderd. Suite 472/472. Gecontroleerd dat de tests bijten: laat het leespad
weer uit de kolom lezen en vier van de zes vallen om.

De karakteriseringstest zette de versievlag hard op 1 en zou bij elke bump
omvallen zonder dat er iets stuk is; die toetst nu het gedrag (ouder = opnieuw,
gelijk of hoger = overslaan) in plaats van het getal.

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

  • Property mode set to 100644
File size: 5.1 KB
Line 
1#!/usr/bin/env node
2//
3// Reactie-migratie handmatig draaien (shaer-9e9).
4//
5// Normaal hoef je dit NIET: migrateReactions() draait bij boot, één keer per
6// REACTIONS_MIGRATION_VERSION-bump, net als de self-heal. Dit script is er om
7// vooraf te kijken wat er zou gebeuren, of om het gericht op één instance te
8// forceren.
9//
10// node scripts/backfill-reactions.mjs --dry-run
11// node scripts/backfill-reactions.mjs # respecteert de versievlag
12// node scripts/backfill-reactions.mjs --force # ook als de vlag al staat
13//
14// Bewust een schil om dezelfde functie die bij boot draait: twee implementaties
15// van een migratie lopen uiteen, en dan repareert de ene wat de andere niet ziet.
16//
17// Wat het doet, en waarom allebei nodig is:
18//
19// HERSLEUTELEN De oude interact-route bewaarde de URI waarmee je binnenkwam,
20// en de bookmarklet geeft de permalink door. Sinds de reacties
21// op de canonieke object-URI gezocht worden, zouden die rijen
22// wees zijn. De created_at reist mee.
23// AANVULLEN Alles wat op oude code via de Krant is gegeven staat alleen in
24// ap_timeline.liked/boosted. Zonder deze stap toont het als
25// niet-gereageerd -- en klikt iemand opnieuw, met een tweede
26// Like de fediverse in als gevolg.
27// UIT REACTIES De derde bron (shaer-ipb): ap_interactions.acted_like/_boost,
28// wat jij deed met een reactie ONDER je eigen post. Zelfde
29// gevolg als hierboven, alleen op een ander oppervlak.
30//
31// Wat het NIET kan: bij AANVULLEN de oorspronkelijke reactiedatum herstellen.
32// Wanneer je reageerde is nergens vastgelegd, dus die rijen krijgen de datum van
33// nu. Bij hersleutelen blijft de datum wel behouden.
34
35import db from '../src/config/database.js';
36import { migrateReactions } from '../src/services/ActivityPubService.js';
37
38const dryRun = process.argv.includes('--dry-run');
39const force = process.argv.includes('--force');
40
41const meet = () => ({
42 tussentabel: db.prepare('SELECT COUNT(*) AS n FROM ap_my_reactions').get().n,
43 liked: db.prepare('SELECT COUNT(*) AS n FROM ap_timeline WHERE liked = 1').get().n,
44 boosted: db.prepare('SELECT COUNT(*) AS n FROM ap_timeline WHERE boosted = 1').get().n,
45 scheef: db.prepare(`
46 SELECT COUNT(*) AS n FROM ap_timeline t
47 WHERE (t.liked = 1 OR t.boosted = 1)
48 AND NOT EXISTS (SELECT 1 FROM ap_my_reactions r
49 WHERE r.site_slug = t.slug AND r.target_uri = t.id)`).get().n,
50 // Een reactie op een COMMENT hoort geen tijdlijnrij te hebben, dus die telt
51 // hier niet als wees -- anders slaat de controle hieronder alarm op precies
52 // wat stap 3 net goed heeft gezet.
53 wees: db.prepare(`
54 SELECT COUNT(*) AS n FROM ap_my_reactions r
55 WHERE NOT EXISTS (SELECT 1 FROM ap_timeline t
56 WHERE t.slug = r.site_slug AND t.id = r.target_uri)
57 AND NOT EXISTS (SELECT 1 FROM ap_interactions i WHERE i.object_uri = r.target_uri)`).get().n,
58 scheefActed: db.prepare(`
59 SELECT COUNT(*) AS n FROM ap_interactions i
60 JOIN posts p ON p.id = i.post_id
61 JOIN sites s ON s.id = p.site_id
62 WHERE (i.acted_like = 1 OR i.acted_boost = 1) AND IFNULL(i.object_uri, '') <> ''
63 AND NOT EXISTS (SELECT 1 FROM ap_my_reactions r
64 WHERE r.site_slug = s.slug AND r.target_uri = i.object_uri)`).get().n,
65});
66
67const voor = meet();
68console.log('vooraf :', JSON.stringify(voor));
69
70const uit = migrateReactions({ dryRun, force: force || dryRun });
71if (uit.overgeslagen) {
72 console.log('\novergeslagen: de versievlag staat al. Gebruik --force om toch te draaien.');
73 process.exit(0);
74}
75if (dryRun) {
76 console.log(`\n--dry-run: zou ${uit.hersleuteld} rij(en) hersleutelen, ${uit.aangevuld} aanvullen`
77 + ` en ${uit.reacties} uit reacties overnemen. Niets geschreven.`);
78 process.exit(0);
79}
80
81const na = meet();
82console.log('hersleuteld:', uit.hersleuteld, ' aangevuld:', uit.aangevuld, ' uit reacties:', uit.reacties);
83console.log('achteraf :', JSON.stringify(na));
84
85// De twee controles die tellen. Blijft er een vlag zonder tegenhanger, dan is de
86// tussentabel niet compleet en tonen reacties als niet-gegeven. Blijft er een
87// tussentabel-rij zonder tijdlijnrij, dan is die op iets buiten je tijdlijn
88// gericht (legitiem) OF nog op een permalink (niet legitiem) -- vandaar de
89// waarschuwing in plaats van een fout.
90if (na.scheef !== 0) {
91 console.error(`\nFOUT: nog ${na.scheef} rij(en) met een vlag zonder tegenhanger.`);
92 process.exit(1);
93}
94if (na.scheefActed !== 0) {
95 console.error(`\nFOUT: nog ${na.scheefActed} reactie(s) onder je eigen posts met acted_* zonder tegenhanger.`);
96 process.exit(1);
97}
98if (na.wees > voor.wees) {
99 console.error('\nFOUT: er zijn tussentabel-rijen bijgekomen die nergens op slaan.');
100 process.exit(1);
101}
102if (na.wees) {
103 console.warn(`\nLET OP: ${na.wees} tussentabel-rij(en) zonder tijdlijnrij. Dat mag (een reactie op iets\n`
104 + 'buiten je tijdlijn), maar controleer of er geen permalinks tussen zitten van een post die je\n'
105 + 'wel kent -- die zouden hersleuteld moeten zijn.');
106}
107console.log('\nOK: elke vlag heeft een tegenhanger in de tussentabel.');
Note: See TracBrowser for help on using the repository browser.