Kiel ni uzis malfruan reproduktadon por katastrofa reakiro kun PostgreSQL

Kiel ni uzis malfruan reproduktadon por katastrofa reakiro kun PostgreSQL
Reproduktado ne estas sekurkopio. Aŭ ne? Jen kiel ni uzis prokrastan reproduktadon por rekuperi de hazarde forigo de ŝparvojoj.

Specialistoj pri infrastrukturo GitLab respondecas pri la laboro GitLab.com - la plej granda GitLab-instanco en la naturo. Kun 3 milionoj da uzantoj kaj preskaŭ 7 milionoj da projektoj, ĝi estas unu el la plej grandaj malfermfontaj SaaS-ejoj kun dediĉita arkitekturo. Sen la datumbaza sistemo PostgreSQL, la GitLab.com-infrastrukturo ne iros malproksimen, kaj kion ni faras por certigi misfunkciadon en kazo de iuj malsukcesoj kiam datumoj povas esti perditaj. Estas neverŝajne, ke tia katastrofo okazos, sed ni estas bone preparitaj kaj provizitaj per diversaj sekurkopioj kaj reproduktaj mekanismoj.

Reproduktado ne estas rimedo por subteni datumbazojn (vidu sube). Sed nun ni vidos kiel rapide reakiri hazarde forigitajn datumojn per maldiligenta reproduktado: ŝaltita GitLab.com la uzanto forigis la ŝparvojon por la projekto gitlab-ce kaj perditaj ligoj kun kunfandaj petoj kaj taskoj.

Kun prokrastita kopio, ni reakiris datumojn en nur 1,5 horoj. Rigardu kiel ĝi okazis.

Punkta reakiro kun PostgreSQL

PostgreSQL havas enkonstruitan funkcion, kiu restarigas la staton de datumbazo al specifa tempopunkto. Ĝi nomiĝas Punkta Reakiro (PITR) kaj uzas la samajn mekanismojn, kiuj konservas la kopion ĝisdatigita: komencante per fidinda momentfoto de la tuta datumbaza areto (baza sekurkopio), ni aplikas serion de ŝtatŝanĝoj ĝis certa tempo.

Por uzi ĉi tiun funkcion por malvarma sekurkopio, ni regule faras bazan datumbazan sekurkopion kaj konservas ĝin en arkivo (GitLab-arkivoj loĝas en Google nuba stokado). Ni ankaŭ kontrolas ŝanĝojn en la stato de la datumbazo per arkivado de la antaŭskriba protokolo (protokolo pri skribado, WAL). Kaj kun ĉio ĉi en loko, ni povas fari PITR por katastrofa reakiro: komencante kun la momentfoto prenita antaŭ la fiasko, kaj aplikante la ŝanĝojn de la WAL-arkivo ĝis la fiasko.

Kio estas prokrastita reproduktado?

Maldiligenta reproduktado estas la apliko de ŝanĝoj de WAL kun prokrasto. Tio estas, la transakcio okazis en horo X, sed ĝi aperos en la kopio kun prokrasto d post unu horo X + d.

PostgreSQL havas 2 manierojn agordi fizikan datumbazan kopion: rezerva reakiro kaj fluanta reproduktado. Restarigo de arkivo, esence funkcias kiel PITR, sed kontinue: ni konstante prenas ŝanĝojn el la WAL-arkivo kaj aplikas ilin al la kopio. A fluanta reproduktado rekte prenas la WAL-rivereton de la kontraŭflua datumbaza gastiganto. Ni preferas arkivan reakiron - ĝi estas pli facile administrebla kaj havas normalan agadon, kiu sekvas la produktadgrupon.

Kiel agordi prokrastan reakiron de arkivo

Ebloj de reakiro priskribita en la dosiero recovery.conf. Ekzemplo:

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'

Kun ĉi tiuj parametroj, ni agordis prokrastan kopion kun rezerva reakiro. Ĉi tie ĝi estas uzata wal-e eltiri WAL-segmentojn (restore_command) de la arkivo, kaj ŝanĝoj estos aplikitaj post ok horoj (recovery_min_apply_delay). La kopio rigardos por templiniaj ŝanĝoj en la arkivo, ekzemple pro grapolmalsukceso (recovery_target_timeline).

