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,71 @@
1
+ require 'cadence/utils'
2
+
3
+ module Cadence
4
+ class Workflow
5
+ class History
6
+ class Event
7
+ EVENT_TYPES = %w[
8
+ ActivityTaskStarted
9
+ ActivityTaskCompleted
10
+ ActivityTaskFailed
11
+ ActivityTaskTimedOut
12
+ ActivityTaskCanceled
13
+ TimerFired
14
+ RequestCancelExternalWorkflowExecutionFailed
15
+ WorkflowExecutionSignaled
16
+ WorkflowExecutionTerminated
17
+ SignalExternalWorkflowExecutionFailed
18
+ ExternalWorkflowExecutionCancelRequested
19
+ ExternalWorkflowExecutionSignaled
20
+ UpsertWorkflowSearchAttributes
21
+ ].freeze
22
+
23
+ CHILD_WORKFLOW_EVENTS = %w[
24
+ StartChildWorkflowExecutionFailed
25
+ ChildWorkflowExecutionStarted
26
+ ChildWorkflowExecutionCompleted
27
+ ChildWorkflowExecutionFailed
28
+ ChildWorkflowExecutionCanceled
29
+ ChildWorkflowExecutionTimedOut
30
+ ChildWorkflowExecutionTerminated
31
+ ].freeze
32
+
33
+ attr_reader :id, :timestamp, :type, :attributes
34
+
35
+ def initialize(raw_event)
36
+ @id = raw_event.eventId
37
+ @timestamp = Utils.time_from_nanos(raw_event.timestamp)
38
+ @type = CadenceThrift::EventType::VALUE_MAP[raw_event.eventType]
39
+ @attributes = extract_attributes(raw_event)
40
+
41
+ freeze
42
+ end
43
+
44
+ # Returns the ID of the first event associated with the current event,
45
+ # referred to as a "decision" event. Not related to DecisionTask.
46
+ def decision_id
47
+ case type
48
+ when 'TimerFired'
49
+ attributes.startedEventId
50
+ when 'WorkflowExecutionSignaled'
51
+ 1 # fixed id for everything related to current workflow
52
+ when *EVENT_TYPES
53
+ attributes.scheduledEventId
54
+ when *CHILD_WORKFLOW_EVENTS
55
+ attributes.initiatedEventId
56
+ else
57
+ id
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def extract_attributes(raw_event)
64
+ attributes_argument = "#{type}EventAttributes"
65
+ attributes_argument[0] = attributes_argument[0].downcase
66
+ raw_event.public_send(attributes_argument)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,79 @@
1
+ require 'cadence/errors'
2
+
3
+ module Cadence
4
+ class Workflow
5
+ class History
6
+ class EventTarget
7
+ class UnexpectedEventType < InternalError; end
8
+
9
+ ACTIVITY_TYPE = :activity
10
+ CANCEL_ACTIVITY_REQUEST_TYPE = :cancel_activity_request
11
+ TIMER_TYPE = :timer
12
+ CANCEL_TIMER_REQUEST_TYPE = :cancel_timer_request
13
+ CHILD_WORKFLOW_TYPE = :child_workflow
14
+ MARKER_TYPE = :marker
15
+ EXTERNAL_WORKFLOW_TYPE = :external_workflow
16
+ CANCEL_EXTERNAL_WORKFLOW_REQUEST_TYPE = :cancel_external_workflow_request
17
+ WORKFLOW_TYPE = :workflow
18
+ CANCEL_WORKFLOW_REQUEST_TYPE = :cancel_workflow_request
19
+
20
+ TARGET_TYPES = {
21
+ 'ActivityTask' => ACTIVITY_TYPE,
22
+ 'ActivityTaskCancel' => CANCEL_ACTIVITY_REQUEST_TYPE,
23
+ 'RequestCancelActivityTask' => CANCEL_ACTIVITY_REQUEST_TYPE,
24
+ 'Timer' => TIMER_TYPE,
25
+ 'CancelTimer' => CANCEL_TIMER_REQUEST_TYPE,
26
+ 'ChildWorkflowExecution' => CHILD_WORKFLOW_TYPE,
27
+ 'StartChildWorkflowExecution' => CHILD_WORKFLOW_TYPE,
28
+ 'Marker' => MARKER_TYPE,
29
+ 'ExternalWorkflowExecution' => EXTERNAL_WORKFLOW_TYPE,
30
+ 'SignalExternalWorkflowExecution' => EXTERNAL_WORKFLOW_TYPE,
31
+ 'ExternalWorkflowExecutionCancel' => CANCEL_EXTERNAL_WORKFLOW_REQUEST_TYPE,
32
+ 'RequestCancelExternalWorkflowExecution' => CANCEL_EXTERNAL_WORKFLOW_REQUEST_TYPE,
33
+ 'UpsertWorkflowSearchAttributes' => WORKFLOW_TYPE,
34
+ 'WorkflowExecution' => WORKFLOW_TYPE,
35
+ 'WorkflowExecutionCancel' => CANCEL_WORKFLOW_REQUEST_TYPE,
36
+ }.freeze
37
+
38
+ attr_reader :id, :type
39
+
40
+ def self.workflow
41
+ @workflow ||= new(1, WORKFLOW_TYPE)
42
+ end
43
+
44
+ def self.from_event(event)
45
+ _, target_type = TARGET_TYPES.find { |type, _| event.type.start_with?(type) }
46
+
47
+ unless target_type
48
+ raise UnexpectedEventType, "Unexpected event #{event.type}"
49
+ end
50
+
51
+ new(event.decision_id, target_type)
52
+ end
53
+
54
+ def initialize(id, type)
55
+ @id = id
56
+ @type = type
57
+
58
+ freeze
59
+ end
60
+
61
+ def ==(other)
62
+ id == other.id && type == other.type
63
+ end
64
+
65
+ def eql?(other)
66
+ self == other
67
+ end
68
+
69
+ def hash
70
+ [id, type].hash
71
+ end
72
+
73
+ def to_s
74
+ "#{type} (#{id})"
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,40 @@
1
+ module Cadence
2
+ class Workflow
3
+ class History
4
+ class Window
5
+ attr_reader :local_time, :last_event_id, :events, :markers
6
+
7
+ def initialize
8
+ @local_time = nil
9
+ @last_event_id = nil
10
+ @events = []
11
+ @markers = []
12
+ @replay = false
13
+ end
14
+
15
+ def replay?
16
+ @replay
17
+ end
18
+
19
+ def add(event)
20
+ case event.type
21
+ when 'MarkerRecorded'
22
+ markers << event
23
+ when 'DecisionTaskStarted'
24
+ @last_event_id = event.id + 1 # one for completed
25
+ @local_time = event.timestamp
26
+ when 'DecisionTaskFailed', 'DecisionTaskTimedOut'
27
+ @next_event_id = nil
28
+ @local_time = nil
29
+ when 'DecisionTaskCompleted'
30
+ @replay = true
31
+ when 'DecisionTaskScheduled', 'DecisionTaskFailed'
32
+ # no-op
33
+ else
34
+ events << event
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,74 @@
1
+ require 'cadence/client'
2
+ require 'cadence/middleware/chain'
3
+ require 'cadence/workflow/decision_task_processor'
4
+
5
+ module Cadence
6
+ class Workflow
7
+ class Poller
8
+ def initialize(domain, task_list, workflow_lookup, middleware = [], options = {})
9
+ @domain = domain
10
+ @task_list = task_list
11
+ @workflow_lookup = workflow_lookup
12
+ @middleware = middleware
13
+ @options = options
14
+ @shutting_down = false
15
+ end
16
+
17
+ def start
18
+ @shutting_down = false
19
+ @thread = Thread.new(&method(:poll_loop))
20
+ end
21
+
22
+ def stop
23
+ @shutting_down = true
24
+ Cadence.logger.info('Shutting down a workflow poller')
25
+ end
26
+
27
+ def wait
28
+ @thread.join
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :domain, :task_list, :client, :workflow_lookup, :middleware, :options
34
+
35
+ def client
36
+ @client ||= Cadence::Client.generate(options)
37
+ end
38
+
39
+ def middleware_chain
40
+ @middleware_chain ||= Middleware::Chain.new(middleware)
41
+ end
42
+
43
+ def shutting_down?
44
+ @shutting_down
45
+ end
46
+
47
+ def poll_loop
48
+ last_poll_time = Time.now
49
+ metrics_tags = { domain: domain, task_list: task_list }.freeze
50
+
51
+ while !shutting_down? do
52
+ time_diff_ms = ((Time.now - last_poll_time) * 1000).round
53
+ Cadence.metrics.timing('workflow_poller.time_since_last_poll', time_diff_ms, metrics_tags)
54
+ Cadence.logger.debug("Polling for decision tasks (#{domain} / #{task_list})")
55
+
56
+ task = poll_for_task
57
+ last_poll_time = Time.now
58
+ process(task) if task&.workflowType
59
+ end
60
+ end
61
+
62
+ def poll_for_task
63
+ client.poll_for_decision_task(domain: domain, task_list: task_list)
64
+ rescue StandardError => error
65
+ Cadence.logger.error("Unable to poll for a decision task: #{error.inspect}")
66
+ nil
67
+ end
68
+
69
+ def process(task)
70
+ DecisionTaskProcessor.new(task, domain, workflow_lookup, client, middleware_chain).process
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,36 @@
1
+ module Cadence
2
+ class Workflow
3
+ class ReplayAwareLogger
4
+ SEVERITIES = %i[debug info warn error fatal unknown].freeze
5
+
6
+ attr_writer :replay
7
+
8
+ def initialize(main_logger, replay = true)
9
+ @main_logger = main_logger
10
+ @replay = replay
11
+ end
12
+
13
+ SEVERITIES.each do |severity|
14
+ define_method severity do |message|
15
+ return if replay?
16
+
17
+ main_logger.public_send(severity, message)
18
+ end
19
+ end
20
+
21
+ def log(severity, message)
22
+ return if replay?
23
+
24
+ main_logger.log(severity, message)
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :main_logger
30
+
31
+ def replay?
32
+ @replay
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,31 @@
1
+ require 'cadence/workflow/decision'
2
+ require 'cadence/workflow/serializer/schedule_activity'
3
+ require 'cadence/workflow/serializer/start_child_workflow'
4
+ require 'cadence/workflow/serializer/request_activity_cancellation'
5
+ require 'cadence/workflow/serializer/record_marker'
6
+ require 'cadence/workflow/serializer/start_timer'
7
+ require 'cadence/workflow/serializer/cancel_timer'
8
+ require 'cadence/workflow/serializer/complete_workflow'
9
+ require 'cadence/workflow/serializer/fail_workflow'
10
+
11
+ module Cadence
12
+ class Workflow
13
+ module Serializer
14
+ SERIALIZERS_MAP = {
15
+ Workflow::Decision::ScheduleActivity => Serializer::ScheduleActivity,
16
+ Workflow::Decision::StartChildWorkflow => Serializer::StartChildWorkflow,
17
+ Workflow::Decision::RequestActivityCancellation => Serializer::RequestActivityCancellation,
18
+ Workflow::Decision::RecordMarker => Serializer::RecordMarker,
19
+ Workflow::Decision::StartTimer => Serializer::StartTimer,
20
+ Workflow::Decision::CancelTimer => Serializer::CancelTimer,
21
+ Workflow::Decision::CompleteWorkflow => Serializer::CompleteWorkflow,
22
+ Workflow::Decision::FailWorkflow => Serializer::FailWorkflow
23
+ }.freeze
24
+
25
+ def self.serialize(object)
26
+ serializer = SERIALIZERS_MAP[object.class]
27
+ serializer.new(object).to_thrift
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,22 @@
1
+ require 'oj'
2
+ require 'gen/thrift/shared_types'
3
+
4
+ module Cadence
5
+ class Workflow
6
+ module Serializer
7
+ class Base
8
+ def initialize(object)
9
+ @object = object
10
+ end
11
+
12
+ def to_thrift
13
+ raise NotImplementedError, 'serializer needs to implement #to_thrift'
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :object
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ require 'cadence/workflow/serializer/base'
2
+
3
+ module Cadence
4
+ class Workflow
5
+ module Serializer
6
+ class CancelTimer < Base
7
+ def to_thrift
8
+ CadenceThrift::Decision.new(
9
+ decisionType: CadenceThrift::DecisionType::CancelTimer,
10
+ cancelTimerDecisionAttributes:
11
+ CadenceThrift::CancelTimerDecisionAttributes.new(
12
+ timerId: object.timer_id.to_s
13
+ )
14
+ )
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ require 'cadence/workflow/serializer/base'
2
+ require 'cadence/json'
3
+
4
+ module Cadence
5
+ class Workflow
6
+ module Serializer
7
+ class CompleteWorkflow < Base
8
+ def to_thrift
9
+ CadenceThrift::Decision.new(
10
+ decisionType: CadenceThrift::DecisionType::CompleteWorkflowExecution,
11
+ completeWorkflowExecutionDecisionAttributes:
12
+ CadenceThrift::CompleteWorkflowExecutionDecisionAttributes.new(
13
+ result: JSON.serialize(object.result)
14
+ )
15
+ )
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ require 'cadence/workflow/serializer/base'
2
+ require 'cadence/json'
3
+
4
+ module Cadence
5
+ class Workflow
6
+ module Serializer
7
+ class FailWorkflow < Base
8
+ def to_thrift
9
+ CadenceThrift::Decision.new(
10
+ decisionType: CadenceThrift::DecisionType::FailWorkflowExecution,
11
+ failWorkflowExecutionDecisionAttributes:
12
+ CadenceThrift::FailWorkflowExecutionDecisionAttributes.new(
13
+ reason: object.reason,
14
+ details: JSON.serialize(object.details)
15
+ )
16
+ )
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ require 'cadence/workflow/serializer/base'
2
+ require 'cadence/json'
3
+
4
+ module Cadence
5
+ class Workflow
6
+ module Serializer
7
+ class RecordMarker < Base
8
+ def to_thrift
9
+ CadenceThrift::Decision.new(
10
+ decisionType: CadenceThrift::DecisionType::RecordMarker,
11
+ recordMarkerDecisionAttributes:
12
+ CadenceThrift::RecordMarkerDecisionAttributes.new(
13
+ markerName: object.name,
14
+ details: JSON.serialize(object.details)
15
+ )
16
+ )
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end