temporal-ruby 0.0.1.pre.pre1 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +2 -0
- data/README.md +23 -3
- data/lib/gen/temporal/api/command/v1/message_pb.rb +1 -1
- data/lib/gen/temporal/api/enums/v1/common_pb.rb +7 -0
- data/lib/gen/temporal/api/errordetails/v1/message_pb.rb +5 -6
- data/lib/gen/temporal/api/version/v1/message_pb.rb +19 -8
- data/lib/gen/temporal/api/workflowservice/v1/request_response_pb.rb +19 -8
- data/lib/gen/temporal/api/workflowservice/v1/service_services_pb.rb +0 -3
- data/lib/temporal.rb +104 -0
- data/lib/temporal/activity/context.rb +5 -1
- data/lib/temporal/activity/poller.rb +26 -9
- data/lib/temporal/activity/task_processor.rb +33 -20
- data/lib/temporal/client/converter/base.rb +35 -0
- data/lib/temporal/client/converter/composite.rb +49 -0
- data/lib/temporal/client/converter/payload/bytes.rb +30 -0
- data/lib/temporal/client/converter/payload/json.rb +28 -0
- data/lib/temporal/client/converter/payload/nil.rb +27 -0
- data/lib/temporal/client/grpc_client.rb +102 -27
- data/lib/temporal/client/retryer.rb +49 -0
- data/lib/temporal/client/serializer.rb +2 -0
- data/lib/temporal/client/serializer/cancel_timer.rb +2 -2
- data/lib/temporal/client/serializer/complete_workflow.rb +6 -4
- data/lib/temporal/client/serializer/continue_as_new.rb +37 -0
- data/lib/temporal/client/serializer/fail_workflow.rb +2 -2
- data/lib/temporal/client/serializer/failure.rb +4 -2
- data/lib/temporal/client/serializer/record_marker.rb +6 -4
- data/lib/temporal/client/serializer/request_activity_cancellation.rb +2 -2
- data/lib/temporal/client/serializer/retry_policy.rb +24 -0
- data/lib/temporal/client/serializer/schedule_activity.rb +8 -20
- data/lib/temporal/client/serializer/start_child_workflow.rb +9 -20
- data/lib/temporal/client/serializer/start_timer.rb +2 -2
- data/lib/temporal/concerns/payloads.rb +51 -0
- data/lib/temporal/configuration.rb +31 -4
- data/lib/temporal/error_handler.rb +11 -0
- data/lib/temporal/errors.rb +24 -0
- data/lib/temporal/execution_options.rb +9 -1
- data/lib/temporal/json.rb +3 -1
- data/lib/temporal/logger.rb +17 -0
- data/lib/temporal/metadata.rb +11 -3
- data/lib/temporal/metadata/activity.rb +15 -2
- data/lib/temporal/metadata/workflow.rb +8 -0
- data/lib/temporal/metadata/workflow_task.rb +11 -0
- data/lib/temporal/retry_policy.rb +6 -9
- data/lib/temporal/saga/concern.rb +1 -1
- data/lib/temporal/testing.rb +1 -0
- data/lib/temporal/testing/future_registry.rb +1 -1
- data/lib/temporal/testing/local_activity_context.rb +1 -1
- data/lib/temporal/testing/local_workflow_context.rb +38 -14
- data/lib/temporal/testing/scheduled_workflows.rb +75 -0
- data/lib/temporal/testing/temporal_override.rb +35 -7
- data/lib/temporal/testing/workflow_override.rb +6 -1
- data/lib/temporal/version.rb +1 -1
- data/lib/temporal/worker.rb +28 -10
- data/lib/temporal/workflow.rb +8 -2
- data/lib/temporal/workflow/command.rb +3 -0
- data/lib/temporal/workflow/context.rb +40 -5
- data/lib/temporal/workflow/errors.rb +39 -0
- data/lib/temporal/workflow/executor.rb +1 -1
- data/lib/temporal/workflow/future.rb +18 -6
- data/lib/temporal/workflow/history/event.rb +1 -3
- data/lib/temporal/workflow/history/event_target.rb +4 -0
- data/lib/temporal/workflow/history/window.rb +1 -1
- data/lib/temporal/workflow/poller.rb +41 -13
- data/lib/temporal/workflow/replay_aware_logger.rb +4 -4
- data/lib/temporal/workflow/state_manager.rb +33 -52
- data/lib/temporal/workflow/task_processor.rb +41 -11
- metadata +21 -9
- data/lib/temporal/client/serializer/payload.rb +0 -25
@@ -1,16 +1,23 @@
|
|
1
1
|
require 'temporal/client'
|
2
|
+
require 'temporal/thread_pool'
|
2
3
|
require 'temporal/middleware/chain'
|
3
4
|
require 'temporal/workflow/task_processor'
|
5
|
+
require 'temporal/error_handler'
|
4
6
|
|
5
7
|
module Temporal
|
6
8
|
class Workflow
|
7
9
|
class Poller
|
8
|
-
|
10
|
+
DEFAULT_OPTIONS = {
|
11
|
+
thread_pool_size: 10
|
12
|
+
}.freeze
|
13
|
+
|
14
|
+
def initialize(namespace, task_queue, workflow_lookup, middleware = [], options = {})
|
9
15
|
@namespace = namespace
|
10
16
|
@task_queue = task_queue
|
11
17
|
@workflow_lookup = workflow_lookup
|
12
18
|
@middleware = middleware
|
13
19
|
@shutting_down = false
|
20
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
14
21
|
end
|
15
22
|
|
16
23
|
def start
|
@@ -18,50 +25,71 @@ module Temporal
|
|
18
25
|
@thread = Thread.new(&method(:poll_loop))
|
19
26
|
end
|
20
27
|
|
21
|
-
def
|
28
|
+
def stop_polling
|
22
29
|
@shutting_down = true
|
23
|
-
|
30
|
+
Temporal.logger.info('Shutting down a workflow poller')
|
31
|
+
end
|
32
|
+
|
33
|
+
def cancel_pending_requests
|
34
|
+
client.cancel_polling_request
|
24
35
|
end
|
25
36
|
|
26
37
|
def wait
|
27
|
-
|
38
|
+
thread.join
|
39
|
+
thread_pool.shutdown
|
28
40
|
end
|
29
41
|
|
30
42
|
private
|
31
43
|
|
32
|
-
attr_reader :namespace, :task_queue, :
|
44
|
+
attr_reader :namespace, :task_queue, :workflow_lookup, :middleware, :options, :thread
|
33
45
|
|
34
46
|
def client
|
35
47
|
@client ||= Temporal::Client.generate
|
36
48
|
end
|
37
49
|
|
38
|
-
def middleware_chain
|
39
|
-
@middleware_chain ||= Middleware::Chain.new(middleware)
|
40
|
-
end
|
41
|
-
|
42
50
|
def shutting_down?
|
43
51
|
@shutting_down
|
44
52
|
end
|
45
53
|
|
46
54
|
def poll_loop
|
47
|
-
|
48
|
-
|
55
|
+
last_poll_time = Time.now
|
56
|
+
metrics_tags = { namespace: namespace, task_queue: task_queue }.freeze
|
57
|
+
|
58
|
+
loop do
|
59
|
+
thread_pool.wait_for_available_threads
|
60
|
+
|
61
|
+
return if shutting_down?
|
62
|
+
|
63
|
+
time_diff_ms = ((Time.now - last_poll_time) * 1000).round
|
64
|
+
Temporal.metrics.timing('workflow_poller.time_since_last_poll', time_diff_ms, metrics_tags)
|
65
|
+
Temporal.logger.debug("Polling Worklow task queue", { namespace: namespace, task_queue: task_queue })
|
49
66
|
|
50
67
|
task = poll_for_task
|
51
|
-
|
68
|
+
last_poll_time = Time.now
|
69
|
+
next unless task&.workflow_type
|
70
|
+
|
71
|
+
thread_pool.schedule { process(task) }
|
52
72
|
end
|
53
73
|
end
|
54
74
|
|
55
75
|
def poll_for_task
|
56
76
|
client.poll_workflow_task_queue(namespace: namespace, task_queue: task_queue)
|
57
77
|
rescue StandardError => error
|
58
|
-
Temporal.logger.error("Unable to poll
|
78
|
+
Temporal.logger.error("Unable to poll Workflow task queue", { namespace: namespace, task_queue: task_queue, error: error.inspect })
|
79
|
+
Temporal::ErrorHandler.handle(error)
|
80
|
+
|
59
81
|
nil
|
60
82
|
end
|
61
83
|
|
62
84
|
def process(task)
|
85
|
+
middleware_chain = Middleware::Chain.new(middleware)
|
86
|
+
|
63
87
|
TaskProcessor.new(task, namespace, workflow_lookup, client, middleware_chain).process
|
64
88
|
end
|
89
|
+
|
90
|
+
def thread_pool
|
91
|
+
@thread_pool ||= ThreadPool.new(options[:thread_pool_size])
|
92
|
+
end
|
65
93
|
end
|
66
94
|
end
|
67
95
|
end
|
@@ -11,17 +11,17 @@ module Temporal
|
|
11
11
|
end
|
12
12
|
|
13
13
|
SEVERITIES.each do |severity|
|
14
|
-
define_method severity do |message|
|
14
|
+
define_method severity do |message, data = {}|
|
15
15
|
return if replay?
|
16
16
|
|
17
|
-
main_logger.public_send(severity, message)
|
17
|
+
main_logger.public_send(severity, message, data)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
def log(severity, message)
|
21
|
+
def log(severity, message, data = {})
|
22
22
|
return if replay?
|
23
23
|
|
24
|
-
main_logger.log(severity, message)
|
24
|
+
main_logger.log(severity, message, data)
|
25
25
|
end
|
26
26
|
|
27
27
|
private
|
@@ -1,13 +1,17 @@
|
|
1
|
-
require '
|
1
|
+
require 'set'
|
2
2
|
require 'temporal/errors'
|
3
3
|
require 'temporal/workflow/command'
|
4
4
|
require 'temporal/workflow/command_state_machine'
|
5
5
|
require 'temporal/workflow/history/event_target'
|
6
6
|
require 'temporal/metadata'
|
7
|
+
require 'temporal/concerns/payloads'
|
8
|
+
require 'temporal/workflow/errors'
|
7
9
|
|
8
10
|
module Temporal
|
9
11
|
class Workflow
|
10
12
|
class StateManager
|
13
|
+
include Concerns::Payloads
|
14
|
+
|
11
15
|
SIDE_EFFECT_MARKER = 'SIDE_EFFECT'.freeze
|
12
16
|
RELEASE_MARKER = 'RELEASE'.freeze
|
13
17
|
|
@@ -101,7 +105,7 @@ module Temporal
|
|
101
105
|
dispatch(
|
102
106
|
History::EventTarget.workflow,
|
103
107
|
'started',
|
104
|
-
|
108
|
+
from_payloads(event.attributes.input),
|
105
109
|
Metadata.generate(Metadata::WORKFLOW_TYPE, event.attributes)
|
106
110
|
)
|
107
111
|
|
@@ -131,26 +135,26 @@ module Temporal
|
|
131
135
|
|
132
136
|
when 'ACTIVITY_TASK_SCHEDULED'
|
133
137
|
state_machine.schedule
|
134
|
-
discard_command(
|
138
|
+
discard_command(target)
|
135
139
|
|
136
140
|
when 'ACTIVITY_TASK_STARTED'
|
137
141
|
state_machine.start
|
138
142
|
|
139
143
|
when 'ACTIVITY_TASK_COMPLETED'
|
140
144
|
state_machine.complete
|
141
|
-
dispatch(target, 'completed',
|
145
|
+
dispatch(target, 'completed', from_result_payloads(event.attributes.result))
|
142
146
|
|
143
147
|
when 'ACTIVITY_TASK_FAILED'
|
144
148
|
state_machine.fail
|
145
|
-
dispatch(target, 'failed',
|
149
|
+
dispatch(target, 'failed', Temporal::Workflow::Errors.generate_error(event.attributes.failure, ActivityException))
|
146
150
|
|
147
151
|
when 'ACTIVITY_TASK_TIMED_OUT'
|
148
152
|
state_machine.time_out
|
149
|
-
dispatch(target, 'failed',
|
153
|
+
dispatch(target, 'failed', Temporal::Workflow::Errors.generate_error(event.attributes.failure))
|
150
154
|
|
151
155
|
when 'ACTIVITY_TASK_CANCEL_REQUESTED'
|
152
156
|
state_machine.requested
|
153
|
-
discard_command(
|
157
|
+
discard_command(target)
|
154
158
|
|
155
159
|
when 'REQUEST_CANCEL_ACTIVITY_TASK_FAILED'
|
156
160
|
state_machine.fail
|
@@ -158,11 +162,11 @@ module Temporal
|
|
158
162
|
|
159
163
|
when 'ACTIVITY_TASK_CANCELED'
|
160
164
|
state_machine.cancel
|
161
|
-
dispatch(target, 'failed',
|
165
|
+
dispatch(target, 'failed', Temporal::Workflow::Errors.generate_error(event.attributes.failure))
|
162
166
|
|
163
167
|
when 'TIMER_STARTED'
|
164
168
|
state_machine.start
|
165
|
-
discard_command(
|
169
|
+
discard_command(target)
|
166
170
|
|
167
171
|
when 'TIMER_FIRED'
|
168
172
|
state_machine.complete
|
@@ -193,10 +197,10 @@ module Temporal
|
|
193
197
|
|
194
198
|
when 'MARKER_RECORDED'
|
195
199
|
state_machine.complete
|
196
|
-
handle_marker(event.id, event.attributes.marker_name,
|
200
|
+
handle_marker(event.id, event.attributes.marker_name, from_details_payloads(event.attributes.details['data']))
|
197
201
|
|
198
202
|
when 'WORKFLOW_EXECUTION_SIGNALED'
|
199
|
-
dispatch(target, 'signaled', event.attributes.signal_name,
|
203
|
+
dispatch(target, 'signaled', event.attributes.signal_name, from_signal_payloads(event.attributes.input))
|
200
204
|
|
201
205
|
when 'WORKFLOW_EXECUTION_TERMINATED'
|
202
206
|
# todo
|
@@ -206,30 +210,30 @@ module Temporal
|
|
206
210
|
|
207
211
|
when 'START_CHILD_WORKFLOW_EXECUTION_INITIATED'
|
208
212
|
state_machine.schedule
|
209
|
-
discard_command(
|
213
|
+
discard_command(target)
|
210
214
|
|
211
215
|
when 'START_CHILD_WORKFLOW_EXECUTION_FAILED'
|
212
216
|
state_machine.fail
|
213
|
-
dispatch(target, 'failed', 'StandardError',
|
217
|
+
dispatch(target, 'failed', 'StandardError', from_payloads(event.attributes.cause))
|
214
218
|
|
215
219
|
when 'CHILD_WORKFLOW_EXECUTION_STARTED'
|
216
220
|
state_machine.start
|
217
221
|
|
218
222
|
when 'CHILD_WORKFLOW_EXECUTION_COMPLETED'
|
219
223
|
state_machine.complete
|
220
|
-
dispatch(target, 'completed',
|
224
|
+
dispatch(target, 'completed', from_result_payloads(event.attributes.result))
|
221
225
|
|
222
226
|
when 'CHILD_WORKFLOW_EXECUTION_FAILED'
|
223
227
|
state_machine.fail
|
224
|
-
dispatch(target, 'failed',
|
228
|
+
dispatch(target, 'failed', Temporal::Workflow::Errors.generate_error(event.attributes.failure))
|
225
229
|
|
226
230
|
when 'CHILD_WORKFLOW_EXECUTION_CANCELED'
|
227
231
|
state_machine.cancel
|
228
|
-
dispatch(target, 'failed',
|
232
|
+
dispatch(target, 'failed', Temporal::Workflow::Errors.generate_error(event.attributes.failure))
|
229
233
|
|
230
234
|
when 'CHILD_WORKFLOW_EXECUTION_TIMED_OUT'
|
231
235
|
state_machine.time_out
|
232
|
-
dispatch(target, 'failed',
|
236
|
+
dispatch(target, 'failed', Temporal::Workflow::Errors.generate_error(event.attributes.failure))
|
233
237
|
|
234
238
|
when 'CHILD_WORKFLOW_EXECUTION_TERMINATED'
|
235
239
|
# todo
|
@@ -277,8 +281,18 @@ module Temporal
|
|
277
281
|
dispatcher.dispatch(target, name, attributes)
|
278
282
|
end
|
279
283
|
|
280
|
-
def discard_command(
|
281
|
-
|
284
|
+
def discard_command(target)
|
285
|
+
# Pop the first command from the list, it is expected to match
|
286
|
+
existing_command_id, existing_command = commands.shift
|
287
|
+
|
288
|
+
if !existing_command_id
|
289
|
+
raise NonDeterministicWorkflowError, "A command #{target} was not scheduled upon replay"
|
290
|
+
end
|
291
|
+
|
292
|
+
existing_target = event_target_from(existing_command_id, existing_command)
|
293
|
+
if target != existing_target
|
294
|
+
raise NonDeterministicWorkflowError, "Unexpected command #{existing_target} (expected #{target})"
|
295
|
+
end
|
282
296
|
end
|
283
297
|
|
284
298
|
def handle_marker(id, type, details)
|
@@ -304,39 +318,6 @@ module Temporal
|
|
304
318
|
end
|
305
319
|
end
|
306
320
|
|
307
|
-
def parse_payload(payload)
|
308
|
-
return if payload.nil? || payload.payloads.empty?
|
309
|
-
|
310
|
-
binary = payload.payloads.first.data
|
311
|
-
JSON.deserialize(binary)
|
312
|
-
end
|
313
|
-
|
314
|
-
def parse_failure(failure, default_exception_class = StandardError)
|
315
|
-
case failure.failure_info
|
316
|
-
when :application_failure_info
|
317
|
-
exception_class = safe_constantize(failure.application_failure_info.type)
|
318
|
-
exception_class ||= default_exception_class
|
319
|
-
details = parse_payload(failure.application_failure_info.details)
|
320
|
-
backtrace = failure.stack_trace.split("\n")
|
321
|
-
|
322
|
-
exception_class.new(details).tap do |exception|
|
323
|
-
exception.set_backtrace(backtrace) if !backtrace.empty?
|
324
|
-
end
|
325
|
-
when :timeout_failure_info
|
326
|
-
TimeoutError.new("Timeout type: #{failure.timeout_failure_info.timeout_type.to_s}")
|
327
|
-
when :canceled_failure_info
|
328
|
-
# TODO: Distinguish between different entity cancellations
|
329
|
-
StandardError.new(parse_payload(failure.canceled_failure_info.details))
|
330
|
-
else
|
331
|
-
StandardError.new(failure.message)
|
332
|
-
end
|
333
|
-
end
|
334
|
-
|
335
|
-
def safe_constantize(const)
|
336
|
-
Object.const_get(const) if Object.const_defined?(const)
|
337
|
-
rescue NameError
|
338
|
-
nil
|
339
|
-
end
|
340
321
|
end
|
341
322
|
end
|
342
323
|
end
|
@@ -1,14 +1,18 @@
|
|
1
1
|
require 'temporal/workflow/executor'
|
2
2
|
require 'temporal/workflow/history'
|
3
3
|
require 'temporal/metadata'
|
4
|
+
require 'temporal/error_handler'
|
4
5
|
require 'temporal/errors'
|
5
6
|
|
6
7
|
module Temporal
|
7
8
|
class Workflow
|
8
9
|
class TaskProcessor
|
10
|
+
MAX_FAILED_ATTEMPTS = 1
|
11
|
+
|
9
12
|
def initialize(task, namespace, workflow_lookup, client, middleware_chain)
|
10
13
|
@task = task
|
11
14
|
@namespace = namespace
|
15
|
+
@metadata = Metadata.generate(Metadata::WORKFLOW_TASK_TYPE, task, namespace)
|
12
16
|
@task_token = task.task_token
|
13
17
|
@workflow_name = task.workflow_type.name
|
14
18
|
@workflow_class = workflow_lookup.find(workflow_name)
|
@@ -19,37 +23,35 @@ module Temporal
|
|
19
23
|
def process
|
20
24
|
start_time = Time.now
|
21
25
|
|
22
|
-
Temporal.logger.
|
26
|
+
Temporal.logger.debug("Processing Workflow task", metadata.to_h)
|
23
27
|
Temporal.metrics.timing('workflow_task.queue_time', queue_time_ms, workflow: workflow_name)
|
24
28
|
|
25
29
|
if !workflow_class
|
26
30
|
raise Temporal::WorkflowNotRegistered, 'Workflow is not registered with this worker'
|
27
31
|
end
|
28
32
|
|
29
|
-
history =
|
33
|
+
history = fetch_full_history
|
30
34
|
# TODO: For sticky workflows we need to cache the Executor instance
|
31
35
|
executor = Workflow::Executor.new(workflow_class, history)
|
32
|
-
metadata = Metadata.generate(Metadata::WORKFLOW_TASK_TYPE, task, namespace)
|
33
36
|
|
34
37
|
commands = middleware_chain.invoke(metadata) do
|
35
38
|
executor.run
|
36
39
|
end
|
37
40
|
|
38
41
|
complete_task(commands)
|
39
|
-
rescue Temporal::ClientError => error
|
40
|
-
fail_task(error)
|
41
42
|
rescue StandardError => error
|
42
|
-
Temporal.
|
43
|
-
|
43
|
+
Temporal::ErrorHandler.handle(error, metadata: metadata)
|
44
|
+
|
45
|
+
fail_task(error)
|
44
46
|
ensure
|
45
47
|
time_diff_ms = ((Time.now - start_time) * 1000).round
|
46
48
|
Temporal.metrics.timing('workflow_task.latency', time_diff_ms, workflow: workflow_name)
|
47
|
-
Temporal.logger.debug("Workflow task processed
|
49
|
+
Temporal.logger.debug("Workflow task processed", metadata.to_h.merge(execution_time: time_diff_ms))
|
48
50
|
end
|
49
51
|
|
50
52
|
private
|
51
53
|
|
52
|
-
attr_reader :task, :namespace, :task_token, :workflow_name, :workflow_class, :client, :middleware_chain
|
54
|
+
attr_reader :task, :namespace, :task_token, :workflow_name, :workflow_class, :client, :middleware_chain, :metadata
|
53
55
|
|
54
56
|
def queue_time_ms
|
55
57
|
scheduled = task.scheduled_time.to_f
|
@@ -57,21 +59,49 @@ module Temporal
|
|
57
59
|
((started - scheduled) * 1_000).round
|
58
60
|
end
|
59
61
|
|
62
|
+
def fetch_full_history
|
63
|
+
events = task.history.events.to_a
|
64
|
+
next_page_token = task.next_page_token
|
65
|
+
|
66
|
+
while !next_page_token.empty? do
|
67
|
+
response = client.get_workflow_execution_history(
|
68
|
+
namespace: namespace,
|
69
|
+
workflow_id: task.workflow_execution.workflow_id,
|
70
|
+
run_id: task.workflow_execution.run_id,
|
71
|
+
next_page_token: next_page_token
|
72
|
+
)
|
73
|
+
|
74
|
+
events += response.history.events.to_a
|
75
|
+
next_page_token = response.next_page_token
|
76
|
+
end
|
77
|
+
|
78
|
+
Workflow::History.new(events)
|
79
|
+
end
|
80
|
+
|
60
81
|
def complete_task(commands)
|
61
|
-
Temporal.logger.info("Workflow task
|
82
|
+
Temporal.logger.info("Workflow task completed", metadata.to_h)
|
62
83
|
|
63
84
|
client.respond_workflow_task_completed(task_token: task_token, commands: commands)
|
64
85
|
end
|
65
86
|
|
66
87
|
def fail_task(error)
|
67
|
-
Temporal.logger.error("Workflow task
|
88
|
+
Temporal.logger.error("Workflow task failed", metadata.to_h.merge(error: error.inspect))
|
68
89
|
Temporal.logger.debug(error.backtrace.join("\n"))
|
69
90
|
|
91
|
+
# Only fail the workflow task on the first attempt. Subsequent failures of the same workflow task
|
92
|
+
# should timeout. This is to avoid spinning on the failed workflow task as the service doesn't
|
93
|
+
# yet exponentially backoff on retries.
|
94
|
+
return if task.attempt > MAX_FAILED_ATTEMPTS
|
95
|
+
|
70
96
|
client.respond_workflow_task_failed(
|
71
97
|
task_token: task_token,
|
72
98
|
cause: Temporal::Api::Enums::V1::WorkflowTaskFailedCause::WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_COMMAND,
|
73
99
|
exception: error
|
74
100
|
)
|
101
|
+
rescue StandardError => error
|
102
|
+
Temporal.logger.error("Unable to fail Workflow task", metadata.to_h.merge(error: error.inspect))
|
103
|
+
|
104
|
+
Temporal::ErrorHandler.handle(error, metadata: metadata)
|
75
105
|
end
|
76
106
|
end
|
77
107
|
end
|