Mga PostgreSQL Antipattern: baguhin ang data sa pag-bypass sa isang trigger

Maaga o huli, marami ang nahaharap sa pangangailangang malawakang ayusin ang isang bagay sa mga talaan ng talahanayan. Meron na ako sabihin sa akin kung paano ito gagawin nang mas mahusay, at kung paano - mas mainam na huwag gawin ito. Ngayon ay magsasalita ako tungkol sa pangalawang aspeto ng mass update - tungkol sa mga trigger.

Halimbawa, sa isang mesa kung saan kailangan mong ayusin ang isang bagay, isang masamang gatilyo ang nakabitin ON UPDATE, paglilipat ng lahat ng pagbabago sa ilang pinagsama-samang. At kailangan mong i-update ang lahat (magsimula ng bagong field, halimbawa) nang maingat upang hindi maapektuhan ang mga pinagsama-samang ito.

I-disable na lang natin ang triggers!

BEGIN;
  ALTER TABLE ... DISABLE TRIGGER ...;
  UPDATE ...; -- Ρ‚ΡƒΡ‚ Π΄ΠΎΠ»Π³ΠΎ-Π΄ΠΎΠ»Π³ΠΎ
  ALTER TABLE ... ENABLE TRIGGER ...;
COMMIT;

Actually, yun lang- lahat ay nakabitin.

Dahil sa ALTER TABLE nagpapataw I-access ang Eksklusibo- isang lock sa ilalim kung saan walang tumatakbo sa parallel, kahit na isang simple SELECT, ay hindi makakapagbasa ng anuman mula sa talahanayan. Ibig sabihin, hanggang sa matapos ang transaksyong ito, lahat ng gustong kahit β€œmagbasa lang” ay maghihintay. At naaalala namin iyon UPDATE matagal na tayo...

Mabilis nating i-off ito, pagkatapos ay mabilis na i-on!

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

UPDATE ...;

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

Narito ang sitwasyon ay mas mahusay na, ang oras ng paghihintay ay mas kaunti. Ngunit dalawang problema lamang ang sumisira sa lahat ng kagandahan:

  • ALTER TABLE mismo ay naghihintay para sa lahat ng iba pang mga operasyon sa talahanayan, kabilang ang mga mahaba SELECT
  • Habang naka-off ang trigger "lumipad sa pamamagitan ng" anumang pagbabago sa mesa, kahit sa amin. At hindi ito mapupunta sa mga pinagsama-sama, bagaman dapat. Gulo!

Pamamahala ng mga variable ng session

Kaya, sa nakaraang bersyon, natitisod tayo sa isang pangunahing punto - kailangan nating ituro ang trigger na makilala ang "aming" mga pagbabago sa talahanayan mula sa "hindi atin". Ang "atin" ay nilaktawan kung ano man, ngunit sa "hindi sa amin" sila ay na-trigger. Para dito maaari mong gamitin mga variable ng session.

session_replication_role

Basahin manwal:

Ang mekanismo ng pag-trigger ay apektado din ng variable ng pagsasaayos session_replication_role. Naka-enable nang walang karagdagang mga tagubilin (default), gagana ang mga trigger kapag ang tungkulin ng pagtitiklop ay "pinagmulan" (default) o "lokal." Pinagana ang mga trigger sa pamamagitan ng pagtukoy ENABLE REPLICA, gagana lamang kung kasalukuyang session mode - "replica", at mga trigger na pinagana sa pamamagitan ng pagtukoy ENABLE ALWAYS, ay gagana anuman ang kasalukuyang replication mode.

Lalo kong bibigyang-diin na ang setting ay hindi nalalapat sa lahat-lahat nang sabay-sabay, bilang ALTER TABLE, ngunit sa aming hiwalay na espesyal na koneksyon lamang. Sa kabuuan, upang walang application na mag-trigger ng trabaho:

SET session_replication_role = replica; -- Π²Ρ‹ΠΊΠ»ΡŽΡ‡ΠΈΠ»ΠΈ Ρ‚Ρ€ΠΈΠ³Π³Π΅Ρ€Ρ‹
UPDATE ...;
SET session_replication_role = DEFAULT; -- Π²Π΅Ρ€Π½ΡƒΠ»ΠΈ Π² исходноС состояниС

Kondisyon sa loob ng trigger

Ngunit ang opsyon sa itaas ay gumagana para sa lahat ng mga trigger nang sabay-sabay (o kailangan mong "magpalitan" ng mga trigger nang maaga na hindi mo gustong i-disable). At kung kailangan natin "i-off" ang isang partikular na trigger?

Makakatulong ito sa atin variable ng session ng "user".:

Ang mga pangalan ng parameter ng extension ay nakasulat tulad ng sumusunod: ang pangalan ng extension na sinusundan ng isang tuldok at pagkatapos ay ang pangalan ng parameter mismo, katulad ng buong pangalan ng object sa SQL. Halimbawa: plpgsql.variable_conflict.
Dahil ang mga opsyon sa labas ng system ay maaaring itakda sa mga prosesong hindi naglo-load ng naaangkop na extension module, tinatanggap ng PostgreSQL mga halaga para sa anumang mga pangalan na may dalawang bahagi.

Una, tinatapos namin ang trigger, tulad nito:

BEGIN
    -- процСссу ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚Π°Ρ†ΠΈΠΈ ΠΌΠΎΠΆΠ½ΠΎ Π΄Π΅Π»Π°Ρ‚ΡŒ всС
    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;
...

Sa pamamagitan ng paraan, ito ay maaaring gawin "para sa kita", nang walang pagharang, sa pamamagitan ng CREATE OR REPLACE para sa trigger function. At pagkatapos ay sa espesyal na koneksyon namin i-cock "aming" variable:


SET mycfg.my_table_convert_process = 'TRUE';
UPDATE ...;
SET mycfg.my_table_convert_process = ''; -- Π²Π΅Ρ€Π½ΡƒΠ»ΠΈ Π² исходноС состояниС

May alam ka bang ibang paraan? Ibahagi sa mga komento.

Pinagmulan: www.habr.com

Magdagdag ng komento