Le mécanisme de sauvegarde intégré de PostgreSQL n'est pas toujours très approprié. Parfois, vous souhaitez mettre l'application dans un état au repos , car elle contient des données externes avec lesquelles vous souhaitez sauvegarder en même temps que vous sauvegardez les données PG. Mais la seule façon de mettre l'application dans un état de repos est de "verrouiller" également la base de données. PG ne dispose pas d'un mécanisme de verrouillage à l'échelle de la base de données ou à l'échelle du cluster. Mettre PG en lecture seule serait un élément de la solution suivante:
Après avoir récupéré des réponses ailleurs sur Internet, j'ai imaginé une solution. Les autres réponses étaient en elles-mêmes incomplètes. Je présente donc une réponse ici dans l'espoir qu'elle profite aux autres.
default_transaction_read_only
De la base de données sur true
.Une fois cela fait, vous (dans ma solution):
CHECKPOINT
(je pense que c'est le plus sûr, mais une pg_xlog_switch()
serait appropriée pour les serveurs à très forte charge)pg_stat_clear_snapshot()
La restauration de l'état de lecture-écriture est pas si simple. Si des connexions en lecture seule existent maintenant, vous devez les interrompre pour que le nouveau statut de lecture-écriture prenne effet. Mais de nouvelles connexions pourraient arriver tout en tuant les existantes. Encore une fois, vous devez
false
Une autre stratégie consiste à modifier les autorisations sur le rôle utilisé par l'application. Cela peut être assez compliqué et moins général.
Par exemple, vous devez révoquer/réaccorder non seulement des tables, mais des séquences, des objets volumineux et probablement le schéma lui-même. De plus, quel est exactement le comportement des connexions existantes lorsque vous modifiez l'accès? Probablement aucun impact, ce qui signifie que vous devez également tuer ces backends. Enfin, supposons que l'application dispose d'un accès en lecture-écriture à la plupart des tables, mais pas à d'autres dans le schéma. Vous devez vous assurer que votre ré-octroi n'inclut pas également ces objets.
Une autre possibilité consiste à VERROUILLER toutes les tables, en interrogeant le catalogue et en effectuant une requête dynamique. Cela semblait périlleux à mes goûts.
Le nom de l'instance de base de données est 'gitlabhq' et le nom d'utilisateur de l'application est 'gitlab'. Remplacez-le par le vôtre:
psql -Upostgres <<'PAUSE_DB'
-- 1. disable new connections
alter database gitlabhq_production with allow_connections = off;
-- 2. Make DB read-only
alter database gitlabhq set default_transaction_read_only = true;
-- 3. Inobtrusively but safely terminate current connections
DO $X$ BEGIN
-- kill open idle connections, try up to 9x. Last time, kill regardless
FOR i IN 1..10 LOOP
PERFORM pg_terminate_backend(pid) from pg_stat_activity where usename = 'gitlab'
and (i >= 10 OR state in ('idle', 'disabled' ));
PERFORM pg_stat_clear_snapshot();
EXIT WHEN NOT EXISTS ( select pid from pg_stat_activity where usename = 'gitlab' );
RAISE NOTICE 'pg backends still open: sleeping 2 seconds';
PERFORM pg_sleep(2);
PERFORM pg_stat_clear_snapshot();
END LOOP;
-- send notice if still open connections
IF EXISTS ( select pid from pg_stat_activity where usename = 'gitlab' ) THEN
RAISE NOTICE 'Hung backends. Backup might not be 100%% consistent';
END IF;
END;$X$;
-- 4. Allow read-only connections while checkpointing/snapshotting
alter database gitlabhq with allow_connections = on;
CHECKPOINT;
alter database gitlabhq_production with allow_connections = off;
alter database gitlabhq set default_transaction_read_only = false;
SELECT pg_stat_clear_snapshot();
SELECT pg_terminate_backend(pid) from pg_stat_activity where usename = 'gitlab';
alter database gitlabhq with allow_connections = on;
Il est possible que dans cette dernière étape, vous tuiez les requêtes en lecture seule/SELECT de longue durée, mais d'après mon expérience, ces requêtes de longue durée peuvent durer des minutes, voire des heures, et il est acceptable de les supprimer afin de garantir la disponibilité pour tous les autres.
Je pense qu'il serait souhaitable d'avoir cette fonctionnalité en tant que fonctionnalité officielle de PostgreSQL, personnellement.
Si vous ne voulez pas vous salir les mains avec le codage C pour une extension PostgreSQL, vous pouvez simplement mettre un pool de connexions devant PostgreSQL. Comme pgBouncer.
pgBouncer a la capacité de suspendre l'activité de l'application intégrée . Cependant, pour le rendre très utile, vous devez vous connecter directement (pas via pgbouncer) et annuler les connexions actives une fois que vous en avez interrompu l'entrée. Juste select pg_terminate_backend(pid) from pg_stat_activity where pid <> pg_backend_pid()
.
Si vous êtes prêt à vous salir les mains, vous pouvez le faire avec une extension C. L'extension doit:
Être chargé dans shared_preload_libraries
Afin qu'il puisse enregistrer un petit segment de mémoire partagée statique avec un indicateur booléen comme db_is_locked .
Enregistrez un ProcessUtility_hook
Et ExecutorStart_hook
Qui teste l'indicateur est verrouillé dans shmem et, s'il est défini, dort dans une boucle WaitLatch
jusqu'à ce qu'il voit que l'indicateur a été effacé à nouveau. (Vous pouvez éventuellement utiliser un crochet analyseur à la place).
Écrivez deux fonctions appelables SQL en C. L'une définit l'indicateur. Un autre efface le drapeau et itère via PGPROC
en définissant le verrou de tous les processus utilisateur, afin qu'ils sachent se réveiller immédiatement.
Vous pouvez éventuellement écrire une troisième fonction qui, si l'indicateur est défini, itère via PGXACT
pour trouver les transactions d'écriture ouvertes et leur signale de se terminer.
Tout cela a déjà été implémenté dans le cadre de l'extension BDR , mais cela fait partie d'un système beaucoup plus vaste. Vous pourriez très probablement extraire les parties pertinentes dans votre propre extension. Voir bdr_locks.c
, bdr_commandfilter.c
, bdr_executor.c
, bdr.c
, Etc.
Notez que cela ne rendra pas PostgreSQL en lecture seule sur le disque - le pointeur de contrôle continuera à fonctionner, le bgwriter continuera à fonctionner, l'archiveur continuera exécuter, etc. Il ne suffit donc pas de vous permettre de faire une sauvegarde de base de données sans un instantané du système de fichiers atomique ou pg_start_backup()
/pg_stop_backup()
. Mais cela convient à votre cas d'utilisation, interrompant l'activité de l'application au niveau de la base de données.