Antipatrones de PostgreSQL: cambie los datos sin pasar por un disparador

Tarde o temprano, muchos se enfrentan a la necesidad de arreglar masivamente algo en los registros de la tabla. Ya tengo dime como hacerlo mejor, y cómo, es mejor no hacerlo. Hoy hablaré sobre el segundo aspecto de la actualización masiva: sobre desencadenantes.

Por ejemplo, en una mesa en la que necesita arreglar algo, cuelga un gatillo malvado ON UPDATE, transfiriendo todos los cambios a algunos agregados. Y debe actualizar todo (iniciar un nuevo campo, por ejemplo) con tanto cuidado que estos agregados no se vean afectados.

¡Desactivemos los disparadores!

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

En realidad, eso es todo - todo está colgando.

Porque ALTER TABLE impone Acceso Exclusivo- un bloqueo bajo el cual nadie corre en paralelo, incluso uno simple SELECT, no podrá leer nada de la tabla. Es decir, hasta que finalice esta transacción, todos los que quieran incluso "solo leer" esperarán. Y recordamos que UPDATE tenemos un largo...

¡Apaguémoslo rápidamente y luego encendámoslo rápidamente!

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

UPDATE ...;

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

Aquí la situación ya es mejor, el tiempo de espera es mucho menor. Pero solo dos problemas estropean toda la belleza:

  • ALTER TABLE espera todas las demás operaciones en la mesa, incluidas las largas SELECT
  • Mientras el gatillo está apagado, "pasa volando" cualquier cambio en la mesa, ni siquiera la nuestra. Y no entrará en los agregados, aunque debería. ¡Problema!

Gestión de variables de sesión

Entonces, en la versión anterior, nos topamos con un punto fundamental: necesitamos enseñarle al disparador de alguna manera a distinguir "nuestros" cambios en la tabla de "no nuestros". Los "nuestros" se omiten tal cual, pero en "no nuestros" se activan. Para esto puedes usar variables de sesión.

rol_replicación_sesión

Nosotros leemos manual:

El mecanismo de activación también se ve afectado por la variable de configuración rol_replicación_sesión. Habilitado sin instrucciones adicionales (predeterminado), los activadores se activarán cuando el rol de replicación sea "origen" (predeterminado) o "local". Desencadenadores habilitados al especificar ENABLE REPLICA, funcionará solo si modo de sesión actual - "réplica" y disparadores habilitados especificando ENABLE ALWAYS, funcionará independientemente del modo de replicación actual.

Destacaré especialmente que la configuración no se aplica a todos a la vez, como ALTER TABLE, pero solo a nuestra conexión especial separada. En total, para que no funcione ningún disparador de aplicaciones:

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

Condición dentro del gatillo

Pero la opción anterior funciona para todos los disparadores a la vez (o necesita "alternar" los disparadores por adelantado que no desea desactivar). Y si necesitamos "apagar" un disparador específico?

esto nos ayudara variable de sesión "usuario":

Los nombres de los parámetros de extensión se escriben de la siguiente manera: el nombre de la extensión seguido de un punto y luego el propio nombre del parámetro, similar a los nombres completos de los objetos en SQL. Por ejemplo: plpgsql.variable_conflict.
Debido a que las opciones fuera del sistema se pueden configurar en procesos que no cargan el módulo de extensión apropiado, PostgreSQL acepta valores para cualquier nombre con dos componentes.

Primero, finalizamos el disparador, algo como esto:

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

Por cierto, esto se puede hacer "con fines de lucro", sin bloqueo, a través de CREATE OR REPLACE para la función de disparo. Y luego en la conexión especial amartillamos "nuestra" variable:


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

¿Conoces otras formas? Comparte en los comentarios.

Fuente: habr.com

Añadir un comentario