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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/README.md +23 -3
  4. data/lib/gen/temporal/api/command/v1/message_pb.rb +1 -1
  5. data/lib/gen/temporal/api/enums/v1/common_pb.rb +7 -0
  6. data/lib/gen/temporal/api/errordetails/v1/message_pb.rb +5 -6
  7. data/lib/gen/temporal/api/version/v1/message_pb.rb +19 -8
  8. data/lib/gen/temporal/api/workflowservice/v1/request_response_pb.rb +19 -8
  9. data/lib/gen/temporal/api/workflowservice/v1/service_services_pb.rb +0 -3
  10. data/lib/temporal.rb +104 -0
  11. data/lib/temporal/activity/context.rb +5 -1
  12. data/lib/temporal/activity/poller.rb +26 -9
  13. data/lib/temporal/activity/task_processor.rb +33 -20
  14. data/lib/temporal/client/converter/base.rb +35 -0
  15. data/lib/temporal/client/converter/composite.rb +49 -0
  16. data/lib/temporal/client/converter/payload/bytes.rb +30 -0
  17. data/lib/temporal/client/converter/payload/json.rb +28 -0
  18. data/lib/temporal/client/converter/payload/nil.rb +27 -0
  19. data/lib/temporal/client/grpc_client.rb +102 -27
  20. data/lib/temporal/client/retryer.rb +49 -0
  21. data/lib/temporal/client/serializer.rb +2 -0
  22. data/lib/temporal/client/serializer/cancel_timer.rb +2 -2
  23. data/lib/temporal/client/serializer/complete_workflow.rb +6 -4
  24. data/lib/temporal/client/serializer/continue_as_new.rb +37 -0
  25. data/lib/temporal/client/serializer/fail_workflow.rb +2 -2
  26. data/lib/temporal/client/serializer/failure.rb +4 -2
  27. data/lib/temporal/client/serializer/record_marker.rb +6 -4
  28. data/lib/temporal/client/serializer/request_activity_cancellation.rb +2 -2
  29. data/lib/temporal/client/serializer/retry_policy.rb +24 -0
  30. data/lib/temporal/client/serializer/schedule_activity.rb +8 -20
  31. data/lib/temporal/client/serializer/start_child_workflow.rb +9 -20
  32. data/lib/temporal/client/serializer/start_timer.rb +2 -2
  33. data/lib/temporal/concerns/payloads.rb +51 -0
  34. data/lib/temporal/configuration.rb +31 -4
  35. data/lib/temporal/error_handler.rb +11 -0
  36. data/lib/temporal/errors.rb +24 -0
  37. data/lib/temporal/execution_options.rb +9 -1
  38. data/lib/temporal/json.rb +3 -1
  39. data/lib/temporal/logger.rb +17 -0
  40. data/lib/temporal/metadata.rb +11 -3
  41. data/lib/temporal/metadata/activity.rb +15 -2
  42. data/lib/temporal/metadata/workflow.rb +8 -0
  43. data/lib/temporal/metadata/workflow_task.rb +11 -0
  44. data/lib/temporal/retry_policy.rb +6 -9
  45. data/lib/temporal/saga/concern.rb +1 -1
  46. data/lib/temporal/testing.rb +1 -0
  47. data/lib/temporal/testing/future_registry.rb +1 -1
  48. data/lib/temporal/testing/local_activity_context.rb +1 -1
  49. data/lib/temporal/testing/local_workflow_context.rb +38 -14
  50. data/lib/temporal/testing/scheduled_workflows.rb +75 -0
  51. data/lib/temporal/testing/temporal_override.rb +35 -7
  52. data/lib/temporal/testing/workflow_override.rb +6 -1
  53. data/lib/temporal/version.rb +1 -1
  54. data/lib/temporal/worker.rb +28 -10
  55. data/lib/temporal/workflow.rb +8 -2
  56. data/lib/temporal/workflow/command.rb +3 -0
  57. data/lib/temporal/workflow/context.rb +40 -5
  58. data/lib/temporal/workflow/errors.rb +39 -0
  59. data/lib/temporal/workflow/executor.rb +1 -1
  60. data/lib/temporal/workflow/future.rb +18 -6
  61. data/lib/temporal/workflow/history/event.rb +1 -3
  62. data/lib/temporal/workflow/history/event_target.rb +4 -0
  63. data/lib/temporal/workflow/history/window.rb +1 -1
  64. data/lib/temporal/workflow/poller.rb +41 -13
  65. data/lib/temporal/workflow/replay_aware_logger.rb +4 -4
  66. data/lib/temporal/workflow/state_manager.rb +33 -52
  67. data/lib/temporal/workflow/task_processor.rb +41 -11
  68. metadata +21 -9
  69. 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
- THREAD_POOL_SIZE = 20
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 stop
28
+ def stop_polling
25
29
  @shutting_down = true
26
- Thread.new { Temporal.logger.info('Shutting down activity poller') }.join
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
- Temporal.logger.debug("Polling activity task queue (#{namespace} / #{task_queue})")
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: #{error.inspect}")
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(THREAD_POOL_SIZE)
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/json'
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.info("Processing activity task for #{activity_name}")
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, parse_payload(task.input))
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 in #{time_diff_ms}ms")
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 #{activity_name} completed")
58
- client.respond_activity_task_completed(task_token: task_token, result: result)
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 #{activity_name}: #{error.inspect}")
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 #{activity_name} failed with: #{error.inspect}")
65
- client.respond_activity_task_failed(task_token: task_token, exception: error)
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 #{activity_name}: #{error.inspect}")
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
- binary = payload.payloads.first.data
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
- name: name,
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(name: name)
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
- name: name,
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(name: name)
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: Serializer::Payload.new(input).to_proto,
96
+ input: to_payloads(input),
86
97
  workflow_execution_timeout: execution_timeout,
87
- workflow_run_timeout: execution_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: (.*)\.$/, 1]
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
- def get_workflow_execution_history(namespace:, workflow_id:, run_id:)
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
- client.poll_workflow_task_queue(request)
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
- client.poll_activity_task_queue(request)
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: Serializer::Payload.new(details).to_proto,
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: Serializer::Payload.new(result).to_proto,
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: Serializer::Payload.new(result).to_proto
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: Serializer::Payload.new(details).to_proto,
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: Serializer::Payload.new(input).to_proto,
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
- raise NotImplementedError
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: 5
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