@temporalio/core-bridge 0.16.4 → 0.18.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 (170) hide show
  1. package/Cargo.lock +339 -226
  2. package/Cargo.toml +7 -3
  3. package/common.js +50 -0
  4. package/index.d.ts +7 -0
  5. package/index.js +12 -0
  6. package/package.json +7 -4
  7. package/releases/aarch64-apple-darwin/index.node +0 -0
  8. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  9. package/{index.node → releases/index.node} +0 -0
  10. package/releases/x86_64-apple-darwin/index.node +0 -0
  11. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  12. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  13. package/scripts/build.js +10 -50
  14. package/sdk-core/.buildkite/docker/Dockerfile +1 -1
  15. package/sdk-core/.buildkite/docker/docker-compose.yaml +2 -2
  16. package/sdk-core/.buildkite/pipeline.yml +2 -0
  17. package/sdk-core/Cargo.toml +1 -88
  18. package/sdk-core/README.md +30 -6
  19. package/sdk-core/bridge-ffi/Cargo.toml +24 -0
  20. package/sdk-core/bridge-ffi/LICENSE.txt +23 -0
  21. package/sdk-core/bridge-ffi/build.rs +25 -0
  22. package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +216 -0
  23. package/sdk-core/bridge-ffi/src/lib.rs +829 -0
  24. package/sdk-core/bridge-ffi/src/wrappers.rs +193 -0
  25. package/sdk-core/client/Cargo.toml +32 -0
  26. package/sdk-core/{src/pollers/gateway.rs → client/src/lib.rs} +101 -195
  27. package/sdk-core/client/src/metrics.rs +89 -0
  28. package/sdk-core/client/src/mocks.rs +167 -0
  29. package/sdk-core/{src/pollers → client/src}/retry.rs +172 -14
  30. package/sdk-core/core/Cargo.toml +96 -0
  31. package/sdk-core/{src → core/src}/core_tests/activity_tasks.rs +193 -37
  32. package/sdk-core/{src → core/src}/core_tests/child_workflows.rs +14 -14
  33. package/sdk-core/{src → core/src}/core_tests/determinism.rs +8 -8
  34. package/sdk-core/core/src/core_tests/local_activities.rs +328 -0
  35. package/sdk-core/{src → core/src}/core_tests/mod.rs +6 -9
  36. package/sdk-core/{src → core/src}/core_tests/queries.rs +54 -54
  37. package/sdk-core/{src → core/src}/core_tests/replay_flag.rs +8 -12
  38. package/sdk-core/{src → core/src}/core_tests/workers.rs +120 -33
  39. package/sdk-core/{src → core/src}/core_tests/workflow_cancels.rs +16 -26
  40. package/sdk-core/{src → core/src}/core_tests/workflow_tasks.rs +280 -292
  41. package/sdk-core/core/src/lib.rs +374 -0
  42. package/sdk-core/{src → core/src}/log_export.rs +3 -27
  43. package/sdk-core/core/src/pending_activations.rs +162 -0
  44. package/sdk-core/{src → core/src}/pollers/mod.rs +4 -22
  45. package/sdk-core/{src → core/src}/pollers/poll_buffer.rs +1 -1
  46. package/sdk-core/core/src/protosext/mod.rs +396 -0
  47. package/sdk-core/core/src/replay/mod.rs +210 -0
  48. package/sdk-core/core/src/retry_logic.rs +144 -0
  49. package/sdk-core/{src → core/src}/telemetry/metrics.rs +3 -58
  50. package/sdk-core/{src → core/src}/telemetry/mod.rs +8 -8
  51. package/sdk-core/{src → core/src}/telemetry/prometheus_server.rs +0 -0
  52. package/sdk-core/{src → core/src}/test_help/mod.rs +35 -83
  53. package/sdk-core/{src → core/src}/worker/activities/activity_heartbeat_manager.rs +95 -42
  54. package/sdk-core/core/src/worker/activities/local_activities.rs +973 -0
  55. package/sdk-core/{src → core/src}/worker/activities.rs +52 -33
  56. package/sdk-core/{src → core/src}/worker/dispatcher.rs +8 -6
  57. package/sdk-core/{src → core/src}/worker/mod.rs +347 -221
  58. package/sdk-core/core/src/worker/wft_delivery.rs +81 -0
  59. package/sdk-core/{src → core/src}/workflow/bridge.rs +5 -2
  60. package/sdk-core/{src → core/src}/workflow/driven_workflow.rs +17 -7
  61. package/sdk-core/{src → core/src}/workflow/history_update.rs +33 -7
  62. package/sdk-core/{src → core/src/workflow}/machines/activity_state_machine.rs +26 -26
  63. package/sdk-core/{src → core/src/workflow}/machines/cancel_external_state_machine.rs +8 -11
  64. package/sdk-core/{src → core/src/workflow}/machines/cancel_workflow_state_machine.rs +19 -21
  65. package/sdk-core/{src → core/src/workflow}/machines/child_workflow_state_machine.rs +20 -31
  66. package/sdk-core/{src → core/src/workflow}/machines/complete_workflow_state_machine.rs +3 -5
  67. package/sdk-core/{src → core/src/workflow}/machines/continue_as_new_workflow_state_machine.rs +18 -18
  68. package/sdk-core/{src → core/src/workflow}/machines/fail_workflow_state_machine.rs +5 -6
  69. package/sdk-core/core/src/workflow/machines/local_activity_state_machine.rs +1451 -0
  70. package/sdk-core/{src → core/src/workflow}/machines/mod.rs +54 -107
  71. package/sdk-core/{src → core/src/workflow}/machines/mutable_side_effect_state_machine.rs +0 -0
  72. package/sdk-core/{src → core/src/workflow}/machines/patch_state_machine.rs +29 -30
  73. package/sdk-core/{src → core/src/workflow}/machines/side_effect_state_machine.rs +0 -0
  74. package/sdk-core/{src → core/src/workflow}/machines/signal_external_state_machine.rs +17 -19
  75. package/sdk-core/{src → core/src/workflow}/machines/timer_state_machine.rs +20 -21
  76. package/sdk-core/{src → core/src/workflow}/machines/transition_coverage.rs +5 -2
  77. package/sdk-core/{src → core/src/workflow}/machines/upsert_search_attributes_state_machine.rs +0 -0
  78. package/sdk-core/core/src/workflow/machines/workflow_machines/local_acts.rs +96 -0
  79. package/sdk-core/{src → core/src/workflow}/machines/workflow_machines.rs +357 -171
  80. package/sdk-core/{src → core/src/workflow}/machines/workflow_task_state_machine.rs +1 -1
  81. package/sdk-core/{src → core/src}/workflow/mod.rs +200 -39
  82. package/sdk-core/{src → core/src}/workflow/workflow_tasks/cache_manager.rs +0 -0
  83. package/sdk-core/{src → core/src}/workflow/workflow_tasks/concurrency_manager.rs +38 -5
  84. package/sdk-core/{src → core/src}/workflow/workflow_tasks/mod.rs +317 -103
  85. package/sdk-core/{test_utils → core-api}/Cargo.toml +10 -7
  86. package/sdk-core/{src → core-api/src}/errors.rs +42 -92
  87. package/sdk-core/core-api/src/lib.rs +158 -0
  88. package/sdk-core/{src/worker/config.rs → core-api/src/worker.rs} +18 -23
  89. package/sdk-core/etc/deps.svg +156 -0
  90. package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +5 -5
  91. package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +3 -5
  92. package/sdk-core/fsm/rustfsm_trait/src/lib.rs +7 -1
  93. package/sdk-core/histories/fail_wf_task.bin +0 -0
  94. package/sdk-core/histories/timer_workflow_history.bin +0 -0
  95. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +44 -13
  96. package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +19 -1
  97. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +1 -1
  98. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +9 -0
  99. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +1 -0
  100. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +1 -0
  101. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +13 -0
  102. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +14 -7
  103. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +176 -18
  104. package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +6 -0
  105. package/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +11 -0
  106. package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +3 -0
  107. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +156 -7
  108. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +135 -104
  109. package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +78 -0
  110. package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +78 -0
  111. package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +205 -0
  112. package/sdk-core/protos/local/temporal/sdk/core/bridge/service.proto +61 -0
  113. package/sdk-core/protos/local/{child_workflow.proto → temporal/sdk/core/child_workflow/child_workflow.proto} +1 -1
  114. package/sdk-core/protos/local/{common.proto → temporal/sdk/core/common/common.proto} +5 -3
  115. package/sdk-core/protos/local/{core_interface.proto → temporal/sdk/core/core_interface.proto} +10 -10
  116. package/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +30 -0
  117. package/sdk-core/protos/local/{workflow_activation.proto → temporal/sdk/core/workflow_activation/workflow_activation.proto} +35 -11
  118. package/sdk-core/protos/local/{workflow_commands.proto → temporal/sdk/core/workflow_commands/workflow_commands.proto} +55 -4
  119. package/sdk-core/protos/local/{workflow_completion.proto → temporal/sdk/core/workflow_completion/workflow_completion.proto} +3 -3
  120. package/sdk-core/sdk/Cargo.toml +32 -0
  121. package/sdk-core/{src/prototype_rust_sdk → sdk/src}/conversions.rs +0 -0
  122. package/sdk-core/sdk/src/lib.rs +699 -0
  123. package/sdk-core/sdk/src/payload_converter.rs +11 -0
  124. package/sdk-core/sdk/src/workflow_context/options.rs +180 -0
  125. package/sdk-core/{src/prototype_rust_sdk → sdk/src}/workflow_context.rs +201 -124
  126. package/sdk-core/{src/prototype_rust_sdk → sdk/src}/workflow_future.rs +63 -30
  127. package/sdk-core/sdk-core-protos/Cargo.toml +10 -0
  128. package/sdk-core/sdk-core-protos/build.rs +28 -6
  129. package/sdk-core/sdk-core-protos/src/constants.rs +7 -0
  130. package/sdk-core/{src/test_help → sdk-core-protos/src}/history_builder.rs +134 -49
  131. package/sdk-core/sdk-core-protos/src/history_info.rs +216 -0
  132. package/sdk-core/sdk-core-protos/src/lib.rs +601 -168
  133. package/sdk-core/sdk-core-protos/src/task_token.rs +38 -0
  134. package/sdk-core/sdk-core-protos/src/utilities.rs +14 -0
  135. package/sdk-core/test-utils/Cargo.toml +32 -0
  136. package/sdk-core/{src/test_help → test-utils/src}/canned_histories.rs +59 -78
  137. package/sdk-core/test-utils/src/histfetch.rs +28 -0
  138. package/sdk-core/{test_utils → test-utils}/src/lib.rs +131 -68
  139. package/sdk-core/tests/integ_tests/client_tests.rs +1 -1
  140. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +11 -7
  141. package/sdk-core/tests/integ_tests/polling_tests.rs +12 -11
  142. package/sdk-core/tests/integ_tests/queries_tests.rs +82 -78
  143. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +91 -71
  144. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +3 -4
  145. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +2 -4
  146. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +4 -6
  147. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +4 -6
  148. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -4
  149. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +496 -0
  150. package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +5 -8
  151. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +125 -0
  152. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +7 -13
  153. package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +33 -5
  154. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +12 -16
  155. package/sdk-core/tests/integ_tests/workflow_tests.rs +85 -82
  156. package/sdk-core/tests/load_tests.rs +6 -6
  157. package/sdk-core/tests/main.rs +2 -2
  158. package/src/conversions.rs +24 -21
  159. package/src/errors.rs +8 -0
  160. package/src/lib.rs +323 -211
  161. package/sdk-core/protos/local/activity_result.proto +0 -46
  162. package/sdk-core/protos/local/activity_task.proto +0 -66
  163. package/sdk-core/src/core_tests/retry.rs +0 -147
  164. package/sdk-core/src/lib.rs +0 -403
  165. package/sdk-core/src/machines/local_activity_state_machine.rs +0 -117
  166. package/sdk-core/src/pending_activations.rs +0 -249
  167. package/sdk-core/src/protosext/mod.rs +0 -160
  168. package/sdk-core/src/prototype_rust_sdk.rs +0 -412
  169. package/sdk-core/src/task_token.rs +0 -20
  170. package/sdk-core/src/test_help/history_info.rs +0 -157
