cadence-ruby 0.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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