С recovery_min_apply_delay Vi povas agordi fluantan reproduktadon kun malfruo, sed ekzistas kelkaj faŭltoj ĉi tie, kiuj rilatas al reproduktaj fendoj, varma standby sugestoj, ktp. La WAL-arkivo permesas vin eviti ilin.

Parametro recovery_min_apply_delay aperis nur en PostgreSQL 9.3. En antaŭaj versioj, por prokrastita reproduktado vi devas agordi la kombinaĵon funkcioj pri reakiro (pg_xlog_replay_pause(), pg_xlog_replay_resume()) aŭ teni WAL-segmentojn en arkivo dum la tempodaŭro de la prokrasto.

Kiel PostgreSQL faras tion?

Estas interese vidi kiel PostgreSQL efektivigas maldiligentan reakiron. Ni rigardu recoveryApplyDelay(XlogReaderState). Ĝi estas vokita de ĉefa ripeta buklo por ĉiu eniro de WAL.

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

    /* nothing to do if no delay configured */
    if (recovery_min_apply_delay <= 0)
        return false;

    /* no delay is applied on a database not yet consistent */
    if (!reachedConsistency)
        return false;

    /*
     * Is it a COMMIT record?
     *
     * We deliberately choose not to delay aborts since they have no effect on
     * MVCC. We already allow replay of records that don't have a timestamp,
     * so there is already opportunity for issues caused by early conflicts on
     * standbys.
     */
    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);

    /*
     * Exit without arming the latch if it's already past time to apply this
     * record
     */
    TimestampDifference(GetCurrentTimestamp(), recoveryDelayUntilTime,
                        &secs, &microsecs);
    if (secs <= 0 && microsecs <= 0)
        return false;

    while (true)
    {
        // Shortened:
        // Use WaitLatch until we reached recoveryDelayUntilTime
        // and then
        break;
    }
    return true;
}

La fundo estas, ke la prokrasto baziĝas sur la fizika tempo registrita en la transakcia transakcia tempostampo (xtime). Kiel vi povas vidi, la prokrasto validas nur por transdonoj kaj ne influas aliajn enirojn - ĉiuj ŝanĝoj estas aplikataj rekte, kaj la transdono estas prokrastita, do ni vidos la ŝanĝojn nur post la agordita prokrasto.

Kiel uzi malfruan kopion por restarigi datumojn

Ni diru, ke ni havas datumbazan areton kaj kopion kun ok-hora prokrasto en produktado. Ni vidu kiel reakiri datumojn uzante ekzemplon hazarde forigante ŝparvojojn.

Kiam ni eksciis pri la problemo, ni arkiva restarigo estas paŭzita por prokrastita kopio:

SELECT pg_xlog_replay_pause();

Kun paŭzo, ni havis neniun riskon, ke la kopio ripetu la peton DELETE. Utila afero se vi bezonas tempon por eltrovi ĉion.

La punkto estas, ke la prokrastita kopio devas atingi la momenton antaŭ la peto DELETE. Ni proksimume konis la fizikan tempon de forigo. Ni forigis recovery_min_apply_delay kaj aldonis recovery_target_time в recovery.conf. Jen kiel la kopio atingas la ĝustan momenton sen prokrasto:

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

Kun tempomarkoj, estas pli bone redukti la troon por ne maltrafi. Vere, ju pli granda estas la malkresko, des pli da datumoj ni perdas. Denove, se ni maltrafas la peton DELETE, ĉio estos denove forigita kaj vi devos rekomenci (aŭ eĉ preni malvarman sekurkopion por PITR).

Ni rekomencis la prokrastita Postgres-instanco kaj la WAL-segmentoj ripetiĝis ĝis la specifita tempo. Vi povas spuri progreson en ĉi tiu etapo demandante:

SELECT
  -- current location in WAL
  pg_last_xlog_replay_location(),
  -- current transaction timestamp (state of the replica)
  pg_last_xact_replay_timestamp(),
  -- current physical time
  now(),
  -- the amount of time still to be applied until recovery_target_time has been reached
  '2018-10-12 09:25:00+00'::timestamptz - pg_last_xact_replay_timestamp() as delay;