@@ -0,0 +1,89 @@
1
+ use opentelemetry::{
2
+ metrics::{Counter, Meter, ValueRecorder},
3
+ KeyValue,
4
+ };
5
+ use std::{sync::Arc, time::Duration};
6
+
7
+ /// Used to track context associated with metrics, and record/update them
8
+ ///
9
+ /// Possible improvement: make generic over some type tag so that methods are only exposed if the
10
+ /// appropriate k/vs have already been set.
11
+ #[derive(Clone, Debug)]
12
+ pub(crate) struct MetricsContext {
13
+ kvs: Arc<Vec<KeyValue>>,
14
+ poll_is_long: bool,
15
+
16
+ svc_request: Counter<u64>,
17
+ svc_request_failed: Counter<u64>,
18
+ long_svc_request: Counter<u64>,
19
+ long_svc_request_failed: Counter<u64>,
20
+
21
+ svc_request_latency: ValueRecorder<u64>,
22
+ long_svc_request_latency: ValueRecorder<u64>,
23
+ }
24
+
25
+ impl MetricsContext {
26
+ fn new(kvs: Vec<KeyValue>, meter: &Meter) -> Self {
27
+ Self {
28
+ kvs: Arc::new(kvs),
29
+ poll_is_long: false,
30
+ svc_request: meter.u64_counter("request").init(),
31
+ svc_request_failed: meter.u64_counter("request_failure").init(),
32
+ long_svc_request: meter.u64_counter("long_request").init(),
33
+ long_svc_request_failed: meter.u64_counter("long_request_failure").init(),
34
+ svc_request_latency: meter.u64_value_recorder("request_latency").init(),
35
+ long_svc_request_latency: meter.u64_value_recorder("long_request_latency").init(),
36
+ }
37
+ }
38
+
39
+ pub(crate) fn top_level(namespace: String, meter: &Meter) -> Self {
40
+ Self::new(vec![KeyValue::new(KEY_NAMESPACE, namespace)], meter)
41
+ }
42
+
43
+ /// Extend an existing metrics context with new attributes
44
+ pub(crate) fn with_new_attrs(&self, new_kvs: impl IntoIterator<Item = KeyValue>) -> Self {
45
+ let mut r = self.clone();
46
+ Arc::make_mut(&mut r.kvs).extend(new_kvs);
47
+ r
48
+ }
49
+
50
+ pub(crate) fn set_is_long_poll(&mut self) {
51
+ self.poll_is_long = true;
52
+ }
53
+
54
+ /// A request to the temporal service was made
55
+ pub(crate) fn svc_request(&self) {
56
+ if self.poll_is_long {
57
+ self.long_svc_request.add(1, &self.kvs);
58
+ } else {
59
+ self.svc_request.add(1, &self.kvs);
60
+ }
61
+ }
62
+
63
+ /// A request to the temporal service failed
64
+ pub(crate) fn svc_request_failed(&self) {
65
+ if self.poll_is_long {
66
+ self.long_svc_request_failed.add(1, &self.kvs);
67
+ } else {
68
+ self.svc_request_failed.add(1, &self.kvs);
69
+ }
70
+ }
71
+
72
+ /// Record service request latency
73
+ pub(crate) fn record_svc_req_latency(&self, dur: Duration) {
74
+ if self.poll_is_long {
75
+ self.long_svc_request_latency
76
+ .record(dur.as_millis() as u64, &self.kvs);
77
+ } else {
78
+ self.svc_request_latency
79
+ .record(dur.as_millis() as u64, &self.kvs);
80
+ }
81
+ }
82
+ }
83
+
84
+ const KEY_NAMESPACE: &str = "namespace";
85
+ const KEY_SVC_METHOD: &str = "operation";
86
+
87
+ pub(crate) fn svc_operation(op: String) -> KeyValue {
88
+ KeyValue::new(KEY_SVC_METHOD, op)
89
+ }
@@ -0,0 +1,167 @@
1
+ //! Helpers for mocking
2
+
3
+ use super::*;
4
+ use crate::{MockServerGatewayApis, ServerGatewayOptions, ServerGatewayOptionsBuilder};
5
+ use std::str::FromStr;
6
+ use url::Url;
7
+
8
+ /// Create a mock client primed with basic necessary expectations
9
+ pub fn mock_gateway() -> MockServerGatewayApis {
10
+ let mut mg = MockServerGatewayApis::new();
11
+ mg.expect_get_options().return_const(fake_sg_opts());
12
+ mg
13
+ }
14
+
15
+ /// Create a mock manual client primed with basic necessary expectations
16
+ pub fn mock_manual_gateway() -> MockManualGateway {
17
+ let mut mg = MockManualGateway::new();
18
+ mg.expect_get_options().return_const(fake_sg_opts());
19
+ mg
20
+ }
21
+
22
+ /// Returns some totally fake client options for use with mock clients
23
+ pub fn fake_sg_opts() -> ServerGatewayOptions {
24
+ ServerGatewayOptionsBuilder::default()
25
+ .target_url(Url::from_str("https://fake").unwrap())
26
+ .namespace("test_namespace".to_string())
27
+ .client_name("fake_client".to_string())
28
+ .client_version("fake_version".to_string())
29
+ .worker_binary_id("fake_binid".to_string())
30
+ .build()
31
+ .unwrap()
32
+ }
33
+
34
+ // Need a version of the mock that can return futures so we can return potentially pending
35
+ // results. This is really annoying b/c of the async trait stuff. Need
36
+ // https://github.com/asomers/mockall/issues/189 to be fixed for it to go away.
37
+ mockall::mock! {
38
+ pub ManualGateway {}
39
+ impl ServerGatewayApis for ManualGateway {
40
+ fn start_workflow<'a, 'b>(
41
+ &self,
42
+ input: Vec<Payload>,
43
+ task_queue: String,
44
+ workflow_id: String,
45
+ workflow_type: String,
46
+ task_timeout: Option<Duration>,
47
+ ) -> impl Future<Output = Result<StartWorkflowExecutionResponse>> + Send + 'b
48
+ where 'a: 'b, Self: 'b;
49
+
50
+ fn poll_workflow_task<'a, 'b>(&'a self, task_queue: String, is_sticky: bool)
51
+ -> impl Future<Output = Result<PollWorkflowTaskQueueResponse>> + Send + 'b
52
+ where 'a: 'b, Self: 'b;
53
+
54
+ fn poll_activity_task<'a, 'b>(&self, task_queue: String)
55
+ -> impl Future<Output = Result<PollActivityTaskQueueResponse>> + Send + 'b
56
+ where 'a: 'b, Self: 'b;
57
+
58
+ fn reset_sticky_task_queue<'a, 'b>(
59
+ &self,
60
+ workflow_id: String,
61
+ run_id: String,
62
+ ) -> impl Future<Output = Result<ResetStickyTaskQueueResponse>> + Send + 'b
63
+ where 'a: 'b, Self: 'b;
64
+
65
+ fn complete_workflow_task<'a, 'b>(
66
+ &self,
67
+ request: WorkflowTaskCompletion,
68
+ ) -> impl Future<Output = Result<RespondWorkflowTaskCompletedResponse>> + Send + 'b
69
+ where 'a: 'b, Self: 'b;
70
+
71
+ fn complete_activity_task<'a, 'b>(
72
+ &self,
73
+ task_token: TaskToken,
74
+ result: Option<Payloads>,
75
+ ) -> impl Future<Output = Result<RespondActivityTaskCompletedResponse>> + Send + 'b
76
+ where 'a: 'b, Self: 'b;
77
+
78
+ fn cancel_activity_task<'a, 'b>(
79
+ &self,
80
+ task_token: TaskToken,
81
+ details: Option<Payloads>,
82
+ ) -> impl Future<Output = Result<RespondActivityTaskCanceledResponse>> + Send + 'b
83
+ where 'a: 'b, Self: 'b;
84
+
85
+ fn fail_activity_task<'a, 'b>(
86
+ &self,
87
+ task_token: TaskToken,
88
+ failure: Option<Failure>,
89
+ ) -> impl Future<Output = Result<RespondActivityTaskFailedResponse>> + Send + 'b
90
+ where 'a: 'b, Self: 'b;
91
+
92
+ fn fail_workflow_task<'a, 'b>(
93
+ &self,
94
+ task_token: TaskToken,
95
+ cause: WorkflowTaskFailedCause,
96
+ failure: Option<Failure>,
97
+ ) -> impl Future<Output = Result<RespondWorkflowTaskFailedResponse>> + Send + 'b
98
+ where 'a: 'b, Self: 'b;
99
+
100
+ fn signal_workflow_execution<'a, 'b>(
101
+ &self,
102
+ workflow_id: String,
103
+ run_id: String,
104
+ signal_name: String,
105
+ payloads: Option<Payloads>,
106
+ ) -> impl Future<Output = Result<SignalWorkflowExecutionResponse>> + Send + 'b
107
+ where 'a: 'b, Self: 'b;
108
+
109
+ fn record_activity_heartbeat<'a, 'b>(
110
+ &self,
111
+ task_token: TaskToken,
112
+ details: Option<Payloads>,
113
+ ) -> impl Future<Output = Result<RecordActivityTaskHeartbeatResponse>> + Send + 'b
114
+ where 'a: 'b, Self: 'b;
115
+
116
+ fn query_workflow_execution<'a, 'b>(
117
+ &self,
118
+ workflow_id: String,
119
+ run_id: String,
120
+ query: WorkflowQuery,
121
+ ) -> impl Future<Output = Result<QueryWorkflowResponse>> + Send + 'b
122
+ where 'a: 'b, Self: 'b;
123
+
124
+ fn describe_workflow_execution<'a, 'b>(
125
+ &self,
126
+ workflow_id: String,
127
+ run_id: Option<String>,
128
+ ) -> impl Future<Output = Result<DescribeWorkflowExecutionResponse>> + Send + 'b
129
+ where 'a: 'b, Self: 'b;
130
+
131
+ fn get_workflow_execution_history<'a, 'b>(
132
+ &self,
133
+ workflow_id: String,
134
+ run_id: Option<String>,
135
+ page_token: Vec<u8>
136
+ ) -> impl Future<Output = Result<GetWorkflowExecutionHistoryResponse>> + Send + 'b
137
+ where 'a: 'b, Self: 'b;
138
+
139
+ fn respond_legacy_query<'a, 'b>(
140
+ &self,
141
+ task_token: TaskToken,
142
+ query_result: QueryResult,
143
+ ) -> impl Future<Output = Result<RespondQueryTaskCompletedResponse>> + Send + 'b
144
+ where 'a: 'b, Self: 'b;
145
+
146
+ fn cancel_workflow_execution<'a, 'b>(
147
+ &self,
148
+ workflow_id: String,
149
+ run_id: Option<String>,
150
+ ) -> impl Future<Output = Result<RequestCancelWorkflowExecutionResponse>> + Send + 'b
151
+ where 'a: 'b, Self: 'b;
152
+
153
+ fn terminate_workflow_execution<'a, 'b>(
154
+ &self,
155
+ workflow_id: String,
156
+ run_id: Option<String>,
157
+ ) -> impl Future<Output = Result<TerminateWorkflowExecutionResponse>> + Send + 'b
158
+ where 'a: 'b, Self: 'b;
159
+
160
+ fn list_namespaces<'a, 'b>(
161
+ &self,
162
+ ) -> impl Future<Output = Result<ListNamespacesResponse>> + Send + 'b
163
+ where 'a: 'b, Self: 'b;
164
+
165
+ fn get_options(&self) -> &ServerGatewayOptions;
166
+ }
167
+ }
@@ -1,11 +1,4 @@
1
- use crate::{
2
- pollers::{
3
- gateway::{RetryConfig, ServerGatewayApis},
4
- Result, RETRYABLE_ERROR_CODES,
5
- },
6
- protosext::WorkflowTaskCompletion,
7
- task_token::TaskToken,
8
- };
1
+ use crate::{Result, RetryConfig, ServerGatewayApis, ServerGatewayOptions, WorkflowTaskCompletion};
9
2
  use backoff::{backoff::Backoff, ExponentialBackoff};
10
3
  use futures_retry::{ErrorHandler, FutureRetry, RetryPolicy};
11
4
  use std::{fmt::Debug, future::Future, time::Duration};
@@ -15,9 +8,21 @@ use temporal_sdk_core_protos::{
15
8
  common::v1::Payloads, enums::v1::WorkflowTaskFailedCause, failure::v1::Failure,
16
9
  query::v1::WorkflowQuery, workflowservice::v1::*,
17
10
  },
11
+ TaskToken,
18
12
  };
