> На какие действия можно повесить триггер (Go)

Уровень: senior · Роль: backend · Язык: Go · Категория: Технические вопросы

Компании: Avito

Стек: Go

> Пример ответа

Короткий ответ

В PostgreSQL триггеры можно повесить на события INSERT, UPDATE, DELETE и TRUNCATE для таблиц, а также на события CREATE, ALTER, DROP и другие DDL-операции для схем и баз данных. В Go-разработке чаще всего используются триггеры на уровне БД (PostgreSQL, MySQL) для аудита, валидации, каскадных обновлений и синхронизации данных. Также существуют логические триггеры в приложении - через middleware или хуки в ORM, но это не относится к БД-триггерам.

Подробное объяснение

Триггеры в реляционных БД - это процедуры, которые автоматически выполняются при наступлении определённого события. В PostgreSQL триггеры бывают:

  • DML-триггеры - на INSERT, UPDATE, DELETE, TRUNCATE. Могут срабатывать до или после операции, для каждой строки или для всей команды (statement-level).
  • DDL-триггеры - на CREATE, ALTER, DROP, GRANT, REVOKE и другие события для таблиц, индексов, функций, схем. Срабатывают на уровне базы данных или схемы.
  • Event triggers - глобальные, на уровне кластера, например, на событие ddl_command_start или ddl_command_end.

В MySQL триггеры поддерживают только INSERT, UPDATE, DELETE, и только на уровне строк. В SQLite - INSERT, UPDATE, DELETE, но с ограничениями.

В Go-приложениях триггеры обычно реализуются на стороне БД через миграции (например, goose, golang-migrate). Альтернатива - прикладные хуки: в GORM есть BeforeSave, AfterCreate и т.п., но это не БД-триггеры, а часть ORM-логики.

На практике

Для senior-разработчика важно понимать trade-off между триггерами в БД и логикой в приложении. Триггеры гарантируют целостность данных на уровне БД, работают для любых клиентов (не только Go-сервисов), но усложняют отладку и тестирование. Прикладные хуки проще поддерживать, но они не защищают от прямых запросов к БД.

Типичные сценарии использования триггеров в Go-проектах:

  • аудит изменений (лог кто и когда изменил запись);
  • автоматическое обновление updated_at;
  • каскадное обновление связанных таблиц;
  • валидация данных, которую сложно выразить через CHECK-constraint;
  • синхронизация с поисковым индексом или кэшем (через outbox-паттерн).

При проектировании стоит избегать тяжёлых вычислений в триггерах - они выполняются в транзакции и блокируют строки. Также нужно помнить про порядок срабатывания нескольких триггеров на одно событие - он определяется алфавитным порядком имён.

Пример кода

SQL
-- Триггер на обновление updated_at
CREATE OR REPLACE FUNCTION set_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_users_updated_at
BEFORE UPDATE ON users
FOR EACH ROW
EXECUTE FUNCTION set_updated_at();
GO
// Миграция с использованием goose
// 00001_create_trigger.sql
-- +goose Up
CREATE TRIGGER trg_orders_audit
AFTER INSERT OR UPDATE OR DELETE ON orders
FOR EACH ROW
EXECUTE FUNCTION log_order_change();
-- +goose Down
DROP TRIGGER IF EXISTS trg_orders_audit ON orders;

Как отвечать на собеседовании

Начни с чёткого перечисления типов триггеров и событий. Затем приведи пример из практики - лучше, если это будет реальный кейс: аудит, синхронизация, защита от некорректных данных. Покажи понимание различий между row-level и statement-level триггерами, а также между триггерами в БД и хуками в ORM. Если спросят про производительность - упомяни, что триггеры выполняются в транзакции, и это может быть узким местом. Хорошо, если ты сможешь обосновать выбор триггера вместо логики в Go: например, когда данные могут меняться из разных источников.

Что проверяет интервьюер

Интервьюер оценивает:

  • знание синтаксиса и возможностей конкретной СУБД (PostgreSQL, MySQL);
  • понимание, когда триггеры уместны, а когда - нет;
  • умение проектировать миграции и поддерживать схему БД;
  • осознание рисков: сложность отладки, влияние на производительность, скрытая логика;
  • способность объяснить альтернативы (CHECK, FOREIGN KEY, прикладные хуки).

Типичные ошибки

  • Путаница между триггерами в БД и хуками ORM - это разные механизмы.
  • Утверждение, что триггеры работают в MySQL на уровне statement - это не так.
  • Игнорирование порядка срабатывания триггеров и рекурсивных вызовов.
  • Размещение тяжёлой бизнес-логики в триггере - это замедляет все операции с таблицей.
  • Забывают про TRUNCATE - на него тоже можно повесить триггер в PostgreSQL.
  • Не учитывают, что триггеры не видны в коде приложения - это усложняет онбординг и поддержку.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью