cadence-ruby 0.0.0 → 0.1.0

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.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +456 -0
  3. data/cadence.gemspec +9 -2
  4. data/lib/cadence-ruby.rb +1 -0
  5. data/lib/cadence.rb +176 -0
  6. data/lib/cadence/activity.rb +33 -0
  7. data/lib/cadence/activity/async_token.rb +34 -0
  8. data/lib/cadence/activity/context.rb +64 -0
  9. data/lib/cadence/activity/poller.rb +89 -0
  10. data/lib/cadence/activity/task_processor.rb +73 -0
  11. data/lib/cadence/activity/workflow_convenience_methods.rb +41 -0
  12. data/lib/cadence/client.rb +21 -0
  13. data/lib/cadence/client/errors.rb +8 -0
  14. data/lib/cadence/client/thrift_client.rb +380 -0
  15. data/lib/cadence/concerns/executable.rb +33 -0
  16. data/lib/cadence/concerns/typed.rb +40 -0
  17. data/lib/cadence/configuration.rb +36 -0
  18. data/lib/cadence/errors.rb +21 -0
  19. data/lib/cadence/executable_lookup.rb +25 -0
  20. data/lib/cadence/execution_options.rb +32 -0
  21. data/lib/cadence/json.rb +18 -0
  22. data/lib/cadence/metadata.rb +73 -0
  23. data/lib/cadence/metadata/activity.rb +28 -0
  24. data/lib/cadence/metadata/base.rb +17 -0
  25. data/lib/cadence/metadata/decision.rb +25 -0
  26. data/lib/cadence/metadata/workflow.rb +23 -0
  27. data/lib/cadence/metrics.rb +37 -0
  28. data/lib/cadence/metrics_adapters/log.rb +33 -0
  29. data/lib/cadence/metrics_adapters/null.rb +9 -0
  30. data/lib/cadence/middleware/chain.rb +30 -0
  31. data/lib/cadence/middleware/entry.rb +9 -0
  32. data/lib/cadence/retry_policy.rb +27 -0
  33. data/lib/cadence/saga/concern.rb +37 -0
  34. data/lib/cadence/saga/result.rb +22 -0
  35. data/lib/cadence/saga/saga.rb +24 -0
  36. data/lib/cadence/testing.rb +50 -0
  37. data/lib/cadence/testing/cadence_override.rb +112 -0
  38. data/lib/cadence/testing/future_registry.rb +27 -0
  39. data/lib/cadence/testing/local_activity_context.rb +17 -0
  40. data/lib/cadence/testing/local_workflow_context.rb +207 -0
  41. data/lib/cadence/testing/workflow_execution.rb +44 -0
  42. data/lib/cadence/testing/workflow_override.rb +36 -0
  43. data/lib/cadence/thread_local_context.rb +14 -0
  44. data/lib/cadence/thread_pool.rb +68 -0
  45. data/lib/cadence/types.rb +7 -0
  46. data/lib/cadence/utils.rb +17 -0
  47. data/lib/cadence/uuid.rb +19 -0
  48. data/lib/cadence/version.rb +1 -1
  49. data/lib/cadence/worker.rb +91 -0
  50. data/lib/cadence/workflow.rb +42 -0
  51. data/lib/cadence/workflow/context.rb +266 -0
  52. data/lib/cadence/workflow/convenience_methods.rb +34 -0
  53. data/lib/cadence/workflow/decision.rb +39 -0
  54. data/lib/cadence/workflow/decision_state_machine.rb +48 -0
  55. data/lib/cadence/workflow/decision_task_processor.rb +105 -0
  56. data/lib/cadence/workflow/dispatcher.rb +31 -0
  57. data/lib/cadence/workflow/execution_info.rb +45 -0
  58. data/lib/cadence/workflow/executor.rb +45 -0
  59. data/lib/cadence/workflow/future.rb +75 -0
  60. data/lib/cadence/workflow/history.rb +76 -0
  61. data/lib/cadence/workflow/history/event.rb +71 -0
  62. data/lib/cadence/workflow/history/event_target.rb +79 -0
  63. data/lib/cadence/workflow/history/window.rb +40 -0
  64. data/lib/cadence/workflow/poller.rb +74 -0
  65. data/lib/cadence/workflow/replay_aware_logger.rb +36 -0
  66. data/lib/cadence/workflow/serializer.rb +31 -0
  67. data/lib/cadence/workflow/serializer/base.rb +22 -0
  68. data/lib/cadence/workflow/serializer/cancel_timer.rb +19 -0
  69. data/lib/cadence/workflow/serializer/complete_workflow.rb +20 -0
  70. data/lib/cadence/workflow/serializer/fail_workflow.rb +21 -0
  71. data/lib/cadence/workflow/serializer/record_marker.rb +21 -0
  72. data/lib/cadence/workflow/serializer/request_activity_cancellation.rb +19 -0
  73. data/lib/cadence/workflow/serializer/schedule_activity.rb +54 -0
  74. data/lib/cadence/workflow/serializer/start_child_workflow.rb +52 -0
  75. data/lib/cadence/workflow/serializer/start_timer.rb +20 -0
  76. data/lib/cadence/workflow/state_manager.rb +324 -0
  77. data/lib/gen/thrift/cadence_constants.rb +11 -0
  78. data/lib/gen/thrift/cadence_types.rb +11 -0
  79. data/lib/gen/thrift/shared_constants.rb +11 -0
  80. data/lib/gen/thrift/shared_types.rb +4600 -0
  81. data/lib/gen/thrift/workflow_service.rb +3142 -0
  82. data/rbi/cadence-ruby.rbi +39 -0
  83. metadata +152 -5
@@ -0,0 +1,41 @@
1
+ # This module provides a set of methods for imitating direct Activities calls
2
+ # from within Workflows:
3
+ #
4
+ # class TestWorkflow < Cadence::Workflow
5
+ # def execute
6
+ # TestActivity.execute!('foo', 'bar')
7
+ # end
8
+ # end
9
+ #
10
+ # This is analogous to calling:
11
+ #
12
+ # workflow.execute_activity(TestActivity, 'foo', 'bar')
13
+ #
14
+ require 'cadence/thread_local_context'
15
+
16
+ module Cadence
17
+ class Activity
18
+ module WorkflowConvenienceMethods
19
+ def execute(*input, **args)
20
+ context = Cadence::ThreadLocalContext.get
21
+ raise 'Called Activity#execute outside of a Workflow context' unless context
22
+
23
+ context.execute_activity(self, *input, **args)
24
+ end
25
+
26
+ def execute!(*input, **args)
27
+ context = Cadence::ThreadLocalContext.get
28
+ raise 'Called Activity#execute! outside of a Workflow context' unless context
29
+
30
+ context.execute_activity!(self, *input, **args)
31
+ end
32
+
33
+ def execute_locally(*input, **args)
34
+ context = Cadence::ThreadLocalContext.get
35
+ raise 'Called Activity#execute_locally outside of a Workflow context' unless context
36
+
37
+ context.execute_local_activity(self, *input, **args)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,21 @@
1
+ require 'cadence/client/thrift_client'
2
+
3
+ module Cadence
4
+ module Client
5
+ CLIENT_TYPES_MAP = {
6
+ thrift: Cadence::Client::ThriftClient
7
+ }.freeze
8
+
9
+ def self.generate(options = {})
10
+ client_class = CLIENT_TYPES_MAP[Cadence.configuration.client_type]
11
+ host = Cadence.configuration.host
12
+ port = Cadence.configuration.port
13
+
14
+ hostname = `hostname`
15
+ thread_id = Thread.current.object_id
16
+ identity = "#{thread_id}@#{hostname}"
17
+
18
+ client_class.new(host, port, identity, options)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,8 @@
1
+ module Cadence
2
+ module Client
3
+ class Error < StandardError; end
4
+
5
+ # incorrect arguments passed to the client
6
+ class ArgumentError < Error; end
7
+ end
8
+ end
@@ -0,0 +1,380 @@
1
+ require 'thrift'
2
+ require 'securerandom'
3
+ require 'cadence/json'
4
+ require 'cadence/client/errors'
5
+ require 'gen/thrift/workflow_service'
6
+
7
+ module Cadence
8
+ module Client
9
+ class ThriftClient
10
+ WORKFLOW_ID_REUSE_POLICY = {
11
+ allow_failed: CadenceThrift::WorkflowIdReusePolicy::AllowDuplicateFailedOnly,
12
+ allow: CadenceThrift::WorkflowIdReusePolicy::AllowDuplicate,
13
+ reject: CadenceThrift::WorkflowIdReusePolicy::RejectDuplicate
14
+ }.freeze
15
+
16
+ DEFAULT_OPTIONS = {
17
+ polling_ttl: 60 # 1 minute
18
+ }.freeze
19
+
20
+ def initialize(host, port, identity, options = {})
21
+ @url = "http://#{host}:#{port}"
22
+ @identity = identity
23
+ @options = DEFAULT_OPTIONS.merge(options)
24
+ @mutex = Mutex.new
25
+ end
26
+
27
+ def register_domain(name:, description: nil, global: false, metrics: false, retention_period: 10)
28
+ request = CadenceThrift::RegisterDomainRequest.new(
29
+ name: name,
30
+ description: description,
31
+ emitMetric: metrics,
32
+ isGlobalDomain: global,
33
+ workflowExecutionRetentionPeriodInDays: retention_period
34
+ )
35
+ send_request('RegisterDomain', request)
36
+ end
37
+
38
+ def describe_domain(name:)
39
+ request = CadenceThrift::DescribeDomainRequest.new(name: name)
40
+ send_request('DescribeDomain', request)
41
+ end
42
+
43
+ def list_domains(page_size:)
44
+ request = CadenceThrift::ListDomainsRequest.new(pageSize: page_size)
45
+ send_request('ListDomains', request)
46
+ end
47
+
48
+ def update_domain(name:, description:)
49
+ request = CadenceThrift::UpdateDomainRequest.new(
50
+ name: name,
51
+ updateInfo: CadenceThrift::UpdateDomainRequest.new(
52
+ description: description
53
+ )
54
+ )
55
+ send_request('UpdateDomain', request)
56
+ end
57
+
58
+ def deprecate_domain(name:)
59
+ request = CadenceThrift::DeprecateDomainRequest.new(name: name)
60
+ send_request('DeprecateDomain', request)
61
+ end
62
+
63
+ def start_workflow_execution(
64
+ domain:,
65
+ workflow_id:,
66
+ workflow_name:,
67
+ task_list:,
68
+ input: nil,
69
+ execution_timeout:,
70
+ task_timeout:,
71
+ workflow_id_reuse_policy: nil,
72
+ headers: nil,
73
+ cron_schedule: nil
74
+ )
75
+ request = CadenceThrift::StartWorkflowExecutionRequest.new(
76
+ identity: identity,
77
+ domain: domain,
78
+ workflowType: CadenceThrift::WorkflowType.new(
79
+ name: workflow_name
80
+ ),
81
+ workflowId: workflow_id,
82
+ taskList: CadenceThrift::TaskList.new(
83
+ name: task_list
84
+ ),
85
+ input: JSON.serialize(input),
86
+ executionStartToCloseTimeoutSeconds: execution_timeout,
87
+ taskStartToCloseTimeoutSeconds: task_timeout,
88
+ requestId: SecureRandom.uuid,
89
+ header: CadenceThrift::Header.new(
90
+ fields: headers
91
+ ),
92
+ cronSchedule: cron_schedule
93
+ )
94
+
95
+ if workflow_id_reuse_policy
96
+ policy = WORKFLOW_ID_REUSE_POLICY[workflow_id_reuse_policy]
97
+ raise Client::ArgumentError, 'Unknown workflow_id_reuse_policy specified' unless policy
98
+
99
+ request.workflowIdReusePolicy = policy
100
+ end
101
+
102
+ send_request('StartWorkflowExecution', request)
103
+ end
104
+
105
+ def get_workflow_execution_history(domain:, workflow_id:, run_id:, next_page_token: nil)
106
+ request = CadenceThrift::GetWorkflowExecutionHistoryRequest.new(
107
+ domain: domain,
108
+ execution: CadenceThrift::WorkflowExecution.new(
109
+ workflowId: workflow_id,
110
+ runId: run_id
111
+ ),
112
+ nextPageToken: next_page_token
113
+ )
114
+
115
+ send_request('GetWorkflowExecutionHistory', request)
116
+ end
117
+
118
+ def poll_for_decision_task(domain:, task_list:)
119
+ request = CadenceThrift::PollForDecisionTaskRequest.new(
120
+ identity: identity,
121
+ domain: domain,
122
+ taskList: CadenceThrift::TaskList.new(
123
+ name: task_list
124
+ )
125
+ )
126
+ send_request('PollForDecisionTask', request)
127
+ end
128
+
129
+ def respond_decision_task_completed(task_token:, decisions:)
130
+ request = CadenceThrift::RespondDecisionTaskCompletedRequest.new(
131
+ identity: identity,
132
+ taskToken: task_token,
133
+ decisions: Array(decisions)
134
+ )
135
+ send_request('RespondDecisionTaskCompleted', request)
136
+ end
137
+
138
+ def respond_decision_task_failed(task_token:, cause:, details: nil)
139
+ request = CadenceThrift::RespondDecisionTaskFailedRequest.new(
140
+ identity: identity,
141
+ taskToken: task_token,
142
+ cause: cause,
143
+ details: JSON.serialize(details)
144
+ )
145
+ send_request('RespondDecisionTaskFailed', request)
146
+ end
147
+
148
+ def poll_for_activity_task(domain:, task_list:)
149
+ request = CadenceThrift::PollForActivityTaskRequest.new(
150
+ identity: identity,
151
+ domain: domain,
152
+ taskList: CadenceThrift::TaskList.new(
153
+ name: task_list
154
+ )
155
+ )
156
+ send_request('PollForActivityTask', request)
157
+ end
158
+
159
+ def record_activity_task_heartbeat(task_token:, details: nil)
160
+ request = CadenceThrift::RecordActivityTaskHeartbeatRequest.new(
161
+ taskToken: task_token,
162
+ details: JSON.serialize(details),
163
+ identity: identity
164
+ )
165
+ send_request('RecordActivityTaskHeartbeat', request)
166
+ end
167
+
168
+ def record_activity_task_heartbeat_by_id
169
+ raise NotImplementedError
170
+ end
171
+
172
+ def respond_activity_task_completed(task_token:, result:)
173
+ request = CadenceThrift::RespondActivityTaskCompletedRequest.new(
174
+ identity: identity,
175
+ taskToken: task_token,
176
+ result: JSON.serialize(result)
177
+ )
178
+ send_request('RespondActivityTaskCompleted', request)
179
+ end
180
+
181
+ def respond_activity_task_completed_by_id(domain:, activity_id:, workflow_id:, run_id:, result:)
182
+ request = CadenceThrift::RespondActivityTaskCompletedByIDRequest.new(
183
+ identity: identity,
184
+ domain: domain,
185
+ workflowID: workflow_id,
186
+ runID: run_id,
187
+ activityID: activity_id,
188
+ result: JSON.serialize(result)
189
+ )
190
+ send_request('RespondActivityTaskCompletedByID', request)
191
+ end
192
+
193
+ def respond_activity_task_failed(task_token:, reason:, details: nil)
194
+ request = CadenceThrift::RespondActivityTaskFailedRequest.new(
195
+ identity: identity,
196
+ taskToken: task_token,
197
+ reason: reason,
198
+ details: JSON.serialize(details)
199
+ )
200
+ send_request('RespondActivityTaskFailed', request)
201
+ end
202
+
203
+ def respond_activity_task_failed_by_id(domain:, activity_id:, workflow_id:, run_id:, reason:, details: nil)
204
+ request = CadenceThrift::RespondActivityTaskFailedByIDRequest.new(
205
+ identity: identity,
206
+ domain: domain,
207
+ workflowID: workflow_id,
208
+ runID: run_id,
209
+ activityID: activity_id,
210
+ reason: reason,
211
+ details: JSON.serialize(details)
212
+ )
213
+ send_request('RespondActivityTaskFailedByID', request)
214
+ end
215
+
216
+ def respond_activity_task_canceled(task_token:, details: nil)
217
+ request = CadenceThrift::RespondActivityTaskCanceledRequest.new(
218
+ taskToken: task_token,
219
+ details: JSON.serialize(details),
220
+ identity: identity
221
+ )
222
+ send_request('RespondActivityTaskCanceled', request)
223
+ end
224
+
225
+ def respond_activity_task_canceled_by_id
226
+ raise NotImplementedError
227
+ end
228
+
229
+ def request_cancel_workflow_execution
230
+ raise NotImplementedError
231
+ end
232
+
233
+ def signal_workflow_execution(domain:, workflow_id:, run_id:, signal:, input: nil)
234
+ request = CadenceThrift::SignalWorkflowExecutionRequest.new(
235
+ domain: domain,
236
+ workflowExecution: CadenceThrift::WorkflowExecution.new(
237
+ workflowId: workflow_id,
238
+ runId: run_id
239
+ ),
240
+ signalName: signal,
241
+ input: JSON.serialize(input),
242
+ identity: identity
243
+ )
244
+ send_request('SignalWorkflowExecution', request)
245
+ end
246
+
247
+ def signal_with_start_workflow_execution
248
+ raise NotImplementedError
249
+ end
250
+
251
+ def reset_workflow_execution(domain:, workflow_id:, run_id:, reason:, decision_task_event_id:)
252
+ request = CadenceThrift::ResetWorkflowExecutionRequest.new(
253
+ domain: domain,
254
+ workflowExecution: CadenceThrift::WorkflowExecution.new(
255
+ workflowId: workflow_id,
256
+ runId: run_id
257
+ ),
258
+ reason: reason,
259
+ decisionFinishEventId: decision_task_event_id,
260
+ requestId: SecureRandom.uuid
261
+ )
262
+ send_request('ResetWorkflowExecution', request)
263
+ end
264
+
265
+ def terminate_workflow_execution(domain:, workflow_id:, run_id:, reason:, details: nil)
266
+ request = CadenceThrift::TerminateWorkflowExecutionRequest.new(
267
+ domain: domain,
268
+ workflowExecution: CadenceThrift::WorkflowExecution.new(
269
+ workflowId: workflow_id,
270
+ runId: run_id
271
+ ),
272
+ reason: reason,
273
+ details: JSON.serialize(details),
274
+ identity: identity
275
+ )
276
+ send_request('TerminateWorkflowExecution', request)
277
+ end
278
+
279
+ def list_open_workflow_executions
280
+ raise NotImplementedError
281
+ end
282
+
283
+ def list_closed_workflow_executions
284
+ raise NotImplementedError
285
+ end
286
+
287
+ def list_workflow_executions
288
+ raise NotImplementedError
289
+ end
290
+
291
+ def list_archived_workflow_executions
292
+ raise NotImplementedError
293
+ end
294
+
295
+ def scan_workflow_executions
296
+ raise NotImplementedError
297
+ end
298
+
299
+ def count_workflow_executions
300
+ raise NotImplementedError
301
+ end
302
+
303
+ def get_search_attributes
304
+ raise NotImplementedError
305
+ end
306
+
307
+ def respond_query_task_completed
308
+ raise NotImplementedError
309
+ end
310
+
311
+ def reset_sticky_task_list
312
+ raise NotImplementedError
313
+ end
314
+
315
+ def query_workflow
316
+ raise NotImplementedError
317
+ end
318
+
319
+ def describe_workflow_execution(domain:, workflow_id:, run_id:)
320
+ request = CadenceThrift::DescribeWorkflowExecutionRequest.new(
321
+ domain: domain,
322
+ execution: CadenceThrift::WorkflowExecution.new(
323
+ workflowId: workflow_id,
324
+ runId: run_id
325
+ )
326
+ )
327
+ send_request('DescribeWorkflowExecution', request)
328
+ end
329
+
330
+ def describe_task_list(domain:, task_list:)
331
+ request = CadenceThrift::DescribeTaskListRequest.new(
332
+ domain: domain,
333
+ taskList: CadenceThrift::TaskList.new(
334
+ name: task_list
335
+ ),
336
+ taskListType: CadenceThrift::TaskListType::Decision,
337
+ includeTaskListStatus: true
338
+ )
339
+ send_request('DescribeTaskList', request)
340
+ end
341
+
342
+ private
343
+
344
+ attr_reader :url, :identity, :options, :mutex
345
+
346
+ def transport
347
+ @transport ||= Thrift::HTTPClientTransport.new(url).tap do |http|
348
+ http.add_headers(
349
+ 'Rpc-Caller' => 'ruby-client',
350
+ 'Rpc-Encoding' => 'thrift',
351
+ 'Rpc-Service' => 'cadence-proxy',
352
+ 'Context-TTL-MS' => (options[:polling_ttl] * 1_000).to_s
353
+ )
354
+ end
355
+ end
356
+
357
+ def connection
358
+ @connection ||= begin
359
+ protocol = Thrift::BinaryProtocol.new(transport)
360
+ CadenceThrift::WorkflowService::Client.new(protocol)
361
+ end
362
+ end
363
+
364
+ def send_request(name, request)
365
+ start_time = Time.now
366
+
367
+ # synchronize these calls because transport headers are mutated
368
+ result = mutex.synchronize do
369
+ transport.add_headers 'Rpc-Procedure' => "WorkflowService::#{name}"
370
+ connection.public_send(name, request)
371
+ end
372
+
373
+ time_diff_ms = ((Time.now - start_time) * 1000).round
374
+ Cadence.metrics.timing('request.latency', time_diff_ms, request_name: name)
375
+
376
+ result
377
+ end
378
+ end
379
+ end
380
+ end