19
13
  use tonic::Code;
20
14
 
15
+ /// List of gRPC error codes that client will retry.
16
+ pub const RETRYABLE_ERROR_CODES: [Code; 7] = [
17
+ Code::DataLoss,
18
+ Code::Internal,
19
+ Code::Unknown,
20
+ Code::ResourceExhausted,
21
+ Code::Aborted,
22
+ Code::OutOfRange,
23
+ Code::Unavailable,
24
+ ];
25
+
21
26
  #[derive(Debug)]
22
27
  /// A wrapper for a [ServerGatewayApis] implementor which performs auto-retries
23
28
  pub struct RetryGateway<SG> {
@@ -103,18 +108,18 @@ impl ErrorHandler<tonic::Status> for TonicErrorHandler {
103
108
  return RetryPolicy::ForwardError(e);
104
109
  }
105
110
 
106
- if current_attempt == 1 {
107
- debug!(error=?e, "gRPC call {} failed on first attempt", self.call_name);
108
- } else if self.should_log_retry_warning(current_attempt) {
109
- warn!(error=?e, "gRPC call {} retried {} times", self.call_name, current_attempt);
110
- }
111
-
112
111
  // Long polls are OK with being cancelled or running into the timeout because there's
113
112
  // nothing to do but retry anyway
114
113
  let long_poll_allowed = self.call_type == CallType::LongPoll
115
114
  && [Code::Cancelled, Code::DeadlineExceeded].contains(&e.code());
116
115
 
117
116
  if RETRYABLE_ERROR_CODES.contains(&e.code()) || long_poll_allowed {
117
+ if current_attempt == 1 {
118
+ debug!(error=?e, "gRPC call {} failed on first attempt", self.call_name);
119
+ } else if self.should_log_retry_warning(current_attempt) {
120
+ warn!(error=?e, "gRPC call {} retried {} times", self.call_name, current_attempt);
121
+ }
122
+
118
123
  match self.backoff.next_backoff() {
119
124
  None => RetryPolicy::ForwardError(e), // None is returned when we've ran out of time
120
125
  Some(backoff) => {
@@ -395,4 +400,157 @@ impl<SG: ServerGatewayApis + Send + Sync + 'static> ServerGatewayApis for RetryG
395
400
  async fn list_namespaces(&self) -> Result<ListNamespacesResponse> {
396
401
  retry_call!(self, CallType::Normal, list_namespaces,)
397
402
  }
403
+
404
+ fn get_options(&self) -> &ServerGatewayOptions {
405
+ self.gateway.get_options()
406
+ }
407
+ }
408
+
409
+ #[cfg(test)]
410
+ mod tests {
411
+ use super::*;
412
+ use crate::MockServerGatewayApis;
413
+ use tonic::Status;
414
+
415
+ #[tokio::test]
416
+ async fn non_retryable_errors() {
417
+ for code in [
418
+ Code::InvalidArgument,
419
+ Code::NotFound,
420
+ Code::AlreadyExists,
421
+ Code::PermissionDenied,
422
+ Code::FailedPrecondition,
423
+ Code::Cancelled,
424
+ Code::DeadlineExceeded,
425
+ Code::Unauthenticated,
426
+ Code::Unimplemented,
427
+ ] {
428
+ let mut mock_gateway = MockServerGatewayApis::new();
429
+ mock_gateway
430
+ .expect_cancel_activity_task()
431
+ .returning(move |_, _| Err(Status::new(code, "non-retryable failure")))
432
+ .times(1);
433
+ let retry_gateway = RetryGateway::new(mock_gateway, Default::default());
434
+ let result = retry_gateway
435
+ .cancel_activity_task(vec![1].into(), None)
436
+ .await;
437
+ // Expecting an error after a single attempt, since there was a non-retryable error.
438
+ assert!(result.is_err());
439
+ }
440
+ }
441
+
442
+ #[tokio::test]
443
+ async fn long_poll_non_retryable_errors() {
444
+ for code in [
445
+ Code::InvalidArgument,
446
+ Code::NotFound,
447
+ Code::AlreadyExists,
448
+ Code::PermissionDenied,
449
+ Code::FailedPrecondition,
450
+ Code::Unauthenticated,
451
+ Code::Unimplemented,
452
+ ] {
453
+ let mut mock_gateway = MockServerGatewayApis::new();
454
+ mock_gateway
455
+ .expect_poll_workflow_task()
456
+ .returning(move |_, _| Err(Status::new(code, "non-retryable failure")))
457
+ .times(1);
458
+ mock_gateway
459
+ .expect_poll_activity_task()
460
+ .returning(move |_| Err(Status::new(code, "non-retryable failure")))
461
+ .times(1);
462
+ let retry_gateway = RetryGateway::new(mock_gateway, Default::default());
463
+ let result = retry_gateway
464
+ .poll_workflow_task("tq".to_string(), false)
465
+ .await;
466
+ assert!(result.is_err());
467
+ let result = retry_gateway.poll_activity_task("tq".to_string()).await;
468
+ assert!(result.is_err());
469
+ }
470
+ }
471
+
472
+ #[tokio::test]
473
+ async fn retryable_errors() {
474
+ for code in RETRYABLE_ERROR_CODES {
475
+ let mut mock_gateway = MockServerGatewayApis::new();
476
+ mock_gateway
477
+ .expect_cancel_activity_task()
478
+ .returning(move |_, _| Err(Status::new(code, "retryable failure")))
479
+ .times(3);
480
+ mock_gateway
481
+ .expect_cancel_activity_task()
482
+ .returning(|_, _| Ok(Default::default()))
483
+ .times(1);
484
+
485
+ let retry_gateway = RetryGateway::new(mock_gateway, Default::default());
486
+ let result = retry_gateway
487
+ .cancel_activity_task(vec![1].into(), None)
488
+ .await;
489
+ // Expecting successful response after retries
490
+ assert!(result.is_ok());
491
+ }
492
+ }
493
+
494
+ #[tokio::test]
495
+ async fn long_poll_retries_forever() {
496
+ let mut mock_gateway = MockServerGatewayApis::new();
497
+ mock_gateway
498
+ .expect_poll_workflow_task()
499
+ .returning(move |_, _| Err(Status::new(Code::Unknown, "retryable failure")))
500
+ .times(50);
501
+ mock_gateway
502
+ .expect_poll_workflow_task()
503
+ .returning(|_, _| Ok(Default::default()))
504
+ .times(1);
505
+ mock_gateway
506
+ .expect_poll_activity_task()
507
+ .returning(move |_| Err(Status::new(Code::Unknown, "retryable failure")))
508
+ .times(50);
509
+ mock_gateway
510
+ .expect_poll_activity_task()
511
+ .returning(|_| Ok(Default::default()))
512
+ .times(1);
513
+
514
+ let retry_gateway = RetryGateway::new(mock_gateway, Default::default());
515
+
516
+ let result = retry_gateway
517
+ .poll_workflow_task("tq".to_string(), false)
518
+ .await;
519
+ assert!(result.is_ok());
520
+ let result = retry_gateway.poll_activity_task("tq".to_string()).await;
521
+ assert!(result.is_ok());
522
+ }
523
+
524
+ #[tokio::test]
525
+ async fn long_poll_retries_deadline_exceeded() {
526
+ // For some reason we will get cancelled in these situations occasionally (always?) too
527
+ for code in [Code::Cancelled, Code::DeadlineExceeded] {
528
+ let mut mock_gateway = MockServerGatewayApis::new();
529
+ mock_gateway
530
+ .expect_poll_workflow_task()
531
+ .returning(move |_, _| Err(Status::new(code, "retryable failure")))
532
+ .times(5);
533
+ mock_gateway
534
+ .expect_poll_workflow_task()
535
+ .returning(|_, _| Ok(Default::default()))
536
+ .times(1);
537
+ mock_gateway
538
+ .expect_poll_activity_task()
539
+ .returning(move |_| Err(Status::new(code, "retryable failure")))
540
+ .times(5);
541
+ mock_gateway
542
+ .expect_poll_activity_task()
543
+ .returning(|_| Ok(Default::default()))
544
+ .times(1);
545
+
546
+ let retry_gateway = RetryGateway::new(mock_gateway, Default::default());
547
+
548
+ let result = retry_gateway
549
+ .poll_workflow_task("tq".to_string(), false)
550
+ .await;
551
+ assert!(result.is_ok());
552
+ let result = retry_gateway.poll_activity_task("tq".to_string()).await;
553
+ assert!(result.is_ok());
554
+ }
555
+ }
398
556
  }
@@ -0,0 +1,96 @@
1
+ [package]
2
+ name = "temporal-sdk-core"
3
+ version = "0.1.0"
4
+ authors = ["Spencer Judge <spencer@temporal.io>", "Vitaly Arbuzov <vitaly@temporal.io>"]
5
+ edition = "2021"
6
+ license-file = "LICENSE.txt"
7
+ description = "Library for building new Temporal SDKs"
8
+ homepage = "https://temporal.io/"
9
+ repository = "https://github.com/temporalio/sdk-core"
10
+ keywords = ["temporal", "workflow"]
11
+ categories = ["development-tools"]
12
+
13
+ [lib]
14
+
15
+ [dependencies]
16
+ anyhow = "1.0"
17
+ arc-swap = "1.3"
18
+ async-trait = "0.1"
19
+ base64 = "0.13"
20
+ crossbeam = "0.8"
21
+ dashmap = "5.0"
22
+ derive_builder = "0.10"
23
+ derive_more = "0.99"
24
+ enum_dispatch = "0.3"
25
+ futures = "0.3"
26
+ http = "0.2"
27
+ hyper = "0.14"
28
+ itertools = "0.10"
29
+ lazy_static = "1.4"
30
+ log = "0.4"
31
+ lru = "0.7"
32
+ once_cell = "1.5"
33
+ opentelemetry = { version = "0.16", features = ["rt-tokio"] }
34
+ opentelemetry-otlp = { version = "0.9.0", features = ["tokio", "metrics"] }
35
+ opentelemetry-prometheus = "0.9.0"
36
+ parking_lot = { version = "0.11", features = ["send_guard"] }
37
+ prometheus = "0.12"
38
+ prost = "0.9"
39
+ prost-types = "0.9"
40
+ rand = "0.8.3"
41
+ ringbuf = "0.2"
42
+ serde = "1.0"
43
+ slotmap = "1.0"
44
+ thiserror = "1.0"
45
+ tokio = { version = "1.1", features = ["rt", "rt-multi-thread", "parking_lot", "time", "fs"] }
46
+ tokio-util = { version = "0.6.9" }
47
+ tokio-stream = "0.1"
48
+ tonic = { version = "0.6", features = ["tls", "tls-roots"] }
49
+ tracing = { version = "0.1", features = ["log-always"] }
50
+ tracing-futures = "0.2"
51
+ tracing-opentelemetry = "0.16"
52
+ tracing-subscriber = { version = "0.3", features = ["parking_lot", "env-filter"] }
53
+ url = "2.2"
54
+ uuid = { version = "0.8.2", features = ["v4"] }
55
+
56
+ # 1st party local deps
57
+ [dependencies.temporal-sdk-core-api]
58
+ path = "../core-api"
59
+ version = "0.1"
60
+
61
+ [dependencies.temporal-sdk-core-protos]
62
+ path = "../sdk-core-protos"
63
+ version = "0.1"
64
+ features = ["history_builders"]
65
+
66
+ [dependencies.temporal-client]
67
+ path = "../client"
68
+ version = "0.1"
69
+ features = ["mocks"]
70
+
71
+ [dependencies.rustfsm]
72
+ path = "../fsm"
73
+ version = "0.1"
74
+
75
+ [dev-dependencies]
76
+ assert_matches = "1.4"
77
+ bimap = "0.6.1"
78
+ mockall = "0.11"
79
+ rstest = "0.12"
80
+ temporal-sdk-core-test-utils = { path = "../test-utils" }
81
+ temporal-sdk = { path = "../sdk" }
82
+
83
+ [build-dependencies]
84
+ tonic-build = "0.6"
85
+
86
+ [[test]]
87
+ name = "integ_tests"
88
+ path = "../tests/main.rs"
89
+ # Prevents autodiscovery, and hence these getting run with `cargo test`. Run with
90
+ # `cargo test --test integ_tests`
91
+ test = false
92
+
93
+ [[test]]
94
+ name = "load_tests"
95
+ path = "../tests/load_tests.rs"
96
+ test = false