Skip to content

Universal trigger registry #3988

Open
Open
@drewdzzz

Description

@drewdzzz

Related dev. issues: tarantool/tarantool#8656

Product: Tarantool
Since: 3.0
Root document: new page - https://www.tarantool.io/en/doc/latest/reference/reference_lua/trigger/
SME (except for transactional triggers): @ drewdzzz
SME (transactional triggers): @ Gumix
https://www.tarantool.io/en/doc/latest/release/3.0.0/#triggers
https://habr.com/ru/companies/vk/articles/782318/

Universal trigger registry

The new Lua module trigger was introduced. It is storage of events - each event has its own unique name and a list of named triggers. Each trigger has unique name within the event in which it is registered.

The module has following methods:

  1. trigger.set(event_name, trigger_name, handler_f) - insert the named handler to the beginning of the trigger list or update a trigger by name (if the name is already taken). Handler can be any callable Lua object.
  2. trigger.del(event_name, trigger_name) - delete a trigger by name from the event. No-op if there is no such trigger.
  3. trigger.call(event_name, arg1, arg2, ...) - call all the triggers registered on the event, from the beginning of the list to its end. The execution is stopped after the first exception. Returned values are ignored, all the arguments are passed without copying and any other preprocessing. For advanced scenarios, pairs method can be used (for example, if you want to process returned values of each trigger).
  4. trigger.pairs(event_name) - an iterator over triggers, registered on event, iterates from the beginning to the end. Iterator yields two values - name of the trigger (string) and its handler (callable object).
  5. trigger.info([event_name]) - return key-value map {event: {{trigger_name1, handler1}, {trigger_name2, handler2}, ...}}, all the triggers inside one event are ordered in call order. If argument event_name is passed, map contains only one event with name event_name, if it has any registered triggers.

The idea of this module - any developer, including us, can create an event. He documents how registered triggers are called and provides this behavior using methods call and pairs. Example: for a trigger like space:on_replace, method call can be used - trigger.call("box.space.space_name.on_replace", old_tuple, new_tuple). But one cannot implement space:before_replace trigger using call method - this trigger passes returned value from current trigger as new_tuple to the next one. For such "advanced" semantics developer needs to use method pairs.

On the other side, users of events just set and delete triggers. Every event has its own behavior, so the user must thoroughly read the documentation of a particular event to set his triggers on it.

Naming

Every name (event_name or trigger_name) is a string, virtually divided into namespaces. Each namespace or name can be a string or a number. To address a sub namespace with a string identifier, one must use dot ("namespace.subnamespace"), in the case of a number identifier, square brackets must be used ("namespace[512]").

Example:

local trigger = require('trigger')
trigger.set('box.space[512].on_replace', 'my_name.on_replace_trigger', function(...) ... end)

Reserved namespaces

Root namespaces tarantool and box (names like tarantool.* and box.*) are reserved for tarantool triggers and internal purposes - creating user triggers or events with such names can lead to negative consequences.

Tarantool triggers

Almost all the tarantool triggers were moved to the trigger registry, old way to set triggers still works.

List of tarantool triggers in the trigger registry:

  1. box.session.on_connect - the new place for this trigger.
  2. box.session.on_disconnect the new place for this trigger.
  3. box.session.on_auth - the new place for this trigger.
  4. box.session.on_access_denied - the new place for this trigger.
  5. box.ctl.on_election - the new place for this trigger.
  6. box.ctl.on_recovery_state - the new place for this trigger.
  7. box.ctl.on_schema_init - the new place for this trigger.
  8. box.ctl.on_shutdown - the new place for this trigger.
  9. Space triggers - described below in a separate section.
  10. New transactional triggers - described below in a separate section.
  11. tarantool.trigger.on_change - described below in a separate section.

Space triggers

NB: behavior of triggers, set with old space trigger API, is not changed. Only the new triggers has new behavior.

In the new trigger system, each space has four types of triggers:

  1. box.space.test.on_replace - works in the same way as old on_replace triggers, but isn't fired on recovery.
  2. box.space.test.before_replace - works in the same way as old before_replace triggers, but isn't fired on recovery.
  3. box.space.test.on_recovery_replace - works in the same way as box.space.test.on_replace, but fired only on recovery and has two additional arguments - xrow header and xrow body of type MsgPack object, both MsgPacks are maps with integer keys.
  4. box.space.test.before_recovery_replace - works in the same way as box.space.test.before_replace, but fired only on recovery and has two additional arguments - xrow header and xrow body of type MsgPack object, both MsgPacks are maps with integer keys.

Also, each trigger can be set by id and by name. For example, we have a space named test with id = 512. We can set an on_replace trigger in two ways:

  1. trigger.set('box.space[512].on_replace', 'my_name.my_on_replace_trigger', function(...) ... end)
  2. trigger.set('box.space.test.on_replace', 'my_name.my_on_replace_trigger', function(...) ... end)

All the triggers, set by id, are fired before the triggers, set by name.

All the restriction are the same as for old space triggers.

New transactional triggers

NB: old transactional triggers are not moved to the trigger registry and still have old behavior.

The new transactional triggers:

  • box.before_commit - triggered when a transaction is ready to commit;
  • box.on_commit - triggered when a transaction is committed;
  • box.on_rollback - triggered when a transaction is rolled back.

Each of them have 3 versions, e.g. `box.on_commit' event has:

  • box.on_commit - global version, called for all transactions;
  • box.on_commit.space.test - called for transactions that write to
    space "test";
  • box.on_commit.space[512] - called for transactions that write to
    space with id 512.

One of the main advantages of the new triggers is that they can be set for all transactions, rather than setting them within each transaction.

Space-specific triggers are called prior to global ones. If a trigger-function fails, the remaining triggers for this event (including global) are not executed.

If a space-specific trigger is added to the registry within an active transaction, it may or may not be called on commit/rollback of this transaction (the behavior is unspecified).

The trigger-function for each event may take an iterator parameter. Similar to old transactional triggers, the iterator goes through the effects of every request that changed a space during the transaction.

box.before_commit trigger-function restrictions:

  • Yield is allowed (on memtx without mvcc it will abort the txn);
  • Allowed to write into database spaces;
  • If the function raises an error, the transaction is rolled back.

box.on_commit and box.on_rollback trigger-function restrictions:

  • Yield is forbidden (it will crash Tarantool);
  • The function should not access any database spaces;
  • If the function raises an error, the error is simply logged.

tarantool.trigger.on_change

In the trigger registry, there is event named 'tarantool.trigger.on_change' which is called when any event is modified (trigger.set or trigger.del is called). All the handlers are called with one argument - name of the modified event. Returned value of each handler is ignored. Handlers are fired after the event is changed (the event contains inserted trigger, if any, and does not contain deleted one, if any). All thrown errors are logged with error level and do not stop execution of triggers.

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.0reference[location] Tarantool manual, Reference parttriggers[area] Related to triggers

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions