La base de Synapse borked

Ecrit le

Il y a quelques jours, je me suis retrouvé avec la base de données de Synapse qui commençait à se casser la figure. C’est-à-dire que, mystérieusement, je ne pouvais plus quitter le salon #matrix:matrix.org à cause de violations d’unicité. Je ne sais pas du tout ce qui s’est passé pour qu’une telle situation se reproduise et je pense que partager mon expérience et ma manière de procéder à l’arrache peut aider.

Manipulation dangereuse

Les manipulations qui ont été effectuées sont dangereuses et peuvent rendre votre base de données Synapse inutilisable.

Ne les reproduisez pas si vous n’avez pas de sauvegarde ou ne savez pas ce que vous êtes en train de faire !

Le problème de départ

J’ai voulu quitter #matrix:matrix.org qui ne m’était plus utile parce que je ne le regardais plus. Le salon n’était pas remis à jour et le serveur ne recevait plus rien. J’ai donc tenté de le quitter. Et là, plus de maillot !

Je redémarre le serveur Synapse et je retente. Tu sais, ça peut marcher sur un malentendu. Je réessaye: une belle erreur avec la même stacktrace.

Si je savais qu’un coup de Synapse janitor pour dégager les salons abandonnés et compacter les messages supprimés allaient créer autant de bordel, je ne l’aurais pas fait.

Les choses ne vont pas en s’améliorant

Soyons clairs: j’ai voulu éviter de devoir faire la manipulation que j’ai finie par faire.

En suivant la doc, j’ai vu que je pouvais essayer par un autre moyen: l’API admin de purge de l’historique des events. Sauf que… Non.

synapse.api.errors.SynapseError: 400: topological_ordering is greater than forward extremeties

Outre que le code HTTP (si c’est un code HTTP) devrait être un qui correspond aux erreurs serveur (500 suffit), bah flûte. Ça marche po™…

Toucher un peu et voir ce que ça donne

Sans réponse depuis quelques temps, j’ai résolu prendre le chemin risqué: faire une chirurgie de la base de données.

J’ai d’abord modifié une ou deux tables à la main pour renommer les clefs problématiques. Je n’avançais pas. Dès que j’ai conclu que je ne vais pas aller plus loin que toucher à deux lignes, j’ai tenté une restauration d’un backup avant chirurgie. Deux siècles plus tard et un changement de config pour pointer au bon endroit, Postgresql lâche des erreurs comme quoi 1) des éléments sont dupliqués et donc qu’il ne peut pas créer la contrainte demandée 2) la clef qui n’a pas pu être créée est demandée dans une requête. Chouette.

Concrètement, cela veut dire que je n’ai pas de moyen de retourner en arrière proprement, à moins qu’il y ai une option que j’ignore.

Le grand saut

Dans le meilleur des cas, j’aurai une possibilité de faire les choses proprement pour la fédération, c’est à dire quitter proprement, dans le pire des cas un salon complètement broken qui aura vocation à rester dans ma liste des salons et l’hanter.

J’ai donc été chercher un bout de SQL issu du script nuke-room-from-db.sh qui ressemble à ça (version modifiée):

DELETE FROM event_forward_extremities WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM event_backward_extremities WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM event_edges WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM room_depth WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM state_forward_extremities WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM events WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM event_json WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM state_events WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM current_state_events WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM room_memberships WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM feedback WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM topics WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
--DELETE FROM room_names WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';

--DELETE FROM rooms WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';

--DELETE FROM room_hosts WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';

--DELETE FROM room_aliases WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';

DELETE FROM state_groups WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM state_groups_state WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM receipts_graph WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM receipts_linearized WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM event_search WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM guest_access WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM history_visibility WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM room_tags WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM room_tags_revisions WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM room_account_data WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM event_push_actions WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM local_invites WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM pusher_throttle WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM event_reports WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM public_room_list_stream WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM stream_ordering_to_exterm WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM event_auth WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';
DELETE FROM appservice_room_list WHERE room_id = '!QtykxKocfZaZOUrTwp:matrix.org';

Certaines lignes ont été commentées pour espérer garder une petite trace du salon en base de données et faire les choses proprement par la suite.

J’ai laissé les requêtes faire leur DELETE pendant la nuit. Certaines sont passées rapidement, d’autres moins, notamment une table où la requête a effacé 60 milions de lignes.

Et tout a l’air de partir sur des roulettes

Le lendemain, j’ai vérifié l’exécution des requêtes via un petit fichier que j’ai créé exprès pour.

Je vide le cache de Riot puis suspense… Le salon a été enlevé de la liste des salons que j’ai rejoint. J’ai tenté de rejoindre le salon. Devinez-quoi ?

ÇA MARCHE !

Avec un grand sourire, j’ai ensuite quitté le salon proprement. Devinez-quoi ?

ÇA MARCHE AUSSI ! \o/

Bon bah super ! J’ai réussi à faire les choses comme il faut même s’il aura fallu faire une chirurgie en base de données. Un petit VACUUM FULL pour compacter le tout et on est reparti sur des roulettes :p