PostgreSQL antipatternlari: triggerni chetlab o'tib ma'lumotlarni o'zgartirish

Ertami-kechmi, ko'pchilik jadval yozuvlarida biror narsani ommaviy ravishda tuzatish zarurati bilan duch kelishadi. Menda allaqachon bor buni qanday qilib yaxshiroq qilishni ayting, va qanday qilib - buni qilmaslik yaxshiroqdir. Bugun men ommaviy yangilanishning ikkinchi jihati haqida gapiraman - triggerlar haqida.

Masalan, biror narsani tuzatishingiz kerak bo'lgan stolda yomon tetik osilgan ON UPDATE, barcha o'zgarishlarni ba'zi agregatlarga o'tkazish. Va siz hamma narsani yangilashingiz kerak (masalan, yangi maydonni ishga tushirish) bu agregatlarga ta'sir qilmasligi uchun ehtiyotkorlik bilan.

Keling, triggerlarni o'chirib qo'yamiz!

BEGIN;
  ALTER TABLE ... DISABLE TRIGGER ...;
  UPDATE ...; -- тут долго-долго
  ALTER TABLE ... ENABLE TRIGGER ...;
COMMIT;

Aslida, hammasi shu - hamma narsa osilgan.

Chunki ALTER TABLE yuklaydi Eksklyuzivga kirish- hech kim parallel ravishda ishlamaydigan qulf, hatto oddiy SELECT, jadvaldan hech narsa o'qiy olmaydi. Ya'ni, ushbu tranzaksiya tugaguniga qadar, hatto "shunchaki o'qishni" istagan har bir kishi kutadi. Va biz buni eslaymiz UPDATE bizda uzoq vaqt bor ...

Keling, uni tezda o'chiring, keyin tezda yoqing!

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

UPDATE ...;

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

Bu erda vaziyat allaqachon yaxshiroq, kutish vaqti ancha kam. Ammo faqat ikkita muammo butun go'zallikni buzadi:

  • ALTER TABLE o'zi stol ustidagi barcha boshqa operatsiyalarni, shu jumladan uzoq operatsiyalarni kutadi SELECT
  • Trigger o'chirilgan paytda har qanday o'zgarish "uchib o'tish" jadvalda, hatto bizniki ham emas. Va u agregatlarga kirmaydi, garchi kerak bo'lsa ham. Muammo!

Seans o'zgaruvchilarini boshqarish

Shunday qilib, oldingi versiyada biz asosiy fikrga qoqildik - biz qandaydir tarzda triggerni jadvaldagi "bizning" o'zgarishlarni "bizniki emas" dan farqlashni o'rgatishimiz kerak. "Bizniki" avvalgidek o'tkazib yuboriladi, lekin "bizniki emas" da ular ishga tushiriladi. Buning uchun siz foydalanishingiz mumkin sessiya o'zgaruvchilari.

sessiya_replikatsiya_roli

O'qing qo'llanma:

Trigger mexanizmiga konfiguratsiya o'zgaruvchisi ham ta'sir qiladi sessiya_replikatsiya_roli. Qo'shimcha ko'rsatmalarsiz yoqilgan (standart), replikatsiya roli "kelib chiqishi" (standart) yoki "mahalliy" bo'lsa, triggerlar ishga tushadi. Triggerlar belgilash orqali yoqilgan ENABLE REPLICA, faqat agar ishlaydi joriy seans rejimi - "replika" va triggerlar belgilash orqali yoqilgan ENABLE ALWAYS, joriy replikatsiya rejimidan qat'iy nazar ishlaydi.

Shuni alohida ta'kidlab o'tamanki, sozlama bir vaqtning o'zida hammasiga taalluqli emas ALTER TABLE, lekin faqat bizning alohida maxsus aloqamiz uchun. Hammasi bo'lib, hech qanday ilova ishlamasligi uchun:

SET session_replication_role = replica; -- выключили триггеры
UPDATE ...;
SET session_replication_role = DEFAULT; -- вернули в исходное состояние

Trigger ichidagi holat

Ammo yuqoridagi variant bir vaqtning o'zida barcha triggerlar uchun ishlaydi (yoki siz o'chirib qo'ymoqchi bo'lmagan triggerlarni oldindan "muqobil" qilishingiz kerak). Va agar kerak bo'lsa ma'lum bir tetikni "o'chirish"?

Bu bizga yordam beradi "foydalanuvchi" sessiyasi o'zgaruvchisi:

Kengaytma parametr nomlari quyidagicha yoziladi: kengaytma nomidan keyin nuqta, so'ngra parametr nomining o'zi, SQLdagi to'liq ob'ekt nomlariga o'xshash. Masalan: plpgsql.variable_conflict.
Tegishli kengaytma modulini yuklamaydigan jarayonlarda tizimdan tashqari variantlar o'rnatilishi mumkinligi sababli, PostgreSQL ikki komponentli har qanday nomlar uchun qiymatlar.

Birinchidan, biz triggerni yakunlaymiz, shunga o'xshash:

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;
...

Aytgancha, bu "foyda uchun", blokirovka qilmasdan, orqali amalga oshirilishi mumkin CREATE OR REPLACE tetik funktsiyasi uchun. Va keyin maxsus ulanishda biz "bizning" o'zgaruvchimizni xo'roz qilamiz:


SET mycfg.my_table_convert_process = 'TRUE';
UPDATE ...;
SET mycfg.my_table_convert_process = ''; -- вернули в исходное состояние

Boshqa usullarni bilasizmi? Izohlarda baham ko'ring.

Manba: www.habr.com

a Izoh qo'shish