Se la tempomarko ne plu ŝanĝiĝas, la reakiro estas kompleta. Ago povas esti personecigita recovery_target_actionpor fermi, antaŭenigi aŭ paŭzi la kazon post reprovo (ĝi estas malakceptita defaŭlte).

La datumbazo revenis al sia stato antaŭ tiu malfeliĉa peto. Nun vi povas, ekzemple, eksporti datumojn. Ni eksportis la forigitajn etikedajn datumojn kaj ĉiujn ligilojn al temoj kaj kunfandi petojn kaj movis ilin en la produktaddatumbazon. Se la perdoj estas grandskalaj, vi povas simple reklami la kopion kaj uzi ĝin kiel la ĉefan. Sed tiam ĉiuj ŝanĝoj post la punkto al kiu ni resaniĝis estos perditaj.

Anstataŭ tempomarkoj, estas pli bone uzi transakciajn identigilojn. Estas utile registri ĉi tiujn identigilojn, ekzemple, por DDL-deklaroj (kiel ekzemple DROP TABLE), uzante log_statements = 'ddl'. Se ni havus transakcian ID, ni prenus recovery_target_xid kaj kuris ĉion malsupren al la transakcio antaŭ la peto DELETE.

Reiri al laboro estas tre simpla: forigu ĉiujn ŝanĝojn de recovery.conf kaj rekomencu Postgres. La kopio baldaŭ havos ok-horan prokraston denove, kaj ni estas pretaj por estontaj problemoj.

Reakiro Profitoj

Kun prokrastita kopio anstataŭ malvarma sekurkopio, vi ne devas pasigi horojn por restarigi la tutan bildon el la arkivo. Ekzemple, ni bezonas kvin horojn por akiri la tutan bazan 2 TB-rezervon. Kaj tiam vi ankoraŭ devas apliki la tutan ĉiutagan WAL por renormaliĝi al la dezirata stato (en la plej malbona kazo).

Prokrastita kopio estas pli bona ol malvarma sekurkopio en du manieroj:

  1. Ne necesas forigi la tutan bazan sekurkopion de la arkivo.
  2. Estas fiksa ok-hora fenestro de WAL-segmentoj, kiuj devas esti ripetitaj.

Ni ankaŭ konstante kontrolas por vidi ĉu eblas fari PITR de WAL, kaj ni rapide rimarkus korupton aŭ aliajn problemojn kun la WAL-arkivo monitorante la malfruon de la prokrastita kopio.

En ĉi tiu ekzemplo, ni bezonis 50 minutojn por restarigi, kio signifas, ke la rapideco estis 110 GB da WAL-datumoj hore (la arkivo ankoraŭ estis enŝaltita. AWS S3). Entute, ni solvis la problemon kaj reakiris la datumojn en 1,5 horoj.

Rezultoj: kie prokrastita kopio estas utila (kaj kie ĝi ne estas)

Uzu prokrastan reproduktadon kiel unua helpo se vi hazarde perdis datumojn kaj rimarkis ĉi tiun problemon en la agordita prokrasto.

Sed memoru: reproduktado ne estas sekurkopio.

Rezervo kaj reproduktado havas malsamajn celojn. Malvarma sekurkopio utilos se vi hazarde faris DELETEDROP TABLE. Ni faras sekurkopion de malvarma konservado kaj restarigas la antaŭan staton de la tabelo aŭ la tuta datumbazo. Sed samtempe la peto DROP TABLE estas preskaŭ tuj reproduktita en ĉiuj kopioj sur la funkcianta areto, do ordinara reproduktado ne helpos ĉi tie. Reproduktado mem tenas la datumbazon havebla kiam individuaj serviloj estas luitaj kaj distribuas la ŝarĝon.

Eĉ kun prokrastita kopio, ni foje vere bezonas malvarman sekurkopion en sekura loko se okazas fiasko de datumcentro, kaŝita damaĝo aŭ aliaj eventoj, kiuj ne tuj rimarkeblas. Reproduktado sole ne utilas ĉi tie.

Примечание. On GitLab.com Ni nuntempe nur protektas kontraŭ datumperdo ĉe la sistemnivelo kaj ne reakiras datumojn ĉe la uzantnivelo.

fonto: www.habr.com

Aldoni komenton