Comment nous avons utilisé la réplication différée pour la récupération d'urgence avec PostgreSQL

Comment nous avons utilisé la réplication différée pour la récupération d'urgence avec PostgreSQL
La rĂ©plication n'est pas une sauvegarde. Ou peut-ĂȘtre que si ? Voici comment nous avons utilisĂ© la rĂ©plication retardĂ©e pour rĂ©cupĂ©rer des donnĂ©es aprĂšs avoir accidentellement supprimĂ© des Ă©tiquettes.

Experts en infrastructure sur GitLab sont responsables du fonctionnement de GitLab.com — le plus grand exemple de GitLab existant. Ici, il y a 3 millions d'utilisateurs et presque 7 millions de projets, ce qui en fait l'un des plus grands sites open source SaaS avec une architecture dĂ©diĂ©e. Sans le systĂšme de bases de donnĂ©es PostgreSQL, l'infrastructure de GitLab.com n'irait pas loin, et nous faisons tout ce qui est en notre pouvoir pour garantir la rĂ©silience en cas de dĂ©faillances qui pourraient entraĂźner une perte de donnĂ©es. Bien qu'une telle catastrophe soit peu probable, nous sommes bien prĂ©parĂ©s et avons divers mĂ©canismes de sauvegarde et de rĂ©plication.

La réplication n'est pas un moyen de sauvegarde de bases de données (voir ci-dessous). Mais maintenant, nous allons voir comment retrouver rapidement des données accidentellement supprimées en utilisant la réplication retardée : j'ai de GitLab.com utilisateur supprimé une étiquette pour le projet gitlab-ce et j'ai perdu le lien avec les demandes de fusion et les tùches.

Avec la réplication retardée, nous avons récupéré les données en seulement 1,5 heure. Voyez comment cela s'est passé.

Récupération à un instant donné avec PostgreSQL

PostgreSQL possĂšde une fonction intĂ©grĂ©e qui restaure l'Ă©tat de la base de donnĂ©es Ă  un moment donnĂ©. Elle s'appelle RĂ©cupĂ©ration Ă  un instant donnĂ© (PITR) et utilise les mĂȘmes mĂ©canismes qui maintiennent la validitĂ© de la rĂ©plique : en commençant par un instantanĂ© fiable de l'ensemble du cluster de bases de donnĂ©es (sauvegarde de base), nous appliquons une sĂ©rie d'Ă©tats jusqu'Ă  un moment prĂ©cis.

Pour utiliser cette fonction pour une sauvegarde à froid, nous faisons réguliÚrement une sauvegarde de base de la base de données et la stockons dans un archive (les archives de GitLab résident dans le stockage cloud de Google). Nous suivons également les changements de l'état de la base de données en archivant le journal des écritures anticipées (write-ahead log, WAL). Et avec tout cela, nous pouvons effectuer un PITR pour la récupération d'urgence : nous commençons par l'instantané fait avant l'erreur et appliquons les changements à partir de l'archive WAL jusqu'à la panne.

Qu'est-ce que la réplication retardée ?

La réplication retardée consiste à appliquer des modifications du WAL avec un certain délai. Cela signifie qu'une transaction s'est produite à une heure donnée X, mais dans la réplique, elle apparaßtra avec un retard d d'une heure X + d.

PostgreSQL propose 2 mĂ©thodes pour configurer une rĂ©plication physique de la base de donnĂ©es : la rĂ©cupĂ©ration Ă  partir d'une archive et la rĂ©plication en streaming. RĂ©cupĂ©ration Ă  partir d'une archive, en fait, fonctionne comme un PITR mais de maniĂšre continue : nous extrayons constamment les modifications de l'archive WAL et les appliquons Ă  la rĂ©plique. A rĂ©plication continue extrait directement le flux WAL de l'hĂŽte de base de donnĂ©es supĂ©rieur. Nous prĂ©fĂ©rons la rĂ©cupĂ©ration depuis l'archive — c'est plus facile Ă  gĂ©rer et cela possĂšde des performances normales qui ne sont pas infĂ©rieures Ă  celles du cluster opĂ©rationnel.

Comment configurer la récupération différée depuis l'archive

Options de récupération décrites dans le fichier recovery.conf. Exemple :

standby_mode = 'on'
restore_command = ' /usr/bin/envdir /etc/wal-e.d/env /opt/wal-e/bin/wal-e wal-fetch -p 4 "%f" "%p"'
recovery_min_apply_delay = '8h'
recovery_target_timeline = 'latest'

Avec ces paramÚtres, nous avons configuré une réplique différée récupérant depuis l'archive. Il utilise wal-e pour extraire les segments WAL (restore_command) de l'archive, et les modifications seront appliquées aprÚs huit heures (recovery_min_apply_delay). La réplique suivra les changements de la chronologie dans l'archive, par exemple, en raison de la gestion des pannes dans le cluster (recovery_target_timeline).

Avec recovery_min_apply_delay il est possible de configurer une réplication continue avec retard, mais il y a quelques piÚges liés aux slots de réplication, à la rétroaction de secours à chaud, etc. L'archive WAL permet de les éviter.

