Erreur PostgreSQL : out of shared memory
ERROR: out of shared memory
HINT: You might need to increase max_locks_per_transaction.
Malgré la formulation alarmante, il ne s'agit presque jamais du serveur qui manque de mémoire en général. Quand elle s'accompagne du hint max_locks_per_transaction, elle signifie une chose précise : la table de verrous partagée — la structure de taille fixe où PostgreSQL suit les verrous sur les tables, index et autres objets — est pleine.
Ce que signifie cette erreur
La table de verrous est dimensionnée au démarrage du serveur, avec de la place pour environ max_locks_per_transaction × (max_connections + max_prepared_transactions) verrous d'objet. Deux propriétés expliquent tout ce que vous observerez :
- Malgré son nom,
max_locks_per_transactionn'est pas un plafond par transaction — il dimensionne simplement le pool partagé. Une seule transaction peut sans problème détenir des milliers de verrous tant que le pool a de la place. - Ce qui signifie aussi qu'une transaction gourmande peut vider le pool pour tout le monde : l'erreur peut frapper un spectateur innocent, pas la transaction qui a consommé les verrous.
Chaque table, partition et index touché par une requête a besoin de sa propre entrée de verrou, conservée jusqu'à la fin de la transaction. C'est l'arithmétique qui vous piège : une requête sur une table de 2 000 partitions avec quelques index chacune peut nécessiter plusieurs milliers d'entrées — alors que le dimensionnement par défaut (64 × ~100 connexions) vous en donne environ 6 400 pour tout le serveur.
Causes fréquentes
- Tables partitionnées avec de nombreuses partitions : une requête qui ne peut pas élaguer touche chaque partition et chacun de ses index. De loin le déclencheur le plus courant.
pg_dump/pg_restoresur des bases comportant un très grand nombre d'objets (il verrouille chaque table qu'il exporte dans une seule transaction).- Migrations qui modifient ou suppriment de nombreuses tables dans une seule transaction.
- De nombreuses transactions concurrentes touchant chacune un nombre modéré de relations — le pool est partagé, donc c'est la somme qui compte.
Comment la diagnostiquer
Pendant que la charge de travail en échec s'exécute (ou juste avant qu'elle échoue) :
-- À quel point la table de verrous est pleine :
SELECT count(*) FROM pg_locks;
-- Qui consomme les entrées :
SELECT pid, count(*)
FROM pg_locks
GROUP BY pid
ORDER BY count(*) DESC
LIMIT 10;
Comparez le total à la formule ci-dessus avec vos réglages. Si un pid détient des milliers de verrous, regardez ce qu'il exécute — et si des partitions sont impliquées, comptez-les : SELECT count(*) FROM pg_inherits WHERE inhparent = 'events'::regclass;
Comment la corriger
- Augmentez
max_locks_per_transaction— par ex. de 64 à 256 ou 512 sur des serveurs à schémas fortement partitionnés. Nécessite un redémarrage ; la mémoire partagée supplémentaire est modeste.ALTER SYSTEM SET max_locks_per_transaction = 256; -- puis redémarrage du serveur - Facilitez l'élagage des partitions : assurez-vous que les requêtes filtrent sur la clé de partitionnement avec des valeurs que le planificateur peut voir, afin que seules les partitions pertinentes soient verrouillées.
- Découpez les opérations larges : divisez les migrations et scripts de maintenance en plusieurs transactions au lieu d'une seule gigantesque.
- Reconsidérez le nombre de partitions : les schémas avec plusieurs milliers de minuscules partitions paient cette taxe partout, pas seulement ici.
