Skip to content

vshard.storage.call masks the original error thrown from a function in some cases #614

@sergepetrenko

Description

@sergepetrenko

Tarantool raises an error Transaction is active at return from function when a called over iproto function leaves an open transaction, but doesn't throw an error. This is the desired behavior, as it was discussed in tarantool/tarantool#7288. On the other hand, if the function raises an error, this error is returned to the user "as is".

vshard.storage.call calls the user function under a pcall, which means that if a user function raises an error, this error is not thrown out of vshard.storage.call. So if a user function throws an error before closing a transaction, the transaction will remain open and at the same time vshard.storage.call will catch this error, which will cause tarantool to raise Transaction is active at return from function instead.

Effectively, the original error will be hidden from the user and replaced with something else, without any traces of the original error location.

Example: apply the following diff to storage.lua in example cluster:

diff --git a/example/storage.lua b/example/storage.lua
index 05e5ef3..e76680e 100755
--- a/example/storage.lua
+++ b/example/storage.lua
@@ -119,9 +119,13 @@ function sleep(time)
 end
 
 function raise_luajit_error()
+    box.begin()
     assert(1 == 2)
+    box.commit()
 end
 
 function raise_client_error()
+    box.begin()
     box.error(box.error.UNKNOWN)
+    box.commit()
 end

Now try to call any of these functions from a router. The original error message will be replaced with Transaction is active at return from function:

unix/:./data/router_1.control> vshard.router.callrw(1, 'raise_client_error')
---
- null
- code: 30
  base_type: ClientError
  type: ClientError
  message: Transaction is active at return from function
  trace:
  - file: src/box/iproto.cc
    line: 2371
...

This is reproduced on vshard 0.1.37 and Tarantool 2.11.8, Tarantool 3.x isn't affected, at least I couldn't find a way to trigger the same error easily. This must be because of the way functions are called on 3.x compared to 2.x:

--
-- Invoke a function on this instance. Arguments are unpacked into the function
-- as arguments.
-- The function returns pcall() as is, because is used from places where
-- exceptions are not allowed.
--
local local_call
if util.version_is_at_least(3, 0, 0, 'beta', 1, 18) then
local_call = function(func_name, args)
return pcall(netbox_self_call, netbox_self, func_name, args)
end
else -- < 3.0.0-beta1-18
-- net_box.self.call() doesn't work with C stored and Lua persistent
-- functions before 3.0.0-beta1-18, so we try to call it via func.call
-- API prior to using net_box.self API.
local_call = function(func_name, args)
local func = box.func and box.func[func_name]
if not func then
return pcall(netbox_self_call, netbox_self, func_name, args)
end
return pcall(func.call, func, args)
end

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions