From 66978101aaa91b827246dd3a5b5ea36472a38bac Mon Sep 17 00:00:00 2001 From: kamenkremen Date: Thu, 18 Dec 2025 12:50:10 +0300 Subject: [PATCH] storage: preserve original error on function fail In Tarantool versions prior to 3.0.0-beta1-18, `net_box.self.call()` cannot invoke C stored or Lua persistent functions. Therefore, if such a function is registered in `box.func`, vshard executes it via `func.call` instead. If such a function raises an error during an uncommitted transaction, 'Transaction is active...' error is going to be raised, which will mask the original error. This patch fixes that bug by checking the function call result. If the function returns an error and a transaction is still active, vshard now explicitly rolls back the transaction. Fixes #614 NO_DOC=bugfix --- test/storage-luatest/storage_1_test.lua | 25 +++++++++++++++++++++++++ vshard/storage/init.lua | 16 +++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/test/storage-luatest/storage_1_test.lua b/test/storage-luatest/storage_1_test.lua index d84f25f7..4d671de7 100644 --- a/test/storage-luatest/storage_1_test.lua +++ b/test/storage-luatest/storage_1_test.lua @@ -674,3 +674,28 @@ test_group.test_info_disable_consistency = function(g) ilt.assert(res.is_enabled) end, {global_cfg}) end + +local function test_error_msg_is_preserved_template(g, add_to_schema) + g.replica_1_a:exec(function(add_to_schema) + rawset(_G, 'test_fail', function() + box.begin() + error('test_error') + end) + if add_to_schema then + box.schema.func.create('test_fail') + end + local status, err = ivshard.storage.call(1, 'read', 'test_fail', {}) + ilt.assert_not(status) + ilt.assert_str_contains(err.message, 'test_error') + ilt.assert_not_str_contains(err.message, 'Transaction is active') + rawset(_G, 'test_fail', nil) + if add_to_schema then + box.schema.func.drop('test_fail') + end + end, {add_to_schema}) +end + +test_group.test_error_msg_is_preserved = function(g) + test_error_msg_is_preserved_template(g, false) + test_error_msg_is_preserved_template(g, true) +end diff --git a/vshard/storage/init.lua b/vshard/storage/init.lua index 30951e09..f62d150f 100644 --- a/vshard/storage/init.lua +++ b/vshard/storage/init.lua @@ -260,6 +260,20 @@ else end end +-- +-- If the function is called directly, fails, and leaves an uncommitted +-- transaction, the original error is replaced by the "Transaction is active" +-- error. We manually check if the function returned an error. If so, we +-- rollback the transaction to reveal the original error. +-- +local function handle_results(status, ...) + if not status then + box.rollback() + return status, box.error.new(box.error.PROC_LUA, (...)) + end + return status, ... +end + -- -- Invoke a function on this instance. Arguments are unpacked into the function -- as arguments. @@ -284,7 +298,7 @@ local_call = function(func_name, args) if not func then return pcall(netbox_self_call, netbox_self, func_name, args) end - return pcall(func.call, func, args) + return handle_results(pcall(func.call, func, args)) end end