Antipatterns PostgreSQL: Ndryshimi i të dhënave përmes një trigger-i

NĂ« njĂ« moment apo tjetĂ«r, shumĂ« njerĂ«z pĂ«rballen me nevojĂ«n pĂ«r tĂ« korrigjuar masivisht tĂ« dhĂ«nat nĂ« tabela. UnĂ« tashmĂ« kam folur pĂ«r mĂ«nyrat mĂ« tĂ« mira pĂ«r ta bĂ«rĂ« kĂ«tĂ«, si dhe pĂ«r atĂ« si — nuk Ă«shtĂ« mirĂ« tĂ« bĂ«het. Sot do tĂ« flas pĂ«r aspektin e dytĂ« tĂ« pĂ«rditĂ«simit masiv — pĂ«r funksionimin e trigger-ave.

Për shembull, në një tabelë, ku ju duhet të bëni disa ndryshime, ka një trigger të rrezikshëm ON UPDATE, i cili përcjell të gjitha ndryshimet në disa agregate. Dhe ju duhet të përditësoni gjithçka (për shembull, të inicializoni një fushë të re) në një mënyrë kaq delikate sa që këto aggregate të mos preken.

Le të mbyllim thjesht trigger-at!

BEGIN;
  ALTER TABLE ... DISABLE TRIGGER ...;
  UPDATE ...; -- këtu zgjat shumë
  ALTER TABLE ... ENABLE TRIGGER ...;
COMMIT;

NĂ« thelb, kĂ«tu Ă«shtĂ« gjithçka — gjithçka Ă«shtĂ« e aktivizuar.

Sepse ALTER TABLE vendos AccessExclusive-bllokimin, nën të cilin askush që ekzekuton paralelisht, madje as një thjesht SELECT, nuk mund të lexojë asgjë nga tabela. Pra, derisa kjo transaksion të përfundojë, të gjithë që dëshirojnë madje "thjesht të lexojnë" do të presin. Dhe ne kujtojmë se UPDATE kemi një periudhë shumë të gjatë...

Le të çaktivizojmë shpejt, pastaj ta aktivizojmë shpejt!

BEGIN;
  ALTER TABLE ... DISABLE TRIGGER ...;
COMMIT;

UPDATE ...;

BEGIN;
  ALTER TABLE ... ENABLE TRIGGER ...;
COMMIT;

Këtu situata është më e mirë, koha e pritjes është dukshëm më e shkurtër. Por dy probleme prishin gjithë bukurinë:

  • ALTER TABLE ai pret tĂ« gjitha operacionet e tjera nĂ« tabelĂ«, duke pĂ«rfshirĂ« ato tĂ« gjatĂ« SELECT
  • NdĂ«rsa trigger-i Ă«shtĂ« i çaktivizuar, "do tĂ« kalojĂ«" çdo ndryshim nĂ« tabelĂ«, madje edhe ai qĂ« nuk Ă«shtĂ« tonin. Dhe nĂ« agregate nuk do tĂ« arrijĂ«, edhe pse duhet. NjĂ« problem!

Menaxhimi i variablave të sesionit

Pra, nĂ« variantin e mĂ«parshĂ«m ne u ndeshĂ«m me njĂ« moment thelbĂ«sor — duhet tĂ« mĂ«sojmĂ« ndonjĂ«herĂ« trigger-in pĂ«r tĂ« dalluar ndryshimet "tona" nĂ« tabelĂ« nga ato "tĂ« huaja". TĂ« "tonat" le tĂ« kalojnĂ« siç janĂ«, ndĂ«rsa "tĂ« huajat" – tĂ« aktivizohen. PĂ«r kĂ«tĂ« mund tĂ« shfrytĂ«zojmĂ« variablat e sesionit.

session_replication_role

Lexojmë manual:

Mekanizmi i funksionimit të trigger-ave gjithashtu ndikohet nga variabla konfiguruese session_replication_role. Trigger-at e përfshirë pa udhëzime të tjera (për të drejtë) do të aktivizohen kur roli i replikimit është "origin" (për të drejtë) ose "local". Trigger-at e përfshirë duke e caktuar ENABLE REPLICA, do të aktivizohen vetëm nëse modi i tanishëm i sesionit është "replica", dhe trigger-at e përfshirë duke e caktuar ENABLE ALWAYS, do të aktivizohen pavarësisht nga moda e tanishme e replikimit.

Dua të theksoj veçanërisht, se kjo konfigurim nuk prek të gjitha gjithashtu, si ALTER TABLE, por një lidhje speciale të veçantë. Në total, që të mos aktivizohen ndonjëherë trigger-a aplikativë:

SET session_replication_role = replica; -- ndalova trigger-at
UPDATE ...;
SET session_replication_role = DEFAULT; -- e riktheva në gjendjen fillestare

Kushti brenda trigger-it

Por varianti i lartpërmendur funksionon për të gjithë trigger-at në të njëjtën kohë (ose duhet "të ndryshoni" më parë trigger-at që nuk dëshironi t'i ndaloni). Por nëse na nevojitet "të ndalojmë" një trigger konkret?

Në këtë na ndihmon "ndryshimi" i variablit të sesionit:

Emrat e parametrave të zgjerimeve shkruhen në këtë mënyrë: emri i zgjerimit, pika dhe pastaj emri real i parametrit, siç janë emrat e plotë të objekteve në SQL. Për shembull: plpgsql.variable_conflict.
Të dhënat jashtë sistemit mund të vendosen në procese që nuk ngarkojnë modulin përkatës të zgjerimit, PostgreSQL pranon vlerat për çdo emër me dy komponente.

Së pari, përmirësojmë trigger-in, në këtë mënyrë:

BEGIN
    -- procesi i konvertimit mund të bëjë gjithçka
    IF current_setting('mycfg.my_table_convert_process') = 'TRUE' THEN
        IF TG_OP IN ('INSERT', 'UPDATE') THEN
            RETURN NEW;
        ELSE
            RETURN OLD;
        END IF;
    END IF;
...

PĂ«r mĂ« tepĂ«r, mund ta bĂ«jmĂ« "nĂ« kohĂ« reale", pa bllokime, pĂ«rmes KRIJO OSE ZËVENDËSO pĂ«r funksionin e trigger-it. NdĂ«rsa mĂ« pas nĂ« lidhjen speciale aktivizojmĂ« "variablin tonĂ«":


SET mycfg.my_table_convert_process = 'TRUE';
UPDATE ...;
SET mycfg.my_table_convert_process = ''; -- e riktheva në gjendjen fillestare

A keni njohuri për mënyra të tjera? Ndani në komentet.

Burimi: habr.com

Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster