Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion src/runner/workflow_state.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1171,12 +1171,27 @@ function methods:handle_process_exit(pid, success, result)
}
})

-- A process can die with any value the runtime hands back — a raw error,
-- userdata, a coroutine. The node.result row must always carry persistable
-- content: a value that binds SQL NULL poisons the whole completion batch
-- and strands the run, so anything that is not plain data persists as its
-- string form.
local result_content = result
local content_kind = type(result_content)
if content_kind ~= "string" and content_kind ~= "number"
and content_kind ~= "boolean" and content_kind ~= "table" then
result_content = result_content ~= nil and tostring(result_content) or nil
end
if result_content == nil then
result_content = success and "Completed" or "Failed"
end

table.insert(self.queued_commands, {
type = consts.COMMAND_TYPES.CREATE_DATA,
payload = {
data_id = result_data_id,
data_type = consts.DATA_TYPE.NODE_RESULT,
content = result or (success and "Completed" or "Failed"),
content = result_content,
node_id = exited_node_id,
discriminator = discriminator
}
Expand Down
42 changes: 42 additions & 0 deletions src/runner/workflow_state_test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,48 @@ local function define_tests()

test.is_nil(exit_info)
end)

-- A process can die with a raw error value (userdata, function —
-- anything the runtime hands back). The queued node.result must
-- always carry persistable, non-nil content: a row that binds SQL
-- NULL poisons the whole completion batch and strands the run.
it("persists a non-encodable terminal result as its string form", function()
local ws = workflow_state.new(test_ctx.dataflow_id) :: any
ws.nodes["node-1"] = { status = consts.STATUS.RUNNING, type = "test_node" }
ws:track_process("node-1", "pid-123")

local weird = coroutine.create(function() end)
local exit_info = ws:handle_process_exit("pid-123", false, weird) :: any
test.not_nil(exit_info)

local content = nil
for _, cmd in ipairs(ws.queued_commands) do
local p = (cmd :: any).payload or {}
if p.data_type == consts.DATA_TYPE.NODE_RESULT then content = p.content end
end
test.not_nil(content)
test.eq(type(content), "string")
end)

it("persists a table result whose values cannot encode as its string form", function()
local ws = workflow_state.new(test_ctx.dataflow_id) :: any
ws.nodes["node-1"] = { status = consts.STATUS.RUNNING, type = "test_node" }
ws:track_process("node-1", "pid-123")

local poison = { message = "boom", raw = coroutine.create(function() end) }
local exit_info = ws:handle_process_exit("pid-123", false, poison) :: any
test.not_nil(exit_info)

local content = nil
for _, cmd in ipairs(ws.queued_commands) do
local p = (cmd :: any).payload or {}
if p.data_type == consts.DATA_TYPE.NODE_RESULT then content = p.content end
end
test.not_nil(content)
local encoded, encode_err = json.encode(content)
test.is_nil(encode_err)
test.not_nil(encoded)
end)
end)

describe("Yield Tracking", function()
Expand Down