temporal-ruby 0.0.0 → 0.0.1.pre.pre1
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/README.md +19 -42
- data/lib/gen/temporal/api/command/v1/message_pb.rb +146 -0
- data/lib/gen/temporal/api/common/v1/message_pb.rb +67 -0
- data/lib/gen/temporal/api/enums/v1/command_type_pb.rb +35 -0
- data/lib/gen/temporal/api/enums/v1/common_pb.rb +34 -0
- data/lib/gen/temporal/api/enums/v1/event_type_pb.rb +62 -0
- data/lib/gen/temporal/api/enums/v1/failed_cause_pb.rb +60 -0
- data/lib/gen/temporal/api/enums/v1/namespace_pb.rb +31 -0
- data/lib/gen/temporal/api/enums/v1/query_pb.rb +31 -0
- data/lib/gen/temporal/api/enums/v1/task_queue_pb.rb +30 -0
- data/lib/gen/temporal/api/enums/v1/workflow_pb.rb +82 -0
- data/lib/gen/temporal/api/errordetails/v1/message_pb.rb +55 -0
- data/lib/gen/temporal/api/failure/v1/message_pb.rb +81 -0
- data/lib/gen/temporal/api/filter/v1/message_pb.rb +38 -0
- data/lib/gen/temporal/api/history/v1/message_pb.rb +423 -0
- data/lib/gen/temporal/api/namespace/v1/message_pb.rb +55 -0
- data/lib/gen/temporal/api/query/v1/message_pb.rb +36 -0
- data/lib/gen/temporal/api/replication/v1/message_pb.rb +27 -0
- data/lib/gen/temporal/api/taskqueue/v1/message_pb.rb +60 -0
- data/lib/gen/temporal/api/version/v1/message_pb.rb +28 -0
- data/lib/gen/temporal/api/workflow/v1/message_pb.rb +83 -0
- data/lib/gen/temporal/api/workflowservice/v1/request_response_pb.rb +538 -0
- data/lib/gen/temporal/api/workflowservice/v1/service_pb.rb +19 -0
- data/lib/gen/temporal/api/workflowservice/v1/service_services_pb.rb +223 -0
- data/lib/temporal-ruby.rb +1 -0
- data/lib/temporal.rb +137 -0
- data/lib/temporal/activity.rb +33 -0
- data/lib/temporal/activity/async_token.rb +34 -0
- data/lib/temporal/activity/context.rb +64 -0
- data/lib/temporal/activity/poller.rb +79 -0
- data/lib/temporal/activity/task_processor.rb +78 -0
- data/lib/temporal/activity/workflow_convenience_methods.rb +41 -0
- data/lib/temporal/client.rb +21 -0
- data/lib/temporal/client/errors.rb +8 -0
- data/lib/temporal/client/grpc_client.rb +345 -0
- data/lib/temporal/client/serializer.rb +31 -0
- data/lib/temporal/client/serializer/base.rb +23 -0
- data/lib/temporal/client/serializer/cancel_timer.rb +19 -0
- data/lib/temporal/client/serializer/complete_workflow.rb +20 -0
- data/lib/temporal/client/serializer/fail_workflow.rb +20 -0
- data/lib/temporal/client/serializer/failure.rb +29 -0
- data/lib/temporal/client/serializer/payload.rb +25 -0
- data/lib/temporal/client/serializer/record_marker.rb +23 -0
- data/lib/temporal/client/serializer/request_activity_cancellation.rb +19 -0
- data/lib/temporal/client/serializer/schedule_activity.rb +53 -0
- data/lib/temporal/client/serializer/start_child_workflow.rb +51 -0
- data/lib/temporal/client/serializer/start_timer.rb +20 -0
- data/lib/temporal/concerns/executable.rb +37 -0
- data/lib/temporal/concerns/typed.rb +40 -0
- data/lib/temporal/configuration.rb +44 -0
- data/lib/temporal/errors.rb +38 -0
- data/lib/temporal/executable_lookup.rb +25 -0
- data/lib/temporal/execution_options.rb +35 -0
- data/lib/temporal/json.rb +18 -0
- data/lib/temporal/metadata.rb +68 -0
- data/lib/temporal/metadata/activity.rb +27 -0
- data/lib/temporal/metadata/base.rb +17 -0
- data/lib/temporal/metadata/workflow.rb +22 -0
- data/lib/temporal/metadata/workflow_task.rb +25 -0
- data/lib/temporal/metrics.rb +37 -0
- data/lib/temporal/metrics_adapters/log.rb +33 -0
- data/lib/temporal/metrics_adapters/null.rb +9 -0
- data/lib/temporal/middleware/chain.rb +30 -0
- data/lib/temporal/middleware/entry.rb +9 -0
- data/lib/temporal/retry_policy.rb +27 -0
- data/lib/temporal/saga/concern.rb +23 -0
- data/lib/temporal/saga/result.rb +22 -0
- data/lib/temporal/saga/saga.rb +24 -0
- data/lib/temporal/testing.rb +50 -0
- data/lib/temporal/testing/future_registry.rb +27 -0
- data/lib/temporal/testing/local_activity_context.rb +17 -0
- data/lib/temporal/testing/local_workflow_context.rb +178 -0
- data/lib/temporal/testing/temporal_override.rb +121 -0
- data/lib/temporal/testing/workflow_execution.rb +44 -0
- data/lib/temporal/testing/workflow_override.rb +36 -0
- data/lib/temporal/thread_local_context.rb +14 -0
- data/lib/temporal/thread_pool.rb +63 -0
- data/lib/temporal/types.rb +7 -0
- data/lib/temporal/uuid.rb +19 -0
- data/lib/temporal/version.rb +1 -1
- data/lib/temporal/worker.rb +88 -0
- data/lib/temporal/workflow.rb +42 -0
- data/lib/temporal/workflow/command.rb +39 -0
- data/lib/temporal/workflow/command_state_machine.rb +48 -0
- data/lib/temporal/workflow/context.rb +243 -0
- data/lib/temporal/workflow/convenience_methods.rb +34 -0
- data/lib/temporal/workflow/dispatcher.rb +31 -0
- data/lib/temporal/workflow/execution_info.rb +51 -0
- data/lib/temporal/workflow/executor.rb +45 -0
- data/lib/temporal/workflow/future.rb +77 -0
- data/lib/temporal/workflow/history.rb +76 -0
- data/lib/temporal/workflow/history/event.rb +69 -0
- data/lib/temporal/workflow/history/event_target.rb +75 -0
- data/lib/temporal/workflow/history/window.rb +40 -0
- data/lib/temporal/workflow/poller.rb +67 -0
- data/lib/temporal/workflow/replay_aware_logger.rb +36 -0
- data/lib/temporal/workflow/state_manager.rb +342 -0
- data/lib/temporal/workflow/task_processor.rb +78 -0
- data/rbi/temporal-ruby.rbi +43 -0
- data/temporal.gemspec +10 -2
- metadata +186 -6
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Temporal
|
|
2
|
+
# Superclass for all Temporal errors
|
|
3
|
+
class Error < StandardError; end
|
|
4
|
+
|
|
5
|
+
# Superclass for errors specific to Temporal worker itself
|
|
6
|
+
class InternalError < Error; end
|
|
7
|
+
|
|
8
|
+
# Superclass for misconfiguration/misuse on the client (user) side
|
|
9
|
+
class ClientError < Error; end
|
|
10
|
+
|
|
11
|
+
# Represents any timeout
|
|
12
|
+
class TimeoutError < ClientError; end
|
|
13
|
+
|
|
14
|
+
# A superclass for activity exceptions raised explicitly
|
|
15
|
+
# with the intent to propagate to a workflow
|
|
16
|
+
class ActivityException < ClientError; end
|
|
17
|
+
|
|
18
|
+
class ActivityNotRegistered < ClientError; end
|
|
19
|
+
class WorkflowNotRegistered < ClientError; end
|
|
20
|
+
|
|
21
|
+
class ApiError < Error; end
|
|
22
|
+
|
|
23
|
+
class NotFoundFailure < ApiError; end
|
|
24
|
+
class WorkflowExecutionAlreadyStartedFailure < ApiError
|
|
25
|
+
attr_reader :run_id
|
|
26
|
+
|
|
27
|
+
def initialize(message, run_id)
|
|
28
|
+
super(message)
|
|
29
|
+
@run_id = run_id
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
class NamespaceNotActiveFailure < ApiError; end
|
|
33
|
+
class ClientVersionNotSupportedFailure < ApiError; end
|
|
34
|
+
class FeatureVersionNotSupportedFailure < ApiError; end
|
|
35
|
+
class NamespaceAlreadyExistsFailure < ApiError; end
|
|
36
|
+
class CancellationAlreadyRequestedFailure < ApiError; end
|
|
37
|
+
class QueryFailedFailure < ApiError; end
|
|
38
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# This class is responsible for matching an executable (activity or workflow) name
|
|
2
|
+
# to a class implementing it.
|
|
3
|
+
#
|
|
4
|
+
# TODO: This class should be responsible for handling executable versions
|
|
5
|
+
# when these are implemented
|
|
6
|
+
#
|
|
7
|
+
module Temporal
|
|
8
|
+
class ExecutableLookup
|
|
9
|
+
def initialize
|
|
10
|
+
@executables = {}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def add(name, executable)
|
|
14
|
+
executables[name] = executable
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def find(name)
|
|
18
|
+
executables[name]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
attr_reader :executables
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require 'temporal/concerns/executable'
|
|
2
|
+
|
|
3
|
+
module Temporal
|
|
4
|
+
class ExecutionOptions
|
|
5
|
+
attr_reader :name, :namespace, :task_queue, :retry_policy, :timeouts, :headers
|
|
6
|
+
|
|
7
|
+
def initialize(object, options = {})
|
|
8
|
+
@name = options[:name] || object.to_s
|
|
9
|
+
@namespace = options[:namespace]
|
|
10
|
+
@task_queue = options[:task_queue] || options[:task_list]
|
|
11
|
+
@retry_policy = options[:retry_policy]
|
|
12
|
+
@timeouts = options[:timeouts] || {}
|
|
13
|
+
@headers = options[:headers] || {}
|
|
14
|
+
|
|
15
|
+
if object.singleton_class.included_modules.include?(Concerns::Executable)
|
|
16
|
+
@namespace ||= object.namespace
|
|
17
|
+
@task_queue ||= object.task_queue
|
|
18
|
+
@retry_policy ||= object.retry_policy
|
|
19
|
+
@timeouts = object.timeouts.merge(@timeouts) if object.timeouts
|
|
20
|
+
@headers = object.headers.merge(@headers) if object.headers
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
@namespace ||= Temporal.configuration.namespace
|
|
24
|
+
@task_queue ||= Temporal.configuration.task_queue
|
|
25
|
+
@timeouts = Temporal.configuration.timeouts.merge(@timeouts)
|
|
26
|
+
@headers = Temporal.configuration.headers.merge(@headers)
|
|
27
|
+
|
|
28
|
+
freeze
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def task_list
|
|
32
|
+
@task_queue
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Helper class for serializing/deserializing JSON
|
|
2
|
+
require 'oj'
|
|
3
|
+
|
|
4
|
+
module Temporal
|
|
5
|
+
module JSON
|
|
6
|
+
OJ_OPTIONS = {
|
|
7
|
+
mode: :object
|
|
8
|
+
}.freeze
|
|
9
|
+
|
|
10
|
+
def self.serialize(value)
|
|
11
|
+
Oj.dump(value, OJ_OPTIONS)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.deserialize(value)
|
|
15
|
+
Oj.load(value.to_s, OJ_OPTIONS)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require 'temporal/errors'
|
|
2
|
+
require 'temporal/metadata/activity'
|
|
3
|
+
require 'temporal/metadata/workflow'
|
|
4
|
+
require 'temporal/metadata/workflow_task'
|
|
5
|
+
|
|
6
|
+
module Temporal
|
|
7
|
+
module Metadata
|
|
8
|
+
ACTIVITY_TYPE = :activity
|
|
9
|
+
WORKFLOW_TASK_TYPE = :workflow_task
|
|
10
|
+
WORKFLOW_TYPE = :workflow
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def generate(type, data, namespace = nil)
|
|
14
|
+
case type
|
|
15
|
+
when ACTIVITY_TYPE
|
|
16
|
+
activity_metadata_from(data, namespace)
|
|
17
|
+
when WORKFLOW_TASK_TYPE
|
|
18
|
+
workflow_task_metadata_from(data, namespace)
|
|
19
|
+
when WORKFLOW_TYPE
|
|
20
|
+
workflow_metadata_from(data)
|
|
21
|
+
else
|
|
22
|
+
raise InternalError, 'Unsupported metadata type'
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def headers(fields)
|
|
29
|
+
fields.transform_values { |v| v[:data] }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def activity_metadata_from(task, namespace)
|
|
33
|
+
Metadata::Activity.new(
|
|
34
|
+
namespace: namespace,
|
|
35
|
+
id: task.activity_id,
|
|
36
|
+
name: task.activity_type.name,
|
|
37
|
+
task_token: task.task_token,
|
|
38
|
+
attempt: task.attempt,
|
|
39
|
+
workflow_run_id: task.workflow_execution.run_id,
|
|
40
|
+
workflow_id: task.workflow_execution.workflow_id,
|
|
41
|
+
workflow_name: task.workflow_type.name,
|
|
42
|
+
headers: headers(task.header&.fields.to_h)
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def workflow_task_metadata_from(task, namespace)
|
|
47
|
+
Metadata::WorkflowTask.new(
|
|
48
|
+
namespace: namespace,
|
|
49
|
+
id: task.started_event_id,
|
|
50
|
+
task_token: task.task_token,
|
|
51
|
+
attempt: task.attempt,
|
|
52
|
+
workflow_run_id: task.workflow_execution.run_id,
|
|
53
|
+
workflow_id: task.workflow_execution.workflow_id,
|
|
54
|
+
workflow_name: task.workflow_type.name
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def workflow_metadata_from(event)
|
|
59
|
+
Metadata::Workflow.new(
|
|
60
|
+
name: event.workflow_type.name,
|
|
61
|
+
run_id: event.original_execution_run_id,
|
|
62
|
+
attempt: event.attempt,
|
|
63
|
+
headers: headers(event.header&.fields.to_h)
|
|
64
|
+
)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'temporal/metadata/base'
|
|
2
|
+
|
|
3
|
+
module Temporal
|
|
4
|
+
module Metadata
|
|
5
|
+
class Activity < Base
|
|
6
|
+
attr_reader :namespace, :id, :name, :task_token, :attempt, :workflow_run_id, :workflow_id, :workflow_name, :headers
|
|
7
|
+
|
|
8
|
+
def initialize(namespace:, id:, name:, task_token:, attempt:, workflow_run_id:, workflow_id:, workflow_name:, headers: {})
|
|
9
|
+
@namespace = namespace
|
|
10
|
+
@id = id
|
|
11
|
+
@name = name
|
|
12
|
+
@task_token = task_token
|
|
13
|
+
@attempt = attempt
|
|
14
|
+
@workflow_run_id = workflow_run_id
|
|
15
|
+
@workflow_id = workflow_id
|
|
16
|
+
@workflow_name = workflow_name
|
|
17
|
+
@headers = headers
|
|
18
|
+
|
|
19
|
+
freeze
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def activity?
|
|
23
|
+
true
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'temporal/metadata/base'
|
|
2
|
+
|
|
3
|
+
module Temporal
|
|
4
|
+
module Metadata
|
|
5
|
+
class Workflow < Base
|
|
6
|
+
attr_reader :name, :run_id, :attempt, :headers
|
|
7
|
+
|
|
8
|
+
def initialize(name:, run_id:, attempt:, headers: {})
|
|
9
|
+
@name = name
|
|
10
|
+
@run_id = run_id
|
|
11
|
+
@attempt = attempt
|
|
12
|
+
@headers = headers
|
|
13
|
+
|
|
14
|
+
freeze
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def workflow?
|
|
18
|
+
true
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'temporal/metadata/base'
|
|
2
|
+
|
|
3
|
+
module Temporal
|
|
4
|
+
module Metadata
|
|
5
|
+
class WorkflowTask < Base
|
|
6
|
+
attr_reader :namespace, :id, :task_token, :attempt, :workflow_run_id, :workflow_id, :workflow_name
|
|
7
|
+
|
|
8
|
+
def initialize(namespace:, id:, task_token:, attempt:, workflow_run_id:, workflow_id:, workflow_name:)
|
|
9
|
+
@namespace = namespace
|
|
10
|
+
@id = id
|
|
11
|
+
@task_token = task_token
|
|
12
|
+
@attempt = attempt
|
|
13
|
+
@workflow_run_id = workflow_run_id
|
|
14
|
+
@workflow_id = workflow_id
|
|
15
|
+
@workflow_name = workflow_name
|
|
16
|
+
|
|
17
|
+
freeze
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def workflow_task?
|
|
21
|
+
true
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Temporal
|
|
2
|
+
class Metrics
|
|
3
|
+
def initialize(adapter)
|
|
4
|
+
@adapter = adapter
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def increment(key, tags = {})
|
|
8
|
+
count(key, 1, tags)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def decrement(key, tags = {})
|
|
12
|
+
count(key, -1, tags)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def count(key, count, tags = {})
|
|
16
|
+
adapter.count(key, count, tags)
|
|
17
|
+
rescue StandardError => error
|
|
18
|
+
Temporal.logger.error("Adapter failed to send count metrics for #{key}: #{error.inspect}")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def gauge(key, value, tags = {})
|
|
22
|
+
adapter.gauge(key, value, tags)
|
|
23
|
+
rescue StandardError => error
|
|
24
|
+
Temporal.logger.error("Adapter failed to send gauge metrics for #{key}: #{error.inspect}")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def timing(key, time, tags = {})
|
|
28
|
+
adapter.timing(key, time, tags)
|
|
29
|
+
rescue StandardError => error
|
|
30
|
+
Temporal.logger.error("Adapter failed to send timing metrics for #{key}: #{error.inspect}")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
attr_reader :adapter
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Temporal
|
|
2
|
+
module MetricsAdapters
|
|
3
|
+
class Log
|
|
4
|
+
def initialize(logger)
|
|
5
|
+
@logger = logger
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def count(key, count, tags)
|
|
9
|
+
logger.debug(format_message(key, 'count', count, tags))
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def gauge(key, value, tags)
|
|
13
|
+
logger.debug(format_message(key, 'gauge', value, tags))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def timing(key, time, tags)
|
|
17
|
+
logger.debug(format_message(key, 'timing', time, tags))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
attr_reader :logger
|
|
23
|
+
|
|
24
|
+
def format_message(key, type, value, tags)
|
|
25
|
+
tags_str = tags.map { |k, v| "#{k}:#{v}" }.join(',')
|
|
26
|
+
parts = [key, type, value]
|
|
27
|
+
parts << tags_str if !tags_str.empty?
|
|
28
|
+
|
|
29
|
+
parts.join(' | ')
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Temporal
|
|
2
|
+
module Middleware
|
|
3
|
+
class Chain
|
|
4
|
+
def initialize(entries = [])
|
|
5
|
+
@middleware = entries.map(&:init_middleware)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def invoke(metadata)
|
|
9
|
+
result = nil
|
|
10
|
+
chain = middleware.dup
|
|
11
|
+
|
|
12
|
+
traverse_chain = lambda do
|
|
13
|
+
if chain.empty?
|
|
14
|
+
result = yield
|
|
15
|
+
else
|
|
16
|
+
chain.shift.call(metadata, &traverse_chain)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
traverse_chain.call
|
|
21
|
+
|
|
22
|
+
result
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
attr_reader :middleware
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'temporal/errors'
|
|
2
|
+
|
|
3
|
+
module Temporal
|
|
4
|
+
class RetryPolicy < Struct.new(:interval, :backoff, :max_interval, :max_attempts,
|
|
5
|
+
:expiration_interval, :non_retriable_errors, keyword_init: true)
|
|
6
|
+
|
|
7
|
+
class InvalidRetryPolicy < ClientError; end
|
|
8
|
+
|
|
9
|
+
def validate!
|
|
10
|
+
unless interval && backoff
|
|
11
|
+
raise InvalidRetryPolicy, 'interval and backoff must be set'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
unless max_attempts || expiration_interval
|
|
15
|
+
raise InvalidRetryPolicy, 'max_attempts or expiration_interval must be set'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
unless [interval, max_interval, expiration_interval].compact.all? { |arg| arg.is_a?(Integer) }
|
|
19
|
+
raise InvalidRetryPolicy, 'All intervals must be specified in whole seconds'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
unless [interval, max_interval, expiration_interval].compact.all? { |arg| arg > 0 }
|
|
23
|
+
raise InvalidRetryPolicy, 'All intervals must be greater than 0'
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require 'temporal/saga/saga'
|
|
2
|
+
require 'temporal/saga/result'
|
|
3
|
+
|
|
4
|
+
module Temporal
|
|
5
|
+
module Saga
|
|
6
|
+
module Concern
|
|
7
|
+
def run_saga(&block)
|
|
8
|
+
saga = Temporal::Saga::Saga.new(workflow)
|
|
9
|
+
|
|
10
|
+
block.call(saga)
|
|
11
|
+
|
|
12
|
+
Result.new(true)
|
|
13
|
+
rescue StandardError => error # TODO: is there a need for a specialized error here?
|
|
14
|
+
logger.error("Saga execution aborted: #{error.inspect}")
|
|
15
|
+
logger.debug(error.backtrace.join("\n"))
|
|
16
|
+
|
|
17
|
+
saga.compensate
|
|
18
|
+
|
|
19
|
+
Result.new(false, error)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|