ParamÚtre recovery_min_apply_delay est apparue seulement dans PostgreSQL 9.3. Dans les versions précédentes, pour la réplication différée, il fallait configurer une combinaison de fonctions de gestion de récupération (pg_xlog_replay_pause(), pg_xlog_replay_resume()) ou conserver les segments WAL dans l'archive pendant la durée du retard.

Comment PostgreSQL fait-il cela ?

Il est intĂ©ressant de voir comment PostgreSQL met en Ɠuvre la rĂ©cupĂ©ration diffĂ©rĂ©e. Regardons recoveryApplyDelay(XlogReaderState). Cela est appelĂ© depuis la boucle principale de rĂ©pĂ©tition pour chaque enregistrement du WAL.

static bool
recoveryApplyDelay(XLogReaderState *record)
{
    uint8       xact_info;
    TimestampTz xtime;
    long        secs;
    int         microsecs;

    /* rien à faire si aucun délai n'est configuré */
    if (recovery_min_apply_delay <= 0)
        return false;

    /* aucun délai n'est appliqué sur une base de données non encore cohérente */
    if (!reachedConsistency)
        return false;

    /*
     * Est-ce un enregistrement COMMIT ?
     *
     * Nous choisissons délibérément de ne pas retarder les aborts car ils n'ont aucun effet sur
     * le MVCC. Nous permettons déjà la relecture des enregistrements qui n'ont pas de timestamp,
     * donc il y a déjà des opportunités pour des problÚmes causés par des conflits précoces sur
     * les réplicas.
     */
    if (XLogRecGetRmid(record) != RM_XACT_ID)
        return false;

    xact_info = XLogRecGetInfo(record) & XLOG_XACT_OPMASK;

    if (xact_info != XLOG_XACT_COMMIT &&
        xact_info != XLOG_XACT_COMMIT_PREPARED)
        return false;

    if (!getRecordTimestamp(record, &xtime))
        return false;

    recoveryDelayUntilTime =
        TimestampTzPlusMilliseconds(xtime, recovery_min_apply_delay);

    /*
     * Quitter sans armer le latch si c'est déjà passé le temps d'appliquer cet
     * enregistrement
     */
    TimestampDifference(GetCurrentTimestamp(), recoveryDelayUntilTime,
                        &secs, &microsecs);
    if (secs <= 0 && microsecs <= 0)
        return false;

    while (true)
    {
        // Raccourci :
        // Utiliser WaitLatch jusqu'Ă  ce que nous atteignions recoveryDelayUntilTime
        // puis
        break;
    }
    return true;
}

L'essentiel est que le dĂ©lai est basĂ© sur le temps physique enregistrĂ© dans l'horodatage du commit de la transaction (xtime). Comme on peut le voir, le dĂ©lai ne s'applique qu'aux commits et ne touche pas d'autres enregistrements — tous les changements sont appliquĂ©s directement, et le commit est retardĂ©, donc nous ne verrons les changements qu'aprĂšs le dĂ©lai configurĂ©.

Comment utiliser une réplique retardée pour la récupération des données

Supposons que nous ayons un cluster de bases de données en production et une réplique avec un retard de huit heures. Voyons comment récupérer les données à l'aide de la suppression accidentelle de raccourcis.

Lorsque nous avons pris connaissance du problÚme, nous avons suspendu la récupération à partir de l'archive pour la réplique retardée :

SELECT pg_xlog_replay_pause();

Avec la pause, nous n'avions pas le risque que la rĂ©plique rĂ©pĂšte la requĂȘte SUPPRIMER. C'est utile si vous avez besoin de temps pour tout comprendre.

L'essentiel est que la rĂ©plique retardĂ©e doit atteindre le moment juste avant la requĂȘte SUPPRIMER. Nous savions Ă  peu prĂšs le temps physique de la suppression. Nous avons supprimĂ© recovery_min_apply_delay et ajoutĂ© recovery_target_time dans recovery.conf. Ainsi, la rĂ©plique atteint le bon moment sans retard :

recovery_target_time = '2018-10-12 09:25:00+00'

Avec les horodatages, il est prĂ©fĂ©rable de rĂ©duire, pour ne pas se tromper. Toutefois, plus vous rĂ©duisez, plus vous perdez de donnĂ©es. Encore une fois, si nous manquons la requĂȘte SUPPRIMER, tout sera Ă  nouveau supprimĂ© et il faudra recommencer (ou utiliser une sauvegarde froide pour le PITR).

Nous avons redĂ©marrĂ© l'instance Postgres diffĂ©rĂ©e, et les segments WAL se rĂ©pĂ©tĂšrent jusqu'Ă  l'heure indiquĂ©e. Vous pouvez suivre les progrĂšs Ă  ce stade avec la requĂȘte :

