@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,193 @@
1
+ use std::str::FromStr;
2
+ use temporal_sdk_core_protos::coresdk::bridge;
3
+
4
+ // Present for try-from only
5
+ pub struct CoreInitOptions(pub bridge::InitRequest);
6
+
7
+ impl TryFrom<CoreInitOptions> for temporal_sdk_core::CoreInitOptions {
8
+ type Error = String;
9
+
10
+ fn try_from(CoreInitOptions(req): CoreInitOptions) -> Result<Self, Self::Error> {
11
+ let mut core_opts = temporal_sdk_core::CoreInitOptionsBuilder::default();
12
+ if let Some(req_gateway_opts) = req.gateway_options {
13
+ let mut gateway_opts = temporal_sdk_core::ServerGatewayOptionsBuilder::default();
14
+ if !req_gateway_opts.target_url.is_empty() {
15
+ gateway_opts.target_url(
16
+ temporal_sdk_core::Url::parse(&req_gateway_opts.target_url)
17
+ .map_err(|err| format!("invalid target URL: {}", err))?,
18
+ );
19
+ }
20
+ if !req_gateway_opts.namespace.is_empty() {
21
+ gateway_opts.namespace(req_gateway_opts.namespace);
22
+ }
23
+ if !req_gateway_opts.client_name.is_empty() {
24
+ gateway_opts.client_name(req_gateway_opts.client_name);
25
+ }
26
+ if !req_gateway_opts.client_version.is_empty() {
27
+ gateway_opts.client_version(req_gateway_opts.client_version);
28
+ }
29
+ if !req_gateway_opts.static_headers.is_empty() {
30
+ gateway_opts.static_headers(req_gateway_opts.static_headers);
31
+ }
32
+ if !req_gateway_opts.identity.is_empty() {
33
+ gateway_opts.identity(req_gateway_opts.identity);
34
+ }
35
+ if !req_gateway_opts.worker_binary_id.is_empty() {
36
+ gateway_opts.worker_binary_id(req_gateway_opts.worker_binary_id);
37
+ }
38
+ if let Some(req_tls_config) = req_gateway_opts.tls_config {
39
+ let mut tls_config = temporal_sdk_core::TlsConfig::default();
40
+ if !req_tls_config.server_root_ca_cert.is_empty() {
41
+ tls_config.server_root_ca_cert = Some(req_tls_config.server_root_ca_cert);
42
+ }
43
+ if !req_tls_config.domain.is_empty() {
44
+ tls_config.domain = Some(req_tls_config.domain);
45
+ }
46
+ if !req_tls_config.client_cert.is_empty()
47
+ || !req_tls_config.client_private_key.is_empty()
48
+ {
49
+ tls_config.client_tls_config = Some(temporal_sdk_core::ClientTlsConfig {
50
+ client_cert: req_tls_config.client_cert,
51
+ client_private_key: req_tls_config.client_private_key,
52
+ })
53
+ }
54
+ gateway_opts.tls_cfg(tls_config);
55
+ }
56
+ if let Some(req_retry_config) = req_gateway_opts.retry_config {
57
+ let mut retry_config = temporal_sdk_core::RetryConfig::default();
58
+ if let Some(v) = req_retry_config.initial_interval {
59
+ retry_config.initial_interval =
60
+ v.try_into().map_err(|_| "invalid initial interval")?;
61
+ }
62
+ if let Some(v) = req_retry_config.randomization_factor {
63
+ retry_config.randomization_factor = v;
64
+ }
65
+ if let Some(v) = req_retry_config.multiplier {
66
+ retry_config.multiplier = v;
67
+ }
68
+ if let Some(v) = req_retry_config.max_interval {
69
+ retry_config.max_interval = v.try_into().map_err(|_| "invalid max interval")?;
70
+ }
71
+ if let Some(v) = req_retry_config.max_elapsed_time {
72
+ retry_config.max_elapsed_time =
73
+ Some(v.try_into().map_err(|_| "invalid max elapsed time")?);
74
+ }
75
+ if let Some(v) = req_retry_config.max_retries {
76
+ retry_config.max_retries = v as usize;
77
+ }
78
+ gateway_opts.retry_config(retry_config);
79
+ }
80
+ core_opts.gateway_opts(
81
+ gateway_opts
82
+ .build()
83
+ .map_err(|err| format!("invalid gateway options: {}", err))?,
84
+ );
85
+ }
86
+ if let Some(req_telemetry_opts) = req.telemetry_options {
87
+ let mut telemetry_opts = temporal_sdk_core::TelemetryOptionsBuilder::default();
88
+ if !req_telemetry_opts.otel_collector_url.is_empty() {
89
+ telemetry_opts.otel_collector_url(
90
+ temporal_sdk_core::Url::parse(&req_telemetry_opts.otel_collector_url)
91
+ .map_err(|err| format!("invalid OpenTelemetry collector URL: {}", err))?,
92
+ );
93
+ }
94
+ if !req_telemetry_opts.tracing_filter.is_empty() {
95
+ telemetry_opts.tracing_filter(req_telemetry_opts.tracing_filter.clone());
96
+ }
97
+ match req_telemetry_opts.log_forwarding_level() {
98
+ bridge::LogLevel::Unspecified => {}
99
+ bridge::LogLevel::Off => {
100
+ telemetry_opts.log_forwarding_level(log::LevelFilter::Off);
101
+ }
102
+ bridge::LogLevel::Error => {
103
+ telemetry_opts.log_forwarding_level(log::LevelFilter::Error);
104
+ }
105
+ bridge::LogLevel::Warn => {
106
+ telemetry_opts.log_forwarding_level(log::LevelFilter::Warn);
107
+ }
108
+ bridge::LogLevel::Info => {
109
+ telemetry_opts.log_forwarding_level(log::LevelFilter::Info);
110
+ }
111
+ bridge::LogLevel::Debug => {
112
+ telemetry_opts.log_forwarding_level(log::LevelFilter::Debug);
113
+ }
114
+ bridge::LogLevel::Trace => {
115
+ telemetry_opts.log_forwarding_level(log::LevelFilter::Trace);
116
+ }
117
+ }
118
+ if !req_telemetry_opts.prometheus_export_bind_address.is_empty() {
119
+ telemetry_opts.prometheus_export_bind_address(
120
+ std::net::SocketAddr::from_str(
121
+ &req_telemetry_opts.prometheus_export_bind_address,
122
+ )
123
+ .map_err(|err| format!("invalid Prometheus address: {}", err))?,
124
+ );
125
+ }
126
+ core_opts.telemetry_opts(
127
+ telemetry_opts
128
+ .build()
129
+ .map_err(|err| format!("invalid telemetry options: {}", err))?,
130
+ );
131
+ }
132
+ core_opts
133
+ .build()
134
+ .map_err(|err| format!("invalid options: {}", err))
135
+ }
136
+ }
137
+
138
+ // Present for try-from only
139
+ pub struct WorkerConfig(pub bridge::RegisterWorkerRequest);
140
+
141
+ impl TryFrom<WorkerConfig> for temporal_sdk_core_api::worker::WorkerConfig {
142
+ type Error = String;
143
+
144
+ fn try_from(WorkerConfig(req): WorkerConfig) -> Result<Self, Self::Error> {
145
+ let mut config = temporal_sdk_core_api::worker::WorkerConfigBuilder::default();
146
+ if !req.task_queue.is_empty() {
147
+ config.task_queue(req.task_queue);
148
+ }
149
+ if let Some(v) = req.max_cached_workflows {
150
+ config.max_cached_workflows(v as usize);
151
+ }
152
+ if let Some(v) = req.max_outstanding_workflow_tasks {
153
+ config.max_outstanding_workflow_tasks(v as usize);
154
+ }
155
+ if let Some(v) = req.max_outstanding_activities {
156
+ config.max_outstanding_activities(v as usize);
157
+ }
158
+ if let Some(v) = req.max_outstanding_local_activities {
159
+ config.max_outstanding_local_activities(v as usize);
160
+ }
161
+ if let Some(v) = req.max_concurrent_wft_polls {
162
+ config.max_concurrent_wft_polls(v as usize);
163
+ }
164
+ if let Some(v) = req.nonsticky_to_sticky_poll_ratio {
165
+ config.nonsticky_to_sticky_poll_ratio(v);
166
+ }
167
+ if let Some(v) = req.max_concurrent_at_polls {
168
+ config.max_concurrent_at_polls(v as usize);
169
+ }
170
+ config.no_remote_activities(req.no_remote_activities);
171
+ if let Some(v) = req.sticky_queue_schedule_to_start_timeout {
172
+ let v: std::time::Duration = v
173
+ .try_into()
174
+ .map_err(|_| "invalid sticky queue schedule to start timeout".to_string())?;
175
+ config.sticky_queue_schedule_to_start_timeout(v);
176
+ }
177
+ if let Some(v) = req.max_heartbeat_throttle_interval {
178
+ let v: std::time::Duration = v
179
+ .try_into()
180
+ .map_err(|_| "invalid max heartbeat throttle interval".to_string())?;
181
+ config.max_heartbeat_throttle_interval(v);
182
+ }
183
+ if let Some(v) = req.default_heartbeat_throttle_interval {
184
+ let v: std::time::Duration = v
185
+ .try_into()
186
+ .map_err(|_| "invalid default heartbeat throttle interval".to_string())?;
187
+ config.default_heartbeat_throttle_interval(v);
188
+ }
189
+ config
190
+ .build()
191
+ .map_err(|err| format!("invalid request: {}", err))
192
+ }
193
+ }
@@ -0,0 +1,32 @@
1
+ [package]
2
+ name = "temporal-client"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+
6
+ # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7
+ [features]
8
+ mocks = ["mockall"]
9
+
10
+ [dependencies]
11
+ async-trait = "0.1"
12
+ backoff = "0.3.0"
13
+ derive_builder = "0.10"
14
+ derive_more = "0.99"
15
+ futures = "0.3"
16
+ futures-retry = "0.6.0"
17
+ http = "0.2"
18
+ mockall = { version = "0.11.0", optional = true }
19
+ opentelemetry = "0.16"
20
+ prost-types = "0.9"
21
+ thiserror = "1.0"
22
+ tokio = "1.1"
23
+ tonic = "0.6"
24
+ tower = "0.4"
25
+ tracing = "0.1"
26
+ url = "2.2"
27
+ uuid = { version = "0.8.2", features = ["v4"] }
28
+
29
+ [dependencies.temporal-sdk-core-protos]
30
+ path = "../sdk-core-protos"
31
+ version = "0.1"
32
+
@@ -1,33 +1,50 @@
1
- use crate::{
2
- pollers::{retry::RetryGateway, Result},
3
- protosext::WorkflowTaskCompletion,
4
- task_token::TaskToken,
5
- telemetry::metrics::{svc_operation, MetricsContext},
6
- CoreInitError,
7
- };
1
+ #![warn(missing_docs)] // error if there are missing docs
2
+
3
+ //! This crate contains client implementations that can be used to contact the Temporal service.
4
+ //!
5
+ //! It implements auto-retry behavior and metrics collection.
6
+
7
+ #[macro_use]
8
+ extern crate tracing;
9
+
10
+ mod metrics;
11
+ #[cfg(any(feature = "mocks", test))]
12
+ pub mod mocks;
13
+ mod retry;
14
+
15
+ pub use crate::retry::RetryGateway;
16
+ #[cfg(any(feature = "mocks", test))]
17
+ pub use mocks::MockManualGateway;
18
+
19
+ use crate::metrics::{svc_operation, MetricsContext};
8
20
  use backoff::{ExponentialBackoff, SystemClock};
9
21
  use futures::{future::BoxFuture, task::Context, FutureExt};
22
+ use http::uri::InvalidUri;
23
+ use opentelemetry::metrics::Meter;
10
24
  use std::{
25
+ collections::HashMap,
11
26
  fmt::{Debug, Formatter},
12
- ops::Deref,
13
- sync::Arc,
27
+ str::FromStr,
14
28
  task::Poll,
15
29
  time::{Duration, Instant},
16
30
  };
17
31
  use temporal_sdk_core_protos::{
18
32
  coresdk::{common::Payload, workflow_commands::QueryResult, IntoPayloadsExt},
19
33
  temporal::api::{
34
+ command::v1::Command,
20
35
  common::v1::{Payloads, WorkflowExecution, WorkflowType},
21
36
  enums::v1::{TaskQueueKind, WorkflowTaskFailedCause},
22
37
  failure::v1::Failure,
23
38
  query::v1::{WorkflowQuery, WorkflowQueryResult},
24
- taskqueue::v1::TaskQueue,
39
+ taskqueue::v1::{StickyExecutionAttributes, TaskQueue},
25
40
  workflowservice::v1::{workflow_service_client::WorkflowServiceClient, *},
26
41
  },
42
+ TaskToken,
27
43
  };
28
44
  use tonic::{
29
45
  body::BoxBody,
30
46
  codegen::{InterceptedService, Service},
47
+ metadata::{MetadataKey, MetadataValue},
31
48
  service::Interceptor,
32
49
  transport::{Certificate, Channel, Endpoint, Identity},
33
50
  IntoRequest, Status,
@@ -36,37 +53,15 @@ use tower::ServiceBuilder;
36
53
  use url::Url;
37
54
  use uuid::Uuid;
38
55
 
39
- #[cfg(test)]
56
+ #[cfg(any(feature = "mocks", test))]
40
57
  use futures::Future;
41
- use std::{collections::HashMap, str::FromStr};
42
- use tonic::metadata::{MetadataKey, MetadataValue};
43
58
 
44
59
  static LONG_POLL_METHOD_NAMES: [&str; 2] = ["PollWorkflowTaskQueue", "PollActivityTaskQueue"];
45
60
  /// The server times out polls after 60 seconds. Set our timeout to be slightly beyond that.
46
61
  const LONG_POLL_TIMEOUT: Duration = Duration::from_secs(70);
47
62
  const OTHER_CALL_TIMEOUT: Duration = Duration::from_secs(30);
48
63
 
49
- pub struct GatewayRef {
50
- pub gw: Arc<dyn ServerGatewayApis + Send + Sync>,
51
- pub options: ServerGatewayOptions,
52
- }
53
-
54
- impl GatewayRef {
55
- pub fn new<SG: ServerGatewayApis + Send + Sync + 'static>(
56
- gw: Arc<SG>,
57
- options: ServerGatewayOptions,
58
- ) -> Self {
59
- Self { gw, options }
60
- }
61
- }
62
-
63
- impl Deref for GatewayRef {
64
- type Target = dyn ServerGatewayApis + Send + Sync;
65
-
66
- fn deref(&self) -> &Self::Target {
67
- self.gw.as_ref()
68
- }
69
- }
64
+ type Result<T, E = tonic::Status> = std::result::Result<T, E>;
70
65
 
71
66
  /// Options for the connection to the temporal server. Construct with [ServerGatewayOptionsBuilder]
72
67
  #[derive(Clone, Debug, derive_builder::Builder)]
@@ -199,14 +194,33 @@ impl Debug for ClientTlsConfig {
199
194
  }
200
195
  }
201
196
 
197
+ /// Errors thrown while attempting to establish a connection to the server
198
+ #[derive(thiserror::Error, Debug)]
199
+ pub enum GatewayInitError {
200
+ /// Invalid URI. Configuration error, fatal.
201
+ #[error("Invalid URI: {0:?}")]
202
+ InvalidUri(#[from] InvalidUri),
203
+ /// Server connection error. Crashing and restarting the worker is likely best.
204
+ #[error("Server connection error: {0:?}")]
205
+ TonicTransportError(#[from] tonic::transport::Error),
206
+ }
207
+
202
208
  impl ServerGatewayOptions {
203
209
  /// Attempt to establish a connection to the Temporal server
204
- pub async fn connect(&self) -> Result<RetryGateway<ServerGateway>, CoreInitError> {
210
+ pub async fn connect(
211
+ &self,
212
+ metrics_meter: Option<&Meter>,
213
+ ) -> Result<RetryGateway<ServerGateway>, GatewayInitError> {
205
214
  let channel = Channel::from_shared(self.target_url.to_string())?;
206
215
  let channel = self.add_tls_to_channel(channel).await?;
207
216
  let channel = channel.connect().await?;
208
217
  let service = ServiceBuilder::new()
209
- .layer_fn(|c| GrpcMetricSvc::new(c, MetricsContext::top_level(self.namespace.clone())))
218
+ .layer_fn(|channel| {
219
+ GrpcMetricSvc::new(
220
+ channel,
221
+ metrics_meter.map(|mm| MetricsContext::top_level(self.namespace.clone(), mm)),
222
+ )
223
+ })
210
224
  .service(channel);
211
225
  let interceptor = ServiceCallInterceptor { opts: self.clone() };
212
226
  let service = WorkflowServiceClient::with_interceptor(service, interceptor);
@@ -220,7 +234,10 @@ impl ServerGatewayOptions {
220
234
 
221
235
  /// If TLS is configured, set the appropriate options on the provided channel and return it.
222
236
  /// Passes it through if TLS options not set.
223
- async fn add_tls_to_channel(&self, channel: Endpoint) -> Result<Endpoint, CoreInitError> {
237
+ async fn add_tls_to_channel(
238
+ &self,
239
+ channel: Endpoint,
240
+ ) -> Result<Endpoint, tonic::transport::Error> {
224
241
  if let Some(tls_cfg) = &self.tls_cfg {
225
242
  let mut tls = tonic::transport::ClientTlsConfig::new();
226
243
 
@@ -239,14 +256,32 @@ impl ServerGatewayOptions {
239
256
  tls = tls.identity(client_identity);
240
257
  }
241
258
 
242
- return Ok(channel.tls_config(tls)?);
259
+ return channel.tls_config(tls);
243
260
  }
244
261
  Ok(channel)
245
262
  }
246
263
  }
247
264
 
265
+ /// A version of [RespondWorkflowTaskCompletedRequest] that will finish being filled out by the
266
+ /// server client
267
+ #[derive(Debug, Clone, PartialEq)]
268
+ pub struct WorkflowTaskCompletion {
269
+ /// The task token that would've been received from polling for a workflow activation
270
+ pub task_token: TaskToken,
271
+ /// A list of new commands to send to the server, such as starting a timer.
272
+ pub commands: Vec<Command>,
273
+ /// If set, indicate that next task should be queued on sticky queue with given attributes.
274
+ pub sticky_attributes: Option<StickyExecutionAttributes>,
275
+ /// Responses to queries in the `queries` field of the workflow task.
276
+ pub query_responses: Vec<QueryResult>,
277
+ /// Indicate that the task completion should return a new WFT if one is available
278
+ pub return_new_workflow_task: bool,
279
+ /// Force a new WFT to be created after this completion
280
+ pub force_create_new_workflow_task: bool,
281
+ }
282
+
248
283
  #[derive(Clone)]
249
- pub struct ServiceCallInterceptor {
284
+ struct ServiceCallInterceptor {
250
285
  opts: ServerGatewayOptions,
251
286
  }
252
287
 
@@ -286,7 +321,8 @@ impl Interceptor for ServiceCallInterceptor {
286
321
  #[derive(derive_more::Constructor, Debug, Clone)]
287
322
  pub(crate) struct GrpcMetricSvc {
288
323
  inner: Channel,
289
- metrics: MetricsContext,
324
+ // If set to none, metrics are a no-op
325
+ metrics: Option<MetricsContext>,
290
326
  }
291
327
 
292
328
  impl Service<http::Request<BoxBody>> for GrpcMetricSvc {
@@ -299,21 +335,17 @@ impl Service<http::Request<BoxBody>> for GrpcMetricSvc {
299
335
  }
300
336
 
301
337
  fn call(&mut self, req: http::Request<BoxBody>) -> Self::Future {
302
- let metrics = req
303
- .uri()
304
- .to_string()
305
- .rsplitn(2, '/')
306
- .next()
307
- .map(|method_name| {
308
- let mut metrics = self
309
- .metrics
310
- .with_new_attrs([svc_operation(method_name.to_string())]);
338
+ let metrics = self.metrics.as_ref().and_then(|metrics| {
339
+ req.uri().to_string().rsplit_once('/').map(|split_tup| {
340
+ let method_name = split_tup.1;
341
+ let mut metrics = metrics.with_new_attrs([svc_operation(method_name.to_string())]);
311
342
  if LONG_POLL_METHOD_NAMES.contains(&method_name) {
312
343
  metrics.set_is_long_poll();
313
344
  }
314
345
  metrics.svc_request();
315
346
  metrics
316
- });
347
+ })
348
+ });
317
349
  let callfut = self.inner.call(req);
318
350
  async move {
319
351
  let started = Instant::now();
@@ -340,7 +372,7 @@ pub struct ServerGateway {
340
372
  }
341
373
 
342
374
  /// This trait provides ways to call the temporal server
343
- #[cfg_attr(test, mockall::automock)]
375
+ #[cfg_attr(any(feature = "mocks", test), mockall::automock)]
344
376
  #[async_trait::async_trait]
345
377
  pub trait ServerGatewayApis {
346
378
  /// Starts workflow execution.
@@ -366,8 +398,8 @@ pub trait ServerGatewayApis {
366
398
  async fn poll_activity_task(&self, task_queue: String)
367
399
  -> Result<PollActivityTaskQueueResponse>;
368
400
 
369
- /// Notifies the server that workflow tasks for a given workflow should be sent to the normal non-sticky task queue.
370
- /// This normally happens when workflow has been evicted from the cache.
401
+ /// Notifies the server that workflow tasks for a given workflow should be sent to the normal
402
+ /// non-sticky task queue. This normally happens when workflow has been evicted from the cache.
371
403
  async fn reset_sticky_task_queue(
372
404
  &self,
373
405
  workflow_id: String,
@@ -381,17 +413,18 @@ pub trait ServerGatewayApis {
381
413
  ) -> Result<RespondWorkflowTaskCompletedResponse>;
382
414
 
383
415
  /// Complete activity task by sending response to the server. `task_token` contains activity
384
- /// identifier that would've been received from [crate::Core::poll_activity_task] API. `result`
385
- /// is a blob that contains activity response.
416
+ /// identifier that would've been received from polling for an activity task. `result` is a blob
417
+ /// that contains activity response.
386
418
  async fn complete_activity_task(
387
419
  &self,
388
420
  task_token: TaskToken,
389
421
  result: Option<Payloads>,
390
422
  ) -> Result<RespondActivityTaskCompletedResponse>;
391
423
 
392
- /// Report activity task heartbeat by sending details to the server. `task_token` contains activity
393
- /// identifier that would've been received from [crate::Core::poll_activity_task] API.
394
- /// `result` contains `cancel_requested` flag, which if set to true indicates that activity has been cancelled.
424
+ /// Report activity task heartbeat by sending details to the server. `task_token` contains
425
+ /// activity identifier that would've been received from polling for an activity task. `result`
426
+ /// contains `cancel_requested` flag, which if set to true indicates that activity has been
427
+ /// cancelled.
395
428
  async fn record_activity_heartbeat(
396
429
  &self,
397
430
  task_token: TaskToken,
@@ -399,8 +432,8 @@ pub trait ServerGatewayApis {
399
432
  ) -> Result<RecordActivityTaskHeartbeatResponse>;
400
433
 
401
434
  /// Cancel activity task by sending response to the server. `task_token` contains activity
402
- /// identifier that would've been received from [crate::Core::poll_activity_task] API. `details`
403
- /// is a blob that provides arbitrary user defined cancellation info.
435
+ /// identifier that would've been received from polling for an activity task. `details` is a
436
+ /// blob that provides arbitrary user defined cancellation info.
404
437
  async fn cancel_activity_task(
405
438
  &self,
406
439
  task_token: TaskToken,
@@ -408,8 +441,8 @@ pub trait ServerGatewayApis {
408
441
  ) -> Result<RespondActivityTaskCanceledResponse>;
409
442
 
410
443
  /// Fail activity task by sending response to the server. `task_token` contains activity
411
- /// identifier that would've been received from [crate::Core::poll_activity_task] API. `failure`
412
- /// provides failure details, such as message, cause and stack trace.
444
+ /// identifier that would've been received from polling for an activity task. `failure` provides
445
+ /// failure details, such as message, cause and stack trace.
413
446
  async fn fail_activity_task(
414
447
  &self,
415
448
  task_token: TaskToken,
@@ -417,7 +450,7 @@ pub trait ServerGatewayApis {
417
450
  ) -> Result<RespondActivityTaskFailedResponse>;
418
451
 
419
452
  /// Fail task by sending the failure to the server. `task_token` is the task token that would've
420
- /// been received from [crate::Core::poll_workflow_activation].
453
+ /// been received from polling for a workflow activation.
421
454
  async fn fail_workflow_task(
422
455
  &self,
423
456
  task_token: TaskToken,
@@ -480,6 +513,9 @@ pub trait ServerGatewayApis {
480
513
 
481
514
  /// Lists all available namespaces
482
515
  async fn list_namespaces(&self) -> Result<ListNamespacesResponse>;
516
+
517
+ /// Returns options that were used to initialize the gateway
518
+ fn get_options(&self) -> &ServerGatewayOptions;
483
519
  }
484
520
 
485
521
  #[async_trait::async_trait]
@@ -883,138 +919,8 @@ impl ServerGatewayApis for ServerGateway {
883
919
  .await?
884
920
  .into_inner())
885
921
  }
886
- }
887
922
 
888
- // Need a version of the mock that can return futures so we can return potentially pending
889
- // results. This is really annoying b/c of the async trait stuff. Need
890
- // https://github.com/asomers/mockall/issues/189 to be fixed for it to go away.
891
- #[cfg(test)]
892
- mockall::mock! {
893
- pub ManualGateway {}
894
- impl ServerGatewayApis for ManualGateway {
895
- fn start_workflow<'a, 'b>(
896
- &self,
897
- input: Vec<Payload>,
898
- task_queue: String,
899
- workflow_id: String,
900
- workflow_type: String,
901
- task_timeout: Option<Duration>,
902
- ) -> impl Future<Output = Result<StartWorkflowExecutionResponse>> + Send + 'b
903
- where 'a: 'b, Self: 'b;
904
-
905
- fn poll_workflow_task<'a, 'b>(&'a self, task_queue: String, is_sticky: bool)
906
- -> impl Future<Output = Result<PollWorkflowTaskQueueResponse>> + Send + 'b
907
- where 'a: 'b, Self: 'b;
908
-
909
- fn poll_activity_task<'a, 'b>(&self, task_queue: String)
910
- -> impl Future<Output = Result<PollActivityTaskQueueResponse>> + Send + 'b
911
- where 'a: 'b, Self: 'b;
912
-
913
- fn reset_sticky_task_queue<'a, 'b>(
914
- &self,
915
- workflow_id: String,
916
- run_id: String,
917
- ) -> impl Future<Output = Result<ResetStickyTaskQueueResponse>> + Send + 'b
918
- where 'a: 'b, Self: 'b;
919
-
920
- fn complete_workflow_task<'a, 'b>(
921
- &self,
922
- request: WorkflowTaskCompletion,
923
- ) -> impl Future<Output = Result<RespondWorkflowTaskCompletedResponse>> + Send + 'b
924
- where 'a: 'b, Self: 'b;
925
-
926
- fn complete_activity_task<'a, 'b>(
927
- &self,
928
- task_token: TaskToken,
929
- result: Option<Payloads>,
930
- ) -> impl Future<Output = Result<RespondActivityTaskCompletedResponse>> + Send + 'b
931
- where 'a: 'b, Self: 'b;
932
-
933
- fn cancel_activity_task<'a, 'b>(
934
- &self,
935
- task_token: TaskToken,
936
- details: Option<Payloads>,
937
- ) -> impl Future<Output = Result<RespondActivityTaskCanceledResponse>> + Send + 'b
938
- where 'a: 'b, Self: 'b;
939
-
940
- fn fail_activity_task<'a, 'b>(
941
- &self,
942
- task_token: TaskToken,
943
- failure: Option<Failure>,
944
- ) -> impl Future<Output = Result<RespondActivityTaskFailedResponse>> + Send + 'b
945
- where 'a: 'b, Self: 'b;
946
-
947
- fn fail_workflow_task<'a, 'b>(
948
- &self,
949
- task_token: TaskToken,
950
- cause: WorkflowTaskFailedCause,
951
- failure: Option<Failure>,
952
- ) -> impl Future<Output = Result<RespondWorkflowTaskFailedResponse>> + Send + 'b
953
- where 'a: 'b, Self: 'b;
954
-
955
- fn signal_workflow_execution<'a, 'b>(
956
- &self,
957
- workflow_id: String,
958
- run_id: String,
959
- signal_name: String,
960
- payloads: Option<Payloads>,
961
- ) -> impl Future<Output = Result<SignalWorkflowExecutionResponse>> + Send + 'b
962
- where 'a: 'b, Self: 'b;
963
-
964
- fn record_activity_heartbeat<'a, 'b>(
965
- &self,
966
- task_token: TaskToken,
967
- details: Option<Payloads>,
968
- ) -> impl Future<Output = Result<RecordActivityTaskHeartbeatResponse>> + Send + 'b
969
- where 'a: 'b, Self: 'b;
970
-
971
- fn query_workflow_execution<'a, 'b>(
972
- &self,
973
- workflow_id: String,
974
- run_id: String,
975
- query: WorkflowQuery,
976
- ) -> impl Future<Output = Result<QueryWorkflowResponse>> + Send + 'b
977
- where 'a: 'b, Self: 'b;
978
-
979
- fn describe_workflow_execution<'a, 'b>(
980
- &self,
981
- workflow_id: String,
982
- run_id: Option<String>,
983
- ) -> impl Future<Output = Result<DescribeWorkflowExecutionResponse>> + Send + 'b
984
- where 'a: 'b, Self: 'b;
985
-
986
- fn get_workflow_execution_history<'a, 'b>(
987
- &self,
988
- workflow_id: String,
989
- run_id: Option<String>,
990
- page_token: Vec<u8>
991
- ) -> impl Future<Output = Result<GetWorkflowExecutionHistoryResponse>> + Send + 'b
992
- where 'a: 'b, Self: 'b;
993
-
994
- fn respond_legacy_query<'a, 'b>(
995
- &self,
996
- task_token: TaskToken,
997
- query_result: QueryResult,
998
- ) -> impl Future<Output = Result<RespondQueryTaskCompletedResponse>> + Send + 'b
999
- where 'a: 'b, Self: 'b;
1000
-
1001
- fn cancel_workflow_execution<'a, 'b>(
1002
- &self,
1003
- workflow_id: String,
1004
- run_id: Option<String>,
1005
- ) -> impl Future<Output = Result<RequestCancelWorkflowExecutionResponse>> + Send + 'b
1006
- where 'a: 'b, Self: 'b;
1007
-
1008
- fn terminate_workflow_execution<'a, 'b>(
1009
- &self,
1010
- workflow_id: String,
1011
- run_id: Option<String>,
1012
- ) -> impl Future<Output = Result<TerminateWorkflowExecutionResponse>> + Send + 'b
1013
- where 'a: 'b, Self: 'b;
1014
-
1015
- fn list_namespaces<'a, 'b>(
1016
- &self,
1017
- ) -> impl Future<Output = Result<ListNamespacesResponse>> + Send + 'b
1018
- where 'a: 'b, Self: 'b;
923
+ fn get_options(&self) -> &ServerGatewayOptions {
924
+ &self.opts
1019
925
  }
1020
926
  }