source: Klonkt/scripts/backfill-reactions.mjs@ 7842ca1

main
Last change on this file since 7842ca1 was 7842ca1, checked in by Claude (agent) <aiclaude@…>, 5 weeks ago

Reactie-migratie draait bij boot, inclusief hersleutelen (shaer-9e9)

Een upgrade naar de fase-2-code was tot nu toe onveilig. Het schema migreert
vanzelf (ensureColumn bij boot), maar voor data was er niets: de backfill was een
handmatig script. Bij een vloot die met klonkt-update in een stap naar nieuwe code
gaat wordt dat vergeten, en het falen is stil -- een reactie die niemand meer
ziet geeft geen fout, en de gebruiker klikt gewoon opnieuw, met een tweede Like
de fediverse in als gevolg.

migrateReactions() draait nu bij boot, één keer per REACTIONS_MIGRATION_VERSION,
op dezelfde manier als selfHealTimeline. Twee stappen:

HERSLEUTELEN De oude interact-route bewaarde de URI waarmee je binnenkwam, en

de bookmarklet geeft window.location.href door -- dus de
permalink. Sinds canonicalReactionUri op de object-URI gezocht
wordt, zouden die rijen wees zijn. Dit gat zat NIET in mijn
eerste backfill-script; dat vulde alleen aan vanuit de kolommen
en zag deze rijen niet. Op dev viel het niet op omdat daar alles
al canoniek stond.

AANVULLEN Alles wat op oude code via de Krant is gegeven staat alleen in

ap_timeline.liked/boosted.

Volgorde binnen het hersleutelen: eerst de canonieke variant erbij, dan de
permalink weg. Een onderbreking levert dan hooguit een dubbele rij op en nooit
een verdwenen reactie. Bij hersleutelen reist created_at mee; bij aanvullen
bestaat die informatie niet en wordt het de migratiedatum.

Mislukt de migratie, dan wordt de versievlag niet gezet en probeert de volgende
boot het opnieuw. De kolommen blijven staan, dus de oude waarheid is intussen
niet weg.

Het losse script is nu een schil om diezelfde functie, met --dry-run en --force.
Twee implementaties van een migratie lopen uiteen, en dan repareert de ene wat de
andere niet ziet.

Droogloop tegen de dev-database: 0 te hersleutelen, 0 aan te vullen -- die is al
schoon, wat meteen de idempotentie bevestigt. Suite 438/438.

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

  • Property mode set to 100644
File size: 4.0 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//
28// Wat het NIET kan: bij AANVULLEN de oorspronkelijke reactiedatum herstellen.
29// Wanneer je reageerde is nergens vastgelegd, dus die rijen krijgen de datum van
30// nu. Bij hersleutelen blijft de datum wel behouden.
31
32import db from '../src/config/database.js';
33import { migrateReactions } from '../src/services/ActivityPubService.js';
34
35const dryRun = process.argv.includes('--dry-run');
36const force = process.argv.includes('--force');
37
38const meet = () => ({
39 tussentabel: db.prepare('SELECT COUNT(*) AS n FROM ap_my_reactions').get().n,
40 liked: db.prepare('SELECT COUNT(*) AS n FROM ap_timeline WHERE liked = 1').get().n,
41 boosted: db.prepare('SELECT COUNT(*) AS n FROM ap_timeline WHERE boosted = 1').get().n,
42 scheef: db.prepare(`
43 SELECT COUNT(*) AS n FROM ap_timeline t
44 WHERE (t.liked = 1 OR t.boosted = 1)
45 AND NOT EXISTS (SELECT 1 FROM ap_my_reactions r
46 WHERE r.site_slug = t.slug AND r.target_uri = t.id)`).get().n,
47 wees: db.prepare(`
48 SELECT COUNT(*) AS n FROM ap_my_reactions r
49 WHERE NOT EXISTS (SELECT 1 FROM ap_timeline t
50 WHERE t.slug = r.site_slug AND t.id = r.target_uri)`).get().n,
51});
52
53const voor = meet();
54console.log('vooraf :', JSON.stringify(voor));
55
56const uit = migrateReactions({ dryRun, force: force || dryRun });
57if (uit.overgeslagen) {
58 console.log('\novergeslagen: de versievlag staat al. Gebruik --force om toch te draaien.');
59 process.exit(0);
60}
61if (dryRun) {
62 console.log(`\n--dry-run: zou ${uit.hersleuteld} rij(en) hersleutelen en ${uit.aangevuld} aanvullen. Niets geschreven.`);
63 process.exit(0);
64}
65
66const na = meet();
67console.log('hersleuteld:', uit.hersleuteld, ' aangevuld:', uit.aangevuld);
68console.log('achteraf :', JSON.stringify(na));
69
70// De twee controles die tellen. Blijft er een vlag zonder tegenhanger, dan is de
71// tussentabel niet compleet en tonen reacties als niet-gegeven. Blijft er een
72// tussentabel-rij zonder tijdlijnrij, dan is die op iets buiten je tijdlijn
73// gericht (legitiem) OF nog op een permalink (niet legitiem) -- vandaar de
74// waarschuwing in plaats van een fout.
75if (na.scheef !== 0) {
76 console.error(`\nFOUT: nog ${na.scheef} rij(en) met een vlag zonder tegenhanger.`);
77 process.exit(1);
78}
79if (na.wees > voor.wees) {
80 console.error('\nFOUT: er zijn tussentabel-rijen bijgekomen die nergens op slaan.');
81 process.exit(1);
82}
83if (na.wees) {
84 console.warn(`\nLET OP: ${na.wees} tussentabel-rij(en) zonder tijdlijnrij. Dat mag (een reactie op iets\n`
85 + 'buiten je tijdlijn), maar controleer of er geen permalinks tussen zitten van een post die je\n'
86 + 'wel kent -- die zouden hersleuteld moeten zijn.');
87}
88console.log('\nOK: elke vlag heeft een tegenhanger in de tussentabel.');
Note: See TracBrowser for help on using the repository browser.