SELECT
  -- emplacement actuel dans le WAL
  pg_last_xlog_replay_location(),
  -- horodatage de transaction actuel (état de la réplique)
  pg_last_xact_replay_timestamp(),
  -- temps physique actuel
  now(),
  -- le temps restant Ă  appliquer jusqu'Ă  ce que recovery_target_time soit atteint
  '2018-10-12 09:25:00+00'::timestamptz - pg_last_xact_replay_timestamp() as delay;

Si l'horodatage ne change plus, la restauration est terminée. Vous pouvez configurer l'action recovery_target_action, pour fermer, avancer ou suspendre l'instance aprÚs la répétition (par défaut, elle est suspendue).

La base de donnĂ©es est revenue Ă  un Ă©tat antĂ©rieur Ă  cette demande malheureuse. Il est maintenant possible, par exemple, d'exporter des donnĂ©es. Nous avons exportĂ© les donnĂ©es supprimĂ©es sur le marqueur et toutes les relations avec les tĂąches et les demandes de fusion et les avons transfĂ©rĂ©es dans la base de donnĂ©es de travail. S'il y a une perte importante, vous pouvez simplement avancer la rĂ©plique et l'utiliser comme principale. Mais dans ce cas, toutes les modifications aprĂšs le moment jusqu'oĂč nous avons rĂ©cupĂ©rĂ© seront perdues.

Il est préférable d'utiliser des ID de transactions au lieu d'horodatages. Il est utile de noter ces ID, par exemple, pour les opérateurs DDL (comme DROP TABLE), en utilisant log_statements = 'ddl'. Si nous avions eu l'ID de transaction, nous aurions pris recovery_target_xid et l'aurions exécuté jusqu'à la transaction précédant la demande. SUPPRIMER.

Revenir au travail est trĂšs simple : retirez toutes les modifications de recovery.conf et redĂ©marrez Postgres. La rĂ©plique aura bientĂŽt un retard de huit heures Ă  nouveau, et nous serons prĂȘts pour de futures mĂ©saventures.

Avantages de la restauration

Avec une réplique différée au lieu d'une sauvegarde à froid, il n'est pas nécessaire de restaurer l'ensemble de l'instantané depuis l'archive pendant des heures. Par exemple, nous avons besoin de cinq heures pour extraire l'ensemble de la sauvegarde de base de 2 To. Et ensuite, il faudra encore appliquer tout le WAL quotidien pour revenir à l'état souhaité (dans le pire des cas).

Une réplique différée est meilleure qu'une sauvegarde à froid pour deux raisons :

  1. Il n'est pas nécessaire d'extraire l'ensemble de la sauvegarde de base depuis l'archive.
  2. Il y a une fenĂȘtre fixe de huit heures de segments WAL Ă  rĂ©pĂ©ter.

De plus, nous vérifions en permanence s'il est possible de faire un PITR depuis le WAL, et nous remarquerions rapidement des dommages ou d'autres problÚmes avec l'archive WAL en surveillant le retard de la réplique différée.

Dans cet exemple, nous avons mis 50 minutes pour la restauration, soit une vitesse de 110 Go de données WAL par heure (l'archive était alors toujours sur AWS S3Nous avons résolu le problÚme et restauré les données en 1,5 heure.

RĂ©sumĂ© : oĂč la rĂ©plication diffĂ©rĂ©e est utile (et oĂč elle ne l'est pas)

Utilisez la rĂ©plication diffĂ©rĂ©e comme un moyen de premiers secours si vous avez accidentellement perdu des donnĂ©es et que vous avez remarquĂ© ce problĂšme dans la fenĂȘtre de dĂ©lai configurĂ©e.

Mais gardez à l'esprit : la réplication n'est pas une sauvegarde.

La sauvegarde et la rĂ©plication ont des objectifs diffĂ©rents. Une sauvegarde Ă  froid est utile si vous avez accidentellement effectuĂ© une SUPPRIMER ou DROP TABLE. Nous faisons une sauvegarde depuis le stockage Ă  froid et restaurons l'Ă©tat prĂ©cĂ©dent de la table ou de l'ensemble de la base de donnĂ©es. Mais Ă  ce stade, la requĂȘte DROP TABLE est presque instantanĂ©ment reproduite dans toutes les rĂ©plicas du cluster de travail, donc la rĂ©plication habituelle ne rĂ©soudra pas le problĂšme. La rĂ©plication elle-mĂȘme maintient la base de donnĂ©es accessible lorsque certains serveurs Ă©chouent, et rĂ©partit la charge.

MĂȘme avec une rĂ©plication diffĂ©rĂ©e, nous avons parfois vraiment besoin d'une sauvegarde Ă  froid dans un endroit sĂ»r, au cas oĂč un Ă©chec du centre de donnĂ©es se produirait, un dommage cachĂ© ou d'autres Ă©vĂ©nements qui ne sont pas immĂ©diatement visibles. Ici, une simple rĂ©plication ne suffit pas.

Remarque. À de GitLab.com nous protĂ©geons actuellement contre la perte de donnĂ©es uniquement au niveau du systĂšme et ne restaurons pas les donnĂ©es au niveau de l'utilisateur.

Source : habr.com

Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS đŸ”„ Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster