42.1. Обзор механизма работы триггеров событий
Триггер события срабатывает всякий раз, когда в базе данных, в которой он определён, происходит связанное с ним событие. В настоящий момент поддерживаются следующие события: login
, ddl_command_start
, ddl_command_end
, table_rewrite
и sql_drop
. Поддержка дополнительных событий может быть добавлена в будущих выпусках.
Событие login
происходит, когда аутентифицированный пользователь входит в систему. Любые ошибки в процедуре триггера для этого события могут помешать успешному входу в систему. Такие ошибки можно исправить, отключив событийные триггеры (см. ignore_event_trigger) и переподключившись к системе или перезапустив сервер в однопользовательском режиме (поскольку триггеры событий в этом режиме отключены). За дополнительными сведениями об однопользовательском режиме обратитесь к справке по postgres. Событийный триггер login
также срабатывает на резервных серверах. Во избежание проблем с подключением к резервным серверам, работающие на них триггеры входа не должны ничего писать в базу данных.
Событие ddl_command_start
происходит непосредственно перед выполнением команд CREATE
, ALTER
, DROP
, SECURITY LABEL
, COMMENT
, GRANT
и REVOKE
. Проверка на существование объекта перед срабатыванием триггера не производится. В качестве исключения, однако, это событие не происходит для команд DDL, обращающихся к общим объектам кластера базы данных — базам данных, табличным пространствам, ролям, а также к самим триггерам событий. Событие ddl_command_start
также происходит непосредственно перед выполнением команды SELECT INTO
, так как она равнозначна команде CREATE TABLE AS
.
Событие ddl_command_end
происходит непосредственно после выполнения команд из того же набора. Чтобы получить дополнительную информацию об операциях DDL, повлёкших произошедшее событие, вызовите функцию pg_event_trigger_ddl_commands()
, возвращающую множество, из кода обработчика события ddl_command_end
(см. Раздел 9.29). Заметьте, что этот триггер срабатывает после того, как эти действия имели место (но до фиксации транзакции), так что в системных каталогах можно увидеть уже изменённое состояние.
Событие sql_drop
происходит непосредственно перед событием ddl_command_end
для команд, которые удаляют объекты базы данных. Для получения списка удалённых объектов используйте возвращающую набор строк функцию pg_event_trigger_dropped_objects()
в триггере события sql_drop
(см. Раздел 9.29). Обратите внимание, что триггер выполняется после удаления объектов из таблиц системного каталога, поэтому их невозможно больше увидеть.
Событие table_rewrite
происходит непосредственно перед тем, как таблица будет перезаписана в результате определённых действий команд ALTER TABLE
и ALTER TYPE
. Хотя перезапись таблицы может быть вызвана и другими управляющими операторами, в частности CLUSTER
и VACUUM
, событие table_rewrite
для них не вызывается. Найти OID таблицы, которая была перезаписана, можно с помощью функции pg_event_trigger_table_rewrite_oid()
(см. Раздел 9.29). Причину (причины) перезаписи можно посмотреть с помощью функции pg_event_trigger_table_rewrite_reason()
.
Триггеры событий (как и прочие функции) не могут выполняться в прерванной транзакции. Поэтому, если команда DDL завершается ошибкой, соответствующие триггеры ddl_command_end
не сработают. И наоборот, если триггер ddl_command_end
завершился с ошибкой, последующие триггеры событий не сработают, так же как и сама команда не будет выполняться. Похожим образом, если триггер ddl_command_end
завершится ошибкой, действие команды DDL будет отменено, как это происходит при возникновении ошибки внутри транзакции.
Полный список команд, которые поддерживаются триггерами событий, можно найти в Разделе 42.2.
Для создания триггера события используется команда CREATE EVENT TRIGGER. Предварительно нужно создать функцию, со специальным возвращаемым типом event_trigger
. Данная функция не обязана возвращать значение (и может не возвращать). Возвращаемый тип служит лишь указанием на то, что функция будет вызываться из триггера события.
Если есть несколько триггеров на одно и то же событие, то они будут вызываться в алфавитном порядке по имени триггера.
В определении триггера можно использовать условие WHEN
, чтобы, например, триггер ddl_command_start
срабатывал только для отдельных команд, которые нужно перехватить. Триггеры событий часто используются для ограничения диапазона DDL-команд, доступных пользователям.
48.2. Logical Decoding Concepts
48.2.1. Logical Decoding
Logical decoding is the process of extracting all persistent changes to a database's tables into a coherent, easy to understand format which can be interpreted without detailed knowledge of the database's internal state.
In Postgres Pro, logical decoding is implemented by decoding the contents of the write-ahead log, which describe changes on a storage level, into an application-specific form such as a stream of tuples or SQL statements.
48.2.2. Replication Slots
In the context of logical replication, a slot represents a stream of changes that can be replayed to a client in the order they were made on the origin server. Each slot streams a sequence of changes from a single database.
Note
Postgres Pro also has streaming replication slots (see Section 25.2.5), but they are used somewhat differently there.
A replication slot has an identifier that is unique across all databases in a Postgres Pro cluster. Slots persist independently of the connection using them and are crash-safe.
A logical slot will emit each change just once in normal operation. The current position of each slot is persisted only at checkpoint, so in the case of a crash the slot may return to an earlier LSN, which will then cause recent changes to be sent again when the server restarts. Logical decoding clients are responsible for avoiding ill effects from handling the same message more than once. Clients may wish to record the last LSN they saw when decoding and skip over any repeated data or (when using the replication protocol) request that decoding start from that LSN rather than letting the server determine the start point. The Replication Progress Tracking feature is designed for this purpose, refer to replication origins.
Multiple independent slots may exist for a single database. Each slot has its own state, allowing different consumers to receive changes from different points in the database change stream. For most applications, a separate slot will be required for each consumer.
A logical replication slot knows nothing about the state of the receiver(s). It's even possible to have multiple different receivers using the same slot at different times; they'll just get the changes following on from when the last receiver stopped consuming them. Only one receiver may consume changes from a slot at any given time.
Caution
Replication slots persist across crashes and know nothing about the state of their consumer(s). They will prevent removal of required resources even when there is no connection using them. This consumes storage because neither required WAL nor required rows from the system catalogs can be removed by VACUUM
as long as they are required by a replication slot. In extreme cases this could cause the database to shut down to prevent transaction ID wraparound (see Section 23.1.5). So if a slot is no longer required it should be dropped.
48.2.3. Output Plugins
Output plugins transform the data from the write-ahead log's internal representation into the format the consumer of a replication slot desires.
48.2.4. Exported Snapshots
When a new replication slot is created using the streaming replication interface (see CREATE_REPLICATION_SLOT), a snapshot is exported (see Section 9.27.5), which will show exactly the state of the database after which all changes will be included in the change stream. This can be used to create a new replica by using SET TRANSACTION SNAPSHOT
to read the state of the database at the moment the slot was created. This transaction can then be used to dump the database's state at that point in time, which afterwards can be updated using the slot's contents without losing any changes.
Creation of a snapshot is not always possible. In particular, it will fail when connected to a hot standby. Applications that do not require snapshot export may suppress it with the NOEXPORT_SNAPSHOT
option.