[Tarantool-patches] [PATCH 1/1] txn: destroy commit and rollback triggers

Serge Petrenko sergepetrenko at tarantool.org
Sun Apr 25 11:44:48 MSK 2021



24.04.2021 03:29, Vladislav Shpilevoy пишет:
> They were not deleted ever. Worked fine for DDL, for which they
> were introduced on the first place, because these triggers are on
> the region memory.
>
> But didn't work when the triggers became available in the public
> API, because these are allocated on the heap. As a result, all the
> box.on_commit() and box.on_rollback() triggers leaked.
>
> The patch ensures all the on_commit/on_rollback triggers are
> destroyed. In order to mitigate the performance impact the
> internal triggers now clear themselves so as it wouldn't be
> necessary to walk their list in txn.c again.
>
> Another hack not to touch these triggers when possible makes their
> destruction happen only in txn_run_commit_triggers() and
> txn_run_rollback_triggers(). These are not called when a
> transaction does not have any triggers, and therefore these
> destroys don't cost anything. On the contrary with if they would
> be destroyed in txn_free() which is called always.
>
> In short: the patch does not affect the common path when the
> triggers are not used.
>
> Another option was to force all the triggers clear themselves. For
> example, in case of commit all the on_commit triggers must clear
> themselves, and the rollback triggers are destroyed. Vice versa
> when a rollback happens. But it didn't work because the Lua
> triggers all work via a common runner lbox_trigger_run(), which
> can't destroy its argument in most of the cases (space:on_replace,
> :before_replace, ...).

Hi! Thanks for  the patch!
Please find some comments below.

>
> Closes #6025
> ---
> Branch: http://github.com/tarantool/tarantool/tree/gerold103/gh-6025-box.on_commit-leak
> Issue: https://github.com/tarantool/tarantool/issues/6025
>
>   .../unreleased/gh-6025-box.on_commit-leak.md  |  3 +
>   src/box/alter.cc                              | 39 +++++++++++--
>   src/box/txn.c                                 | 26 +++++++--
>   src/box/txn_limbo.c                           |  2 +
>   src/box/vinyl.c                               |  2 +
>   .../gh-6025-box.on_commit-leak.test.lua       | 57 +++++++++++++++++++
>   6 files changed, 119 insertions(+), 10 deletions(-)
>   create mode 100644 changelogs/unreleased/gh-6025-box.on_commit-leak.md
>   create mode 100755 test/app-tap/gh-6025-box.on_commit-leak.test.lua
>

...

> diff --git a/src/box/txn.c b/src/box/txn.c
> index 03b39e0de..4dfe1ad82 100644
> --- a/src/box/txn.c
> +++ b/src/box/txn.c
> @@ -54,7 +54,7 @@ static int
>   txn_on_yield(struct trigger *trigger, void *event);
>   
>   static void
> -txn_run_rollback_triggers(struct txn *txn, struct rlist *triggers);
> +txn_run_rollback_triggers(struct txn *txn);
>   
>   static int
>   txn_add_redo(struct txn *txn, struct txn_stmt *stmt, struct request *request)
> @@ -150,7 +150,7 @@ txn_rollback_one_stmt(struct txn *txn, struct txn_stmt *stmt)
>   	if (txn->engine != NULL && stmt->space != NULL)
>   		engine_rollback_statement(txn->engine, txn, stmt);
>   	if (stmt->has_triggers)
> -		txn_run_rollback_triggers(txn, &stmt->on_rollback);
> +		txn_run_rollback_triggers(txn);
>   }


1. This piece ran stmt->on_rollback, and you run txn->on_rollback now.
    These are not the same, for example check txn_rollback_to_svp below.


>   
>   static void
> @@ -438,8 +438,9 @@ fail:

...

>   
> diff --git a/src/box/txn_limbo.c b/src/box/txn_limbo.c
> index c22bd6665..7d5ece81e 100644
> --- a/src/box/txn_limbo.c
> +++ b/src/box/txn_limbo.c

...

> diff --git a/src/box/vinyl.c b/src/box/vinyl.c
> index b53e97593..d381179d0 100644
> --- a/src/box/vinyl.c
> +++ b/src/box/vinyl.c
...


2. There's also applier_txn_rollback_cb() in applier.cc
    You should probably  clear it as  well.


> diff --git a/test/app-tap/gh-6025-box.on_commit-leak.test.lua b/test/app-tap/gh-6025-box.on_commit-leak.test.lua
> new file mode 100755
> index 000000000..ec4e8b099
> --- /dev/null
> +++ b/test/app-tap/gh-6025-box.on_commit-leak.test.lua

3. The test's cool, I wouldn't have come up with it myself.
    Shouldn't it be in box-tap though?

> @@ -0,0 +1,57 @@
> +#!/usr/bin/env tarantool
> +
> +--
> +-- gh-6025: box.on_commit() and box.on_rollback() triggers always leaked.
> +--
> +
> +local tap = require('tap')
> +
> +--
> +-- Create a functional reference at the passed value. Not at the variable
> +-- keeping it in the caller, but at the value itself.
> +--
> +local function wrap_value(value)
> +    return function()
> +        assert(value == nil or value ~= nil)
> +    end
> +end
> +
> +local function test_on_commit_rollback(test)
> +    test:plan(2)
> +
> +    local s = box.schema.create_space('test')
> +    s:create_index('pk')
> +    local weak_ref = setmetatable({}, {__mode = 'v'})
> +
> +    -- If the triggers are deleted, the wrapped value reference must disappear
> +    -- and nothing should keep the value from collection from the weak table.
> +    local value = {}
> +    weak_ref[1] = value
> +    box.begin()
> +    s:replace{1}
> +    box.on_commit(wrap_value(value))
> +    box.on_rollback(wrap_value(value))
> +    box.commit()
> +    value = nil
> +    collectgarbage()
> +    test:isnil(weak_ref[1], 'on commit both triggers are deleted')
> +
> +    value = {}
> +    weak_ref[1] = value
> +    box.begin()
> +    s:replace{2}
> +    box.on_commit(wrap_value(value))
> +    box.on_rollback(wrap_value(value))
> +    box.rollback()
> +    value = nil
> +    collectgarbage()
> +    test:isnil(weak_ref[1], 'on rollback both triggers are deleted')
> +end
> +
> +box.cfg{}
> +
> +local test = tap.test('gh-6025-box.on_commit-leak')
> +test:plan(1)
> +test:test('delete triggers on commit and rollback', test_on_commit_rollback)
> +
> +os.exit(test:check() and 0 or 1)

-- 
Serge Petrenko



More information about the Tarantool-patches mailing list