PostgreSQL Antipatterns: ndryshimi i të dhënave përmes një trigeri

PĂ«r njĂ« moment tĂ« caktuar, shumĂ« ndodhen pĂ«rballĂ« nevojĂ«s pĂ«r tĂ« bĂ«rĂ« disa korrigjime masive nĂ« regjistrimet e tabelĂ«s. UnĂ« tashmĂ« kam folur pĂ«r mĂ«nyrĂ«n mĂ« tĂ« mirĂ« pĂ«r ta bĂ«rĂ« kĂ«tĂ«, si dhe pĂ«r atĂ« se si nuk duhet ta bĂ«ni. Sot do tĂ« flas pĂ«r aspektin e dytĂ« tĂ« pĂ«rditĂ«simit masiv — pĂ«r aktivizimin e triggereve.

Për shembull, në tabelën ku duhet të bëni disa ndryshime, ka një trigger të padëshirueshëm ON UPDATE, që transferon të gjitha ndryshimet në disa agregate. Dhe ju duhet të përditësoni gjithçka (të inicializoni një fushë të re, për shembull) në një mënyrë kaq të kujdesshme sa që ato agregate të mos preken.

Le t'i fikim thjesht triggere!

BEGIN;
  ALTER TABLE ... DISABLE TRIGGER ...;
  UPDATE ...; -- këtu do të zgjaste
  ALTER TABLE ... ENABLE TRIGGER ...;
COMMIT;

Kjo Ă«shtĂ« e gjitha — gjithçka tashmĂ« Ă«shtĂ« nĂ« suspens.

Sepse ALTER TABLE ngarkon bllokimin AccessExclusive-bllokimi, nën të cilin askush që po ekzekuton paralelisht, madje edhe një i thjeshtë SELECT, nuk do të jetë i aftë të lexojë asgjë nga tabela. Do të thotë se derisa kjo transaksion të përfundojë, të gjithë ata që duan madje "thjesht të lexojnë" do të presin. Dhe ne e mbajmë mend se UPDATE kemi një shumë-lloog ...

Le t'i fikim shpejt, pastaj t'i kthejmë shpejt!

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

UPDATE ...;

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

Tani situata është më e mirë, koha e pritjes është dukshëm më e shkurtër. Por dy probleme prishet gjithë bukurinë:

  • ALTER TABLE vetĂ« pret tĂ« gjitha operacionet e tjera nĂ« tabelĂ«, duke pĂ«rfshirĂ« ato tĂ« gjata SELECT
  • NdĂ«rsa triggeri Ă«shtĂ« i fikur, "do tĂ« kalojĂ«" çdo ndryshim nĂ« tabelĂ«, madje edhe ato qĂ« nuk janĂ« tonat. Dhe atyre agregate me siguri nuk do t'u shkojnĂ«, edhe pse duhet. KatastrofĂ«!

Menaxhimi i variablave të sesionit

Pra, nĂ« versionin e mĂ«parshĂ«m ne u pĂ«rballĂ«m me njĂ« moment themelor — duhet tĂ« mĂ«sojmĂ« triggerin tĂ« dallojĂ« ndryshimet "tona" nĂ« tabelĂ« nga "tĂ« huajat". TĂ« "tonat" tĂ« kalojnĂ« ashtu si janĂ«, ndĂ«rsa ato "tĂ« huaja" — tĂ« aktivizohen. PĂ«r kĂ«tĂ«, mund tĂ« pĂ«rdorim variabla sesioni.

session_replication_role

Lexojmë manuali:

Mekanizmi i aktivizimit tĂ« triggereve gjithashtu ndikohet nga variabla konfiguruese session_replication_role. TriggerĂ«t e aktivizuar pa udhĂ«zime tĂ« tjera (nĂ« mĂ«nyrĂ« tĂ« paracaktuar) do tĂ« aktivizohen kur roli i replikimit Ă«shtĂ« "origin" (nĂ« parazgjedhje) ose "local". TriggerĂ«t tĂ« aktivizuar duke specifikuar ENABLE REPLICA, do tĂ« aktivizohen vetĂ«m nĂ«se reĆŸimi aktual i sesionit Ă«shtĂ« "replica", dhe triggerĂ«t tĂ« aktivizuar duke specifikuar ENABLE ALWAYS, do tĂ« aktivizohen pavarĂ«sisht nga aktuali regjimi i replikimit.

Dua të theksoj se kjo konfigurim i takon jo të gjithëve, si ALTER TABLE, dhe jo në lidhje me lidhjen tonë të veçantë. Në përfundim, për të mos aktivizuar ndonjë mënyrë njoftimi:

SET session_replication_role = replica; -- çaktivizoi njoftimet
UPDATE ...;
SET session_replication_role = DEFAULT; -- ktheu në gjendjen fillestare

Kushti brenda njoftimit

Por, opsioni i mësipërm funksionon për të gjitha njoftimet njëherazi (apo duhet të "ndryshojmë" paraprakisht njoftimet që nuk duam të çaktivizojmë). Po nëse na nevojitet "çaktivizimi" i një njoftimi të caktuar?

Në këtë na ndihmon "variabla" e përdoruesit të sesionit:

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

Së pari, përmirësojmë njoftimin, përafërsisht kështu:

BEGIN
    -- në procesin e konvertimit mund të bëjmë 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, kjo mund të bëhet "në qershi", pa bllokime, përmes CREATE OR REPLACE për funksionin e njoftimit. Pastaj në lidhjen speciale vendosim "variablën" tonë:


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

Dini mënyra të tjera? Ndani në komentet.

Burimi: habr.com

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