temporal-ruby 0.0.1.pre.pre1 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -2,18 +2,22 @@ require 'temporal/client'
|
|
2
2
|
require 'temporal/thread_pool'
|
3
3
|
require 'temporal/middleware/chain'
|
4
4
|
require 'temporal/activity/task_processor'
|
5
|
+
require 'temporal/error_handler'
|
5
6
|
|
6
7
|
module Temporal
|
7
8
|
class Activity
|
8
9
|
class Poller
|
9
|
-
|
10
|
+
DEFAULT_OPTIONS = {
|
11
|
+
thread_pool_size: 20
|
12
|
+
}.freeze
|
10
13
|
|
11
|
-
def initialize(namespace, task_queue, activity_lookup, middleware = [])
|
14
|
+
def initialize(namespace, task_queue, activity_lookup, middleware = [], options = {})
|
12
15
|
@namespace = namespace
|
13
16
|
@task_queue = task_queue
|
14
17
|
@activity_lookup = activity_lookup
|
15
18
|
@middleware = middleware
|
16
19
|
@shutting_down = false
|
20
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
17
21
|
end
|
18
22
|
|
19
23
|
def start
|
@@ -21,18 +25,23 @@ module Temporal
|
|
21
25
|
@thread = Thread.new(&method(:poll_loop))
|
22
26
|
end
|
23
27
|
|
24
|
-
def
|
28
|
+
def stop_polling
|
25
29
|
@shutting_down = true
|
26
|
-
|
30
|
+
Temporal.logger.info('Shutting down activity poller', { namespace: namespace, task_queue: task_queue })
|
31
|
+
end
|
32
|
+
|
33
|
+
def cancel_pending_requests
|
34
|
+
client.cancel_polling_request
|
27
35
|
end
|
28
36
|
|
29
37
|
def wait
|
30
38
|
thread.join
|
39
|
+
thread_pool.shutdown
|
31
40
|
end
|
32
41
|
|
33
42
|
private
|
34
43
|
|
35
|
-
attr_reader :namespace, :task_queue, :activity_lookup, :middleware, :thread
|
44
|
+
attr_reader :namespace, :task_queue, :activity_lookup, :middleware, :options, :thread
|
36
45
|
|
37
46
|
def client
|
38
47
|
@client ||= Temporal::Client.generate
|
@@ -43,14 +52,20 @@ module Temporal
|
|
43
52
|
end
|
44
53
|
|
45
54
|
def poll_loop
|
55
|
+
last_poll_time = Time.now
|
56
|
+
metrics_tags = { namespace: namespace, task_queue: task_queue }.freeze
|
57
|
+
|
46
58
|
loop do
|
47
59
|
thread_pool.wait_for_available_threads
|
48
60
|
|
49
61
|
return if shutting_down?
|
50
62
|
|
51
|
-
|
63
|
+
time_diff_ms = ((Time.now - last_poll_time) * 1000).round
|
64
|
+
Temporal.metrics.timing('activity_poller.time_since_last_poll', time_diff_ms, metrics_tags)
|
65
|
+
Temporal.logger.debug("Polling activity task queue", { namespace: namespace, task_queue: task_queue })
|
52
66
|
|
53
67
|
task = poll_for_task
|
68
|
+
last_poll_time = Time.now
|
54
69
|
next unless task&.activity_type
|
55
70
|
|
56
71
|
thread_pool.schedule { process(task) }
|
@@ -60,19 +75,21 @@ module Temporal
|
|
60
75
|
def poll_for_task
|
61
76
|
client.poll_activity_task_queue(namespace: namespace, task_queue: task_queue)
|
62
77
|
rescue StandardError => error
|
63
|
-
Temporal.logger.error("Unable to poll activity task queue:
|
78
|
+
Temporal.logger.error("Unable to poll activity task queue", { namespace: namespace, task_queue: task_queue, error: error.inspect })
|
79
|
+
|
80
|
+
Temporal::ErrorHandler.handle(error)
|
81
|
+
|
64
82
|
nil
|
65
83
|
end
|
66
84
|
|
67
85
|
def process(task)
|
68
|
-
client = Temporal::Client.generate
|
69
86
|
middleware_chain = Middleware::Chain.new(middleware)
|
70
87
|
|
71
88
|
TaskProcessor.new(task, namespace, activity_lookup, client, middleware_chain).process
|
72
89
|
end
|
73
90
|
|
74
91
|
def thread_pool
|
75
|
-
@thread_pool ||= ThreadPool.new(
|
92
|
+
@thread_pool ||= ThreadPool.new(options[:thread_pool_size])
|
76
93
|
end
|
77
94
|
end
|
78
95
|
end
|
@@ -1,14 +1,19 @@
|
|
1
1
|
require 'temporal/metadata'
|
2
|
+
require 'temporal/error_handler'
|
2
3
|
require 'temporal/errors'
|
3
4
|
require 'temporal/activity/context'
|
4
|
-
require 'temporal/
|
5
|
+
require 'temporal/concerns/payloads'
|
6
|
+
require 'temporal/client/retryer'
|
5
7
|
|
6
8
|
module Temporal
|
7
9
|
class Activity
|
8
10
|
class TaskProcessor
|
11
|
+
include Concerns::Payloads
|
12
|
+
|
9
13
|
def initialize(task, namespace, activity_lookup, client, middleware_chain)
|
10
14
|
@task = task
|
11
15
|
@namespace = namespace
|
16
|
+
@metadata = Metadata.generate(Metadata::ACTIVITY_TYPE, task, namespace)
|
12
17
|
@task_token = task.task_token
|
13
18
|
@activity_name = task.activity_type.name
|
14
19
|
@activity_class = activity_lookup.find(activity_name)
|
@@ -19,33 +24,34 @@ module Temporal
|
|
19
24
|
def process
|
20
25
|
start_time = Time.now
|
21
26
|
|
22
|
-
Temporal.logger.
|
27
|
+
Temporal.logger.debug("Processing Activity task", metadata.to_h)
|
23
28
|
Temporal.metrics.timing('activity_task.queue_time', queue_time_ms, activity: activity_name)
|
24
29
|
|
30
|
+
context = Activity::Context.new(client, metadata)
|
31
|
+
|
25
32
|
if !activity_class
|
26
33
|
raise ActivityNotRegistered, 'Activity is not registered with this worker'
|
27
34
|
end
|
28
35
|
|
29
|
-
metadata = Metadata.generate(Metadata::ACTIVITY_TYPE, task, namespace)
|
30
|
-
context = Activity::Context.new(client, metadata)
|
31
|
-
|
32
36
|
result = middleware_chain.invoke(metadata) do
|
33
|
-
activity_class.execute_in_context(context,
|
37
|
+
activity_class.execute_in_context(context, from_payloads(task.input))
|
34
38
|
end
|
35
39
|
|
36
40
|
# Do not complete asynchronous activities, these should be completed manually
|
37
41
|
respond_completed(result) unless context.async?
|
38
42
|
rescue StandardError, ScriptError => error
|
43
|
+
Temporal::ErrorHandler.handle(error, metadata: metadata)
|
44
|
+
|
39
45
|
respond_failed(error)
|
40
46
|
ensure
|
41
47
|
time_diff_ms = ((Time.now - start_time) * 1000).round
|
42
48
|
Temporal.metrics.timing('activity_task.latency', time_diff_ms, activity: activity_name)
|
43
|
-
Temporal.logger.debug("Activity task processed
|
49
|
+
Temporal.logger.debug("Activity task processed", metadata.to_h.merge(execution_time: time_diff_ms))
|
44
50
|
end
|
45
51
|
|
46
52
|
private
|
47
53
|
|
48
|
-
attr_reader :task, :namespace, :task_token, :activity_name, :activity_class, :client, :middleware_chain
|
54
|
+
attr_reader :task, :namespace, :task_token, :activity_name, :activity_class, :client, :middleware_chain, :metadata
|
49
55
|
|
50
56
|
def queue_time_ms
|
51
57
|
scheduled = task.current_attempt_scheduled_time.to_f
|
@@ -54,24 +60,31 @@ module Temporal
|
|
54
60
|
end
|
55
61
|
|
56
62
|
def respond_completed(result)
|
57
|
-
Temporal.logger.info("Activity
|
58
|
-
|
63
|
+
Temporal.logger.info("Activity task completed", metadata.to_h)
|
64
|
+
log_retry = proc do
|
65
|
+
Temporal.logger.debug("Failed to report activity task completion, retrying", metadata.to_h)
|
66
|
+
end
|
67
|
+
Temporal::Client::Retryer.with_retries(on_retry: log_retry) do
|
68
|
+
client.respond_activity_task_completed(task_token: task_token, result: result)
|
69
|
+
end
|
59
70
|
rescue StandardError => error
|
60
|
-
Temporal.logger.error("Unable to complete Activity
|
71
|
+
Temporal.logger.error("Unable to complete Activity", metadata.to_h.merge(error: error.inspect))
|
72
|
+
|
73
|
+
Temporal::ErrorHandler.handle(error, metadata: metadata)
|
61
74
|
end
|
62
75
|
|
63
76
|
def respond_failed(error)
|
64
|
-
Temporal.logger.error("Activity
|
65
|
-
|
77
|
+
Temporal.logger.error("Activity task failed", metadata.to_h.merge(error: error.inspect))
|
78
|
+
log_retry = proc do
|
79
|
+
Temporal.logger.debug("Failed to report activity task failure, retrying", metadata.to_h)
|
80
|
+
end
|
81
|
+
Temporal::Client::Retryer.with_retries(on_retry: log_retry) do
|
82
|
+
client.respond_activity_task_failed(task_token: task_token, exception: error)
|
83
|
+
end
|
66
84
|
rescue StandardError => error
|
67
|
-
Temporal.logger.error("Unable to fail Activity
|
68
|
-
end
|
69
|
-
|
70
|
-
def parse_payload(payload)
|
71
|
-
return if payload.nil? || payload.payloads.empty?
|
85
|
+
Temporal.logger.error("Unable to fail Activity task", metadata.to_h.merge(error: error.inspect))
|
72
86
|
|
73
|
-
|
74
|
-
JSON.deserialize(binary)
|
87
|
+
Temporal::ErrorHandler.handle(error, metadata: metadata)
|
75
88
|
end
|
76
89
|
end
|
77
90
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Temporal
|
2
|
+
module Client
|
3
|
+
module Converter
|
4
|
+
class Base
|
5
|
+
def initialize(payload_converter:)
|
6
|
+
@payload_converter = payload_converter
|
7
|
+
end
|
8
|
+
|
9
|
+
def from_payloads(payloads)
|
10
|
+
return nil if payloads.nil?
|
11
|
+
payloads.payloads.map(&method(:from_payload))
|
12
|
+
end
|
13
|
+
|
14
|
+
def from_payload(payload)
|
15
|
+
payload_converter.from_payload(payload)
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_payloads(data)
|
19
|
+
return nil if data.nil?
|
20
|
+
Temporal::Api::Common::V1::Payloads.new(
|
21
|
+
payloads: data.map(&method(:to_payload))
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_payload(data)
|
26
|
+
payload_converter.to_payload(data)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
attr_reader :payload_converter
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'temporal/client/converter/base'
|
2
|
+
|
3
|
+
module Temporal
|
4
|
+
module Client
|
5
|
+
module Converter
|
6
|
+
class Composite < Base
|
7
|
+
class ConverterNotFound < RuntimeError; end
|
8
|
+
class MetadataNotSet < RuntimeError; end
|
9
|
+
|
10
|
+
def initialize(payload_converters:)
|
11
|
+
@payload_converters = payload_converters
|
12
|
+
@payload_converters_by_encoding = {}
|
13
|
+
|
14
|
+
@payload_converters.each do |converter|
|
15
|
+
@payload_converters_by_encoding[converter.encoding] = converter
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def from_payload(payload)
|
20
|
+
encoding = payload.metadata['encoding']
|
21
|
+
if encoding.nil?
|
22
|
+
raise MetadataNotSet
|
23
|
+
end
|
24
|
+
|
25
|
+
converter = payload_converters_by_encoding[encoding]
|
26
|
+
|
27
|
+
if converter.nil?
|
28
|
+
raise ConverterNotFound
|
29
|
+
end
|
30
|
+
|
31
|
+
converter.from_payload(payload)
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_payload(data)
|
35
|
+
payload_converters.each do |converter|
|
36
|
+
payload = converter.to_payload(data)
|
37
|
+
return payload unless payload.nil?
|
38
|
+
end
|
39
|
+
|
40
|
+
raise ConverterNotFound
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
attr_reader :payload_converters, :payload_converters_by_encoding
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'temporal/json'
|
2
|
+
|
3
|
+
module Temporal
|
4
|
+
module Client
|
5
|
+
module Converter
|
6
|
+
module Payload
|
7
|
+
class Bytes
|
8
|
+
ENCODING = 'binary/plain'.freeze
|
9
|
+
|
10
|
+
def encoding
|
11
|
+
ENCODING
|
12
|
+
end
|
13
|
+
|
14
|
+
def from_payload(payload)
|
15
|
+
payload.data
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_payload(data)
|
19
|
+
return nil unless data.is_a?(String) && data.encoding == Encoding::ASCII_8BIT
|
20
|
+
|
21
|
+
Temporal::Api::Common::V1::Payload.new(
|
22
|
+
metadata: { 'encoding' => ENCODING },
|
23
|
+
data: data
|
24
|
+
)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'temporal/json'
|
2
|
+
|
3
|
+
module Temporal
|
4
|
+
module Client
|
5
|
+
module Converter
|
6
|
+
module Payload
|
7
|
+
class JSON
|
8
|
+
ENCODING = 'json/plain'.freeze
|
9
|
+
|
10
|
+
def encoding
|
11
|
+
ENCODING
|
12
|
+
end
|
13
|
+
|
14
|
+
def from_payload(payload)
|
15
|
+
Temporal::JSON.deserialize(payload.data)
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_payload(data)
|
19
|
+
Temporal::Api::Common::V1::Payload.new(
|
20
|
+
metadata: { 'encoding' => ENCODING },
|
21
|
+
data: Temporal::JSON.serialize(data).b
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Temporal
|
2
|
+
module Client
|
3
|
+
module Converter
|
4
|
+
module Payload
|
5
|
+
class Nil
|
6
|
+
ENCODING = 'binary/null'.freeze
|
7
|
+
|
8
|
+
def encoding
|
9
|
+
ENCODING
|
10
|
+
end
|
11
|
+
|
12
|
+
def from_payload(payload)
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_payload(data)
|
17
|
+
return nil unless data.nil?
|
18
|
+
|
19
|
+
Temporal::Api::Common::V1::Payload.new(
|
20
|
+
metadata: { 'encoding' => ENCODING }
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -1,30 +1,39 @@
|
|
1
1
|
require 'grpc'
|
2
2
|
require 'google/protobuf/well_known_types'
|
3
3
|
require 'securerandom'
|
4
|
-
require 'temporal/json'
|
5
4
|
require 'temporal/client/errors'
|
6
5
|
require 'temporal/client/serializer'
|
7
|
-
require 'temporal/client/serializer/payload'
|
8
6
|
require 'temporal/client/serializer/failure'
|
9
7
|
require 'gen/temporal/api/workflowservice/v1/service_services_pb'
|
8
|
+
require 'temporal/concerns/payloads'
|
10
9
|
|
11
10
|
module Temporal
|
12
11
|
module Client
|
13
12
|
class GRPCClient
|
13
|
+
include Concerns::Payloads
|
14
|
+
|
14
15
|
WORKFLOW_ID_REUSE_POLICY = {
|
15
16
|
allow_failed: Temporal::Api::Enums::V1::WorkflowIdReusePolicy::WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE_FAILED_ONLY,
|
16
17
|
allow: Temporal::Api::Enums::V1::WorkflowIdReusePolicy::WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE,
|
17
18
|
reject: Temporal::Api::Enums::V1::WorkflowIdReusePolicy::WORKFLOW_ID_REUSE_POLICY_REJECT_DUPLICATE
|
18
19
|
}.freeze
|
19
20
|
|
21
|
+
HISTORY_EVENT_FILTER = {
|
22
|
+
all: Temporal::Api::Enums::V1::HistoryEventFilterType::HISTORY_EVENT_FILTER_TYPE_ALL_EVENT,
|
23
|
+
close: Temporal::Api::Enums::V1::HistoryEventFilterType::HISTORY_EVENT_FILTER_TYPE_CLOSE_EVENT,
|
24
|
+
}.freeze
|
25
|
+
|
20
26
|
def initialize(host, port, identity)
|
21
27
|
@url = "#{host}:#{port}"
|
22
28
|
@identity = identity
|
29
|
+
@poll = true
|
30
|
+
@poll_mutex = Mutex.new
|
31
|
+
@poll_request = nil
|
23
32
|
end
|
24
33
|
|
25
34
|
def register_namespace(name:, description: nil, global: false, retention_period: 10)
|
26
35
|
request = Temporal::Api::WorkflowService::V1::RegisterNamespaceRequest.new(
|
27
|
-
|
36
|
+
namespace: name,
|
28
37
|
description: description,
|
29
38
|
is_global_namespace: global,
|
30
39
|
workflow_execution_retention_period: Google::Protobuf::Duration.new(
|
@@ -37,7 +46,7 @@ module Temporal
|
|
37
46
|
end
|
38
47
|
|
39
48
|
def describe_namespace(name:)
|
40
|
-
request = Temporal::Api::WorkflowService::V1::DescribeNamespaceRequest.new(
|
49
|
+
request = Temporal::Api::WorkflowService::V1::DescribeNamespaceRequest.new(namespace: name)
|
41
50
|
client.describe_namespace(request)
|
42
51
|
end
|
43
52
|
|
@@ -48,7 +57,7 @@ module Temporal
|
|
48
57
|
|
49
58
|
def update_namespace(name:, description:)
|
50
59
|
request = Temporal::Api::WorkflowService::V1::UpdateNamespaceRequest.new(
|
51
|
-
|
60
|
+
namespace: name,
|
52
61
|
update_info: Temporal::Api::WorkflowService::V1::UpdateNamespaceInfo.new(
|
53
62
|
description: description
|
54
63
|
)
|
@@ -57,7 +66,7 @@ module Temporal
|
|
57
66
|
end
|
58
67
|
|
59
68
|
def deprecate_namespace(name:)
|
60
|
-
request = Temporal::Api::WorkflowService::V1::DeprecateNamespaceRequest.new(
|
69
|
+
request = Temporal::Api::WorkflowService::V1::DeprecateNamespaceRequest.new(namespace: name)
|
61
70
|
client.deprecate_namespace(request)
|
62
71
|
end
|
63
72
|
|
@@ -68,9 +77,11 @@ module Temporal
|
|
68
77
|
task_queue:,
|
69
78
|
input: nil,
|
70
79
|
execution_timeout:,
|
80
|
+
run_timeout:,
|
71
81
|
task_timeout:,
|
72
82
|
workflow_id_reuse_policy: nil,
|
73
|
-
headers: nil
|
83
|
+
headers: nil,
|
84
|
+
cron_schedule: nil
|
74
85
|
)
|
75
86
|
request = Temporal::Api::WorkflowService::V1::StartWorkflowExecutionRequest.new(
|
76
87
|
identity: identity,
|
@@ -82,14 +93,15 @@ module Temporal
|
|
82
93
|
task_queue: Temporal::Api::TaskQueue::V1::TaskQueue.new(
|
83
94
|
name: task_queue
|
84
95
|
),
|
85
|
-
input:
|
96
|
+
input: to_payloads(input),
|
86
97
|
workflow_execution_timeout: execution_timeout,
|
87
|
-
workflow_run_timeout:
|
98
|
+
workflow_run_timeout: run_timeout,
|
88
99
|
workflow_task_timeout: task_timeout,
|
89
100
|
request_id: SecureRandom.uuid,
|
90
101
|
header: Temporal::Api::Common::V1::Header.new(
|
91
102
|
fields: headers
|
92
|
-
)
|
103
|
+
),
|
104
|
+
cron_schedule: cron_schedule
|
93
105
|
)
|
94
106
|
|
95
107
|
if workflow_id_reuse_policy
|
@@ -102,20 +114,43 @@ module Temporal
|
|
102
114
|
client.start_workflow_execution(request)
|
103
115
|
rescue GRPC::AlreadyExists => e
|
104
116
|
# Feel like there should be cleaner way to do this...
|
105
|
-
run_id = e.details[/RunId: (
|
117
|
+
run_id = e.details[/RunId: ([a-f0-9]+-[a-f0-9]+-[a-f0-9]+-[a-f0-9]+-[a-f0-9]+)/, 1]
|
106
118
|
raise Temporal::WorkflowExecutionAlreadyStartedFailure.new(e.details, run_id)
|
107
119
|
end
|
108
120
|
|
109
|
-
|
121
|
+
SERVER_MAX_GET_WORKFLOW_EXECUTION_HISTORY_POLL = 30
|
122
|
+
|
123
|
+
def get_workflow_execution_history(
|
124
|
+
namespace:,
|
125
|
+
workflow_id:,
|
126
|
+
run_id:,
|
127
|
+
next_page_token: nil,
|
128
|
+
wait_for_new_event: false,
|
129
|
+
event_type: :all,
|
130
|
+
timeout: nil
|
131
|
+
)
|
132
|
+
if wait_for_new_event
|
133
|
+
if timeout.nil?
|
134
|
+
# This is an internal error. Wrappers should enforce this.
|
135
|
+
raise "You must specify a timeout when wait_for_new_event = true."
|
136
|
+
elsif timeout > SERVER_MAX_GET_WORKFLOW_EXECUTION_HISTORY_POLL
|
137
|
+
raise ClientError.new(
|
138
|
+
"You may not specify a timeout of more than #{SERVER_MAX_GET_WORKFLOW_EXECUTION_HISTORY_POLL} seconds, got: #{timeout}."
|
139
|
+
)
|
140
|
+
end
|
141
|
+
end
|
110
142
|
request = Temporal::Api::WorkflowService::V1::GetWorkflowExecutionHistoryRequest.new(
|
111
143
|
namespace: namespace,
|
112
144
|
execution: Temporal::Api::Common::V1::WorkflowExecution.new(
|
113
145
|
workflow_id: workflow_id,
|
114
146
|
run_id: run_id
|
115
|
-
)
|
147
|
+
),
|
148
|
+
next_page_token: next_page_token,
|
149
|
+
wait_new_event: wait_for_new_event,
|
150
|
+
history_event_filter_type: HISTORY_EVENT_FILTER[event_type]
|
116
151
|
)
|
117
|
-
|
118
|
-
client.get_workflow_execution_history(request)
|
152
|
+
deadline = timeout ? Time.now + timeout : nil
|
153
|
+
client.get_workflow_execution_history(request, deadline: deadline)
|
119
154
|
end
|
120
155
|
|
121
156
|
def poll_workflow_task_queue(namespace:, task_queue:)
|
@@ -126,7 +161,13 @@ module Temporal
|
|
126
161
|
name: task_queue
|
127
162
|
)
|
128
163
|
)
|
129
|
-
|
164
|
+
|
165
|
+
poll_mutex.synchronize do
|
166
|
+
return unless can_poll?
|
167
|
+
@poll_request = client.poll_workflow_task_queue(request, return_op: true)
|
168
|
+
end
|
169
|
+
|
170
|
+
poll_request.execute
|
130
171
|
end
|
131
172
|
|
132
173
|
def respond_workflow_task_completed(task_token:, commands:)
|
@@ -156,13 +197,19 @@ module Temporal
|
|
156
197
|
name: task_queue
|
157
198
|
)
|
158
199
|
)
|
159
|
-
|
200
|
+
|
201
|
+
poll_mutex.synchronize do
|
202
|
+
return unless can_poll?
|
203
|
+
@poll_request = client.poll_activity_task_queue(request, return_op: true)
|
204
|
+
end
|
205
|
+
|
206
|
+
poll_request.execute
|
160
207
|
end
|
161
208
|
|
162
209
|
def record_activity_task_heartbeat(task_token:, details: nil)
|
163
210
|
request = Temporal::Api::WorkflowService::V1::RecordActivityTaskHeartbeatRequest.new(
|
164
211
|
task_token: task_token,
|
165
|
-
details:
|
212
|
+
details: to_details_payloads(details),
|
166
213
|
identity: identity
|
167
214
|
)
|
168
215
|
client.record_activity_task_heartbeat(request)
|
@@ -176,7 +223,7 @@ module Temporal
|
|
176
223
|
request = Temporal::Api::WorkflowService::V1::RespondActivityTaskCompletedRequest.new(
|
177
224
|
identity: identity,
|
178
225
|
task_token: task_token,
|
179
|
-
result:
|
226
|
+
result: to_result_payloads(result),
|
180
227
|
)
|
181
228
|
client.respond_activity_task_completed(request)
|
182
229
|
end
|
@@ -188,7 +235,7 @@ module Temporal
|
|
188
235
|
workflow_id: workflow_id,
|
189
236
|
run_id: run_id,
|
190
237
|
activity_id: activity_id,
|
191
|
-
result:
|
238
|
+
result: to_result_payloads(result)
|
192
239
|
)
|
193
240
|
client.respond_activity_task_completed_by_id(request)
|
194
241
|
end
|
@@ -217,7 +264,7 @@ module Temporal
|
|
217
264
|
def respond_activity_task_canceled(task_token:, details: nil)
|
218
265
|
request = Temporal::Api::WorkflowService::V1::RespondActivityTaskCanceledRequest.new(
|
219
266
|
task_token: task_token,
|
220
|
-
details:
|
267
|
+
details: to_details_payloads(details),
|
221
268
|
identity: identity
|
222
269
|
)
|
223
270
|
client.respond_activity_task_canceled(request)
|
@@ -239,7 +286,7 @@ module Temporal
|
|
239
286
|
run_id: run_id
|
240
287
|
),
|
241
288
|
signal_name: signal,
|
242
|
-
input:
|
289
|
+
input: to_signal_payloads(input),
|
243
290
|
identity: identity
|
244
291
|
)
|
245
292
|
client.signal_workflow_execution(request)
|
@@ -254,7 +301,7 @@ module Temporal
|
|
254
301
|
namespace: namespace,
|
255
302
|
workflow_execution: Temporal::Api::Common::V1::WorkflowExecution.new(
|
256
303
|
workflow_id: workflow_id,
|
257
|
-
run_id: run_id
|
304
|
+
run_id: run_id,
|
258
305
|
),
|
259
306
|
reason: reason,
|
260
307
|
workflow_task_finish_event_id: workflow_task_event_id
|
@@ -262,8 +309,25 @@ module Temporal
|
|
262
309
|
client.reset_workflow_execution(request)
|
263
310
|
end
|
264
311
|
|
265
|
-
def terminate_workflow_execution
|
266
|
-
|
312
|
+
def terminate_workflow_execution(
|
313
|
+
namespace:,
|
314
|
+
workflow_id:,
|
315
|
+
run_id:,
|
316
|
+
reason: nil,
|
317
|
+
details: nil
|
318
|
+
)
|
319
|
+
request = Temporal::Api::WorkflowService::V1::TerminateWorkflowExecutionRequest.new(
|
320
|
+
identity: identity,
|
321
|
+
namespace: namespace,
|
322
|
+
workflow_execution: Temporal::Api::Common::V1::WorkflowExecution.new(
|
323
|
+
workflow_id: workflow_id,
|
324
|
+
run_id: run_id,
|
325
|
+
),
|
326
|
+
reason: reason,
|
327
|
+
details: to_details_payloads(details)
|
328
|
+
)
|
329
|
+
|
330
|
+
client.terminate_workflow_execution(request)
|
267
331
|
end
|
268
332
|
|
269
333
|
def list_open_workflow_executions
|
@@ -329,17 +393,28 @@ module Temporal
|
|
329
393
|
client.describe_task_queue(request)
|
330
394
|
end
|
331
395
|
|
396
|
+
def cancel_polling_request
|
397
|
+
poll_mutex.synchronize do
|
398
|
+
@poll = false
|
399
|
+
poll_request&.cancel
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
332
403
|
private
|
333
404
|
|
334
|
-
attr_reader :url, :identity
|
405
|
+
attr_reader :url, :identity, :poll_mutex, :poll_request
|
335
406
|
|
336
407
|
def client
|
337
408
|
@client ||= Temporal::Api::WorkflowService::V1::WorkflowService::Stub.new(
|
338
409
|
url,
|
339
410
|
:this_channel_is_insecure,
|
340
|
-
timeout:
|
411
|
+
timeout: 60
|
341
412
|
)
|
342
413
|
end
|
414
|
+
|
415
|
+
def can_poll?
|
416
|
+
@poll
|
417
|
+
end
|
343
418
|
end
|
344
419
|
end
|
345
420
|
end
|