-
Notifications
You must be signed in to change notification settings - Fork 43
Universal trigger registry #3988
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
sergepetrenko
pushed a commit
to tarantool/tarantool
that referenced
this issue
Feb 13, 2024
This patch allows to override IPROTO request handlers by setting triggers on the corresponding events after the initial `box.cfg{}' call. Part of #8138 @TarantoolBot document Title: Document iproto override using event triggers Product: Tarantool Since: 3.1 Root document: New page - https://www.tarantool.io/en/doc/latest/reference/reference_lua/trigger/ Since Tarantool 3.1 there are 2 ways to override iproto request handlers: 1. Using `box.iproto.override()`, introduced in Tarantool 2.11: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_iproto/override/ 2. Using universal trigger registry: tarantool/doc#3988 To override an iproto request handler for the given request type, one can set a trigger (or multiple triggers) on the corresponding event. There are 2 types of iproto-overriding events: 1. set by request type id, e.g.: - box.iproto.override[1] - box.iproto.override[-1] 2. set by request type name (the name must be in the lowercase), e.g.: - box.iproto.override.select - box.iproto.override.unknown Override-by-id allows to set a handler for a particular request type, that is not known by the given version of Tarantool. This is not possible with override-by-name, where a type name must be known by Tarantool. Also there are a special type name "unknown" and a type id box.iproto.type.UNKNOWN (== -1) that allow to set a single handler for all unknown request types. Multiple triggers can be associated with a single event. The triggers are called in reverse order of their installation, however triggers set by id are called before triggers set by name. If a trigger returns `false`, the next trigger in the list is called, or a system handler if there are no more triggers. If a trigger returns `true`, no more triggers or system handlers are called. If some request type is overridden by both interfaces (legacy `box.iproto.override()' and new `trigger.set()'), the order of invocation of those handlers is unspecified. Co-authored-by: Andrey Saranchin <[email protected]>
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
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 ofevent
s - eachevent
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:
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.trigger.del(event_name, trigger_name)
- delete a trigger by name from the event. No-op if there is no such trigger.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).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).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 argumentevent_name
is passed, map contains only one event with nameevent_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
andpairs
. Example: for a trigger likespace:on_replace
, methodcall
can be used -trigger.call("box.space.space_name.on_replace", old_tuple, new_tuple)
. But one cannot implementspace:before_replace
trigger usingcall
method - this trigger passes returned value from current trigger asnew_tuple
to the next one. For such "advanced" semantics developer needs to use methodpairs
.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
ortrigger_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:
Reserved namespaces
Root namespaces
tarantool
andbox
(names liketarantool.*
andbox.*
) 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:
box.session.on_connect
- the new place for this trigger.box.session.on_disconnect
the new place for this trigger.box.session.on_auth
- the new place for this trigger.box.session.on_access_denied
- the new place for this trigger.box.ctl.on_election
- the new place for this trigger.box.ctl.on_recovery_state
- the new place for this trigger.box.ctl.on_schema_init
- the new place for this trigger.box.ctl.on_shutdown
- the new place for this trigger.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:
box.space.test.on_replace
- works in the same way as oldon_replace
triggers, but isn't fired on recovery.box.space.test.before_replace
- works in the same way as oldbefore_replace
triggers, but isn't fired on recovery.box.space.test.on_recovery_replace
- works in the same way asbox.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.box.space.test.before_recovery_replace
- works in the same way asbox.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
withid = 512
. We can set an on_replace trigger in two ways:trigger.set('box.space[512].on_replace', 'my_name.my_on_replace_trigger', function(...) ... end)
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 tospace "test";
box.on_commit.space[512]
- called for transactions that write tospace 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:box.on_commit
andbox.on_rollback
trigger-function restrictions: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
ortrigger.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.The text was updated successfully, but these errors were encountered: