@temporalio/core-bridge 0.19.2 → 0.20.2

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 (125) hide show
  1. package/Cargo.lock +90 -157
  2. package/Cargo.toml +1 -0
  3. package/index.d.ts +11 -27
  4. package/package.json +3 -3
  5. package/releases/aarch64-apple-darwin/index.node +0 -0
  6. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  7. package/releases/x86_64-apple-darwin/index.node +0 -0
  8. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  9. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  10. package/sdk-core/.buildkite/docker/Dockerfile +1 -1
  11. package/sdk-core/.buildkite/docker/docker-compose.yaml +1 -1
  12. package/sdk-core/.cargo/config.toml +1 -0
  13. package/sdk-core/CODEOWNERS +1 -1
  14. package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +119 -86
  15. package/sdk-core/bridge-ffi/src/lib.rs +311 -315
  16. package/sdk-core/bridge-ffi/src/wrappers.rs +108 -113
  17. package/sdk-core/client/Cargo.toml +13 -9
  18. package/sdk-core/client/LICENSE.txt +23 -0
  19. package/sdk-core/client/src/lib.rs +286 -174
  20. package/sdk-core/client/src/metrics.rs +86 -12
  21. package/sdk-core/client/src/raw.rs +566 -0
  22. package/sdk-core/client/src/retry.rs +137 -99
  23. package/sdk-core/core/Cargo.toml +15 -10
  24. package/sdk-core/core/LICENSE.txt +23 -0
  25. package/sdk-core/core/benches/workflow_replay.rs +79 -0
  26. package/sdk-core/core/src/abstractions.rs +38 -0
  27. package/sdk-core/core/src/core_tests/activity_tasks.rs +108 -182
  28. package/sdk-core/core/src/core_tests/child_workflows.rs +16 -11
  29. package/sdk-core/core/src/core_tests/determinism.rs +24 -12
  30. package/sdk-core/core/src/core_tests/local_activities.rs +53 -27
  31. package/sdk-core/core/src/core_tests/mod.rs +30 -43
  32. package/sdk-core/core/src/core_tests/queries.rs +82 -81
  33. package/sdk-core/core/src/core_tests/workers.rs +111 -296
  34. package/sdk-core/core/src/core_tests/workflow_cancels.rs +4 -4
  35. package/sdk-core/core/src/core_tests/workflow_tasks.rs +257 -242
  36. package/sdk-core/core/src/lib.rs +73 -318
  37. package/sdk-core/core/src/pollers/mod.rs +4 -6
  38. package/sdk-core/core/src/pollers/poll_buffer.rs +20 -14
  39. package/sdk-core/core/src/protosext/mod.rs +7 -10
  40. package/sdk-core/core/src/replay/mod.rs +11 -150
  41. package/sdk-core/core/src/telemetry/metrics.rs +35 -2
  42. package/sdk-core/core/src/telemetry/mod.rs +49 -16
  43. package/sdk-core/core/src/telemetry/prometheus_server.rs +14 -35
  44. package/sdk-core/core/src/test_help/mod.rs +104 -170
  45. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +57 -34
  46. package/sdk-core/core/src/worker/activities/local_activities.rs +95 -23
  47. package/sdk-core/core/src/worker/activities.rs +23 -16
  48. package/sdk-core/core/src/worker/client/mocks.rs +86 -0
  49. package/sdk-core/core/src/worker/client.rs +209 -0
  50. package/sdk-core/core/src/worker/mod.rs +207 -108
  51. package/sdk-core/core/src/workflow/driven_workflow.rs +21 -6
  52. package/sdk-core/core/src/workflow/history_update.rs +107 -24
  53. package/sdk-core/core/src/workflow/machines/activity_state_machine.rs +2 -3
  54. package/sdk-core/core/src/workflow/machines/child_workflow_state_machine.rs +2 -3
  55. package/sdk-core/core/src/workflow/machines/mod.rs +20 -17
  56. package/sdk-core/core/src/workflow/machines/signal_external_state_machine.rs +56 -19
  57. package/sdk-core/core/src/workflow/machines/transition_coverage.rs +5 -0
  58. package/sdk-core/core/src/workflow/machines/upsert_search_attributes_state_machine.rs +230 -22
  59. package/sdk-core/core/src/workflow/machines/workflow_machines.rs +81 -115
  60. package/sdk-core/core/src/workflow/machines/workflow_task_state_machine.rs +4 -4
  61. package/sdk-core/core/src/workflow/mod.rs +13 -1
  62. package/sdk-core/core/src/workflow/workflow_tasks/concurrency_manager.rs +70 -11
  63. package/sdk-core/core/src/workflow/workflow_tasks/mod.rs +65 -41
  64. package/sdk-core/core-api/Cargo.toml +9 -1
  65. package/sdk-core/core-api/LICENSE.txt +23 -0
  66. package/sdk-core/core-api/src/errors.rs +7 -38
  67. package/sdk-core/core-api/src/lib.rs +44 -52
  68. package/sdk-core/core-api/src/worker.rs +10 -2
  69. package/sdk-core/etc/deps.svg +127 -96
  70. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +11 -7
  71. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +10 -0
  72. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +6 -1
  73. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +6 -0
  74. package/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +6 -0
  75. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +2 -1
  76. package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +3 -0
  77. package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +12 -0
  78. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +25 -0
  79. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +4 -0
  80. package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +19 -35
  81. package/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +2 -6
  82. package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +53 -11
  83. package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +14 -7
  84. package/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +3 -5
  85. package/sdk-core/sdk/Cargo.toml +16 -2
  86. package/sdk-core/sdk/LICENSE.txt +23 -0
  87. package/sdk-core/sdk/src/interceptors.rs +11 -0
  88. package/sdk-core/sdk/src/lib.rs +139 -151
  89. package/sdk-core/sdk/src/workflow_context/options.rs +86 -1
  90. package/sdk-core/sdk/src/workflow_context.rs +36 -17
  91. package/sdk-core/sdk/src/workflow_future.rs +19 -25
  92. package/sdk-core/sdk-core-protos/Cargo.toml +1 -1
  93. package/sdk-core/sdk-core-protos/build.rs +1 -0
  94. package/sdk-core/sdk-core-protos/src/history_info.rs +17 -4
  95. package/sdk-core/sdk-core-protos/src/lib.rs +251 -47
  96. package/sdk-core/test-utils/Cargo.toml +3 -1
  97. package/sdk-core/test-utils/src/canned_histories.rs +27 -0
  98. package/sdk-core/test-utils/src/histfetch.rs +3 -3
  99. package/sdk-core/test-utils/src/lib.rs +223 -68
  100. package/sdk-core/tests/integ_tests/client_tests.rs +27 -4
  101. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +93 -14
  102. package/sdk-core/tests/integ_tests/polling_tests.rs +18 -12
  103. package/sdk-core/tests/integ_tests/queries_tests.rs +50 -53
  104. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +117 -103
  105. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +8 -1
  106. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +10 -5
  107. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +7 -1
  108. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +32 -9
  109. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +7 -1
  110. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +76 -15
  111. package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +19 -3
  112. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +39 -42
  113. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +84 -0
  114. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +30 -8
  115. package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +21 -6
  116. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +26 -16
  117. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +66 -0
  118. package/sdk-core/tests/integ_tests/workflow_tests.rs +78 -74
  119. package/sdk-core/tests/load_tests.rs +9 -6
  120. package/sdk-core/tests/main.rs +43 -10
  121. package/src/conversions.rs +7 -12
  122. package/src/lib.rs +322 -357
  123. package/sdk-core/client/src/mocks.rs +0 -167
  124. package/sdk-core/core/src/worker/dispatcher.rs +0 -171
  125. package/sdk-core/protos/local/temporal/sdk/core/bridge/service.proto +0 -61
@@ -8,24 +8,25 @@
8
8
  extern crate tracing;
9
9
 
10
10
  mod metrics;
11
- #[cfg(any(feature = "mocks", test))]
12
- pub mod mocks;
11
+ mod raw;
13
12
  mod retry;
14
13
 
15
- pub use crate::retry::RetryGateway;
16
- #[cfg(any(feature = "mocks", test))]
17
- pub use mocks::MockManualGateway;
14
+ pub use crate::retry::{CallType, RetryClient};
15
+ pub use raw::WorkflowService;
18
16
 
19
- use crate::metrics::{svc_operation, MetricsContext};
17
+ use crate::{
18
+ metrics::{GrpcMetricSvc, MetricsContext},
19
+ raw::{sealed::RawClientLike, AttachMetricLabels},
20
+ };
20
21
  use backoff::{ExponentialBackoff, SystemClock};
21
- use futures::{future::BoxFuture, task::Context, FutureExt};
22
22
  use http::uri::InvalidUri;
23
23
  use opentelemetry::metrics::Meter;
24
24
  use std::{
25
25
  collections::HashMap,
26
26
  fmt::{Debug, Formatter},
27
+ ops::{Deref, DerefMut},
27
28
  str::FromStr,
28
- task::Poll,
29
+ sync::Arc,
29
30
  time::{Duration, Instant},
30
31
  };
31
32
  use temporal_sdk_core_protos::{
@@ -42,19 +43,17 @@ use temporal_sdk_core_protos::{
42
43
  TaskToken,
43
44
  };
44
45
  use tonic::{
45
- body::BoxBody,
46
- codegen::{InterceptedService, Service},
46
+ codegen::InterceptedService,
47
47
  metadata::{MetadataKey, MetadataValue},
48
48
  service::Interceptor,
49
49
  transport::{Certificate, Channel, Endpoint, Identity},
50
- IntoRequest, Status,
50
+ Code, Status,
51
51
  };
52
52
  use tower::ServiceBuilder;
53
53
  use url::Url;
54
54
  use uuid::Uuid;
55
55
 
56
- #[cfg(any(feature = "mocks", test))]
57
- use futures::Future;
56
+ use temporal_sdk_core_protos::temporal::api::taskqueue::v1::TaskQueueMetadata;
58
57
 
59
58
  static LONG_POLL_METHOD_NAMES: [&str; 2] = ["PollWorkflowTaskQueue", "PollActivityTaskQueue"];
60
59
  /// The server times out polls after 60 seconds. Set our timeout to be slightly beyond that.
@@ -63,17 +62,14 @@ const OTHER_CALL_TIMEOUT: Duration = Duration::from_secs(30);
63
62
 
64
63
  type Result<T, E = tonic::Status> = std::result::Result<T, E>;
65
64
 
66
- /// Options for the connection to the temporal server. Construct with [ServerGatewayOptionsBuilder]
65
+ /// Options for the connection to the temporal server. Construct with [ClientOptionsBuilder]
67
66
  #[derive(Clone, Debug, derive_builder::Builder)]
68
67
  #[non_exhaustive]
69
- pub struct ServerGatewayOptions {
68
+ pub struct ClientOptions {
70
69
  /// The URL of the Temporal server to connect to
71
70
  #[builder(setter(into))]
72
71
  pub target_url: Url,
73
72
 
74
- /// What namespace will we operate under
75
- pub namespace: String,
76
-
77
73
  /// The name of the SDK being implemented on top of core. Is set as `client-name` header in
78
74
  /// all RPC calls
79
75
  pub client_name: String,
@@ -99,7 +95,7 @@ pub struct ServerGatewayOptions {
99
95
  #[builder(setter(strip_option), default)]
100
96
  pub tls_cfg: Option<TlsConfig>,
101
97
 
102
- /// Retry configuration for the server gateway. Default is [RetryConfig::default]
98
+ /// Retry configuration for the server client. Default is [RetryConfig::default]
103
99
  #[builder(default)]
104
100
  pub retry_config: RetryConfig,
105
101
  }
@@ -196,40 +192,152 @@ impl Debug for ClientTlsConfig {
196
192
 
197
193
  /// Errors thrown while attempting to establish a connection to the server
198
194
  #[derive(thiserror::Error, Debug)]
199
- pub enum GatewayInitError {
195
+ pub enum ClientInitError {
200
196
  /// Invalid URI. Configuration error, fatal.
201
197
  #[error("Invalid URI: {0:?}")]
202
198
  InvalidUri(#[from] InvalidUri),
203
199
  /// Server connection error. Crashing and restarting the worker is likely best.
204
200
  #[error("Server connection error: {0:?}")]
205
201
  TonicTransportError(#[from] tonic::transport::Error),
202
+ /// We couldn't successfully make the `get_system_info` call at connection time to establish
203
+ /// server capabilities / verify server is responding.
204
+ #[error("`get_system_info` call error after connection: {0:?}")]
205
+ SystemInfoCallError(tonic::Status),
206
+ }
207
+
208
+ #[doc(hidden)]
209
+ /// Allows passing different kinds of clients into things that want to be flexible. Motivating
210
+ /// use-case was worker initialization.
211
+ ///
212
+ /// Needs to exist in this crate to avoid blanket impl conflicts.
213
+ pub enum AnyClient {
214
+ /// A high level client, like the type workers work with
215
+ HighLevel(Arc<dyn WorkflowClientTrait + Send + Sync>),
216
+ /// A low level gRPC client, wrapped with the typical interceptors
217
+ LowLevel(Box<ConfiguredClient<WorkflowServiceClientWithMetrics>>),
218
+ }
219
+
220
+ impl<SGA> From<SGA> for AnyClient
221
+ where
222
+ SGA: WorkflowClientTrait + Send + Sync + 'static,
223
+ {
224
+ fn from(s: SGA) -> Self {
225
+ Self::HighLevel(Arc::new(s))
226
+ }
227
+ }
228
+ impl<SGA> From<Arc<SGA>> for AnyClient
229
+ where
230
+ SGA: WorkflowClientTrait + Send + Sync + 'static,
231
+ {
232
+ fn from(s: Arc<SGA>) -> Self {
233
+ Self::HighLevel(s)
234
+ }
235
+ }
236
+ impl From<RetryClient<ConfiguredClient<WorkflowServiceClientWithMetrics>>> for AnyClient {
237
+ fn from(c: RetryClient<ConfiguredClient<WorkflowServiceClientWithMetrics>>) -> Self {
238
+ Self::LowLevel(Box::new(c.into_inner()))
239
+ }
240
+ }
241
+ impl From<ConfiguredClient<WorkflowServiceClientWithMetrics>> for AnyClient {
242
+ fn from(c: ConfiguredClient<WorkflowServiceClientWithMetrics>) -> Self {
243
+ Self::LowLevel(Box::new(c))
244
+ }
245
+ }
246
+
247
+ /// A client with [ClientOptions] attached, which can be passed to initialize workers,
248
+ /// or can be used directly.
249
+ #[derive(Clone, Debug)]
250
+ pub struct ConfiguredClient<C> {
251
+ client: C,
252
+ options: ClientOptions,
253
+ /// Capabilities as read from the `get_system_info` RPC call made on client connection
254
+ capabilities: Option<get_system_info_response::Capabilities>,
255
+ }
256
+
257
+ impl<C> ConfiguredClient<C> {
258
+ /// Returns the options the client is configured with
259
+ pub fn options(&self) -> &ClientOptions {
260
+ &self.options
261
+ }
262
+
263
+ /// Returns the server capabilities we (may have) learned about when establishing an initial
264
+ /// connection
265
+ pub fn capabilities(&self) -> Option<&get_system_info_response::Capabilities> {
266
+ self.capabilities.as_ref()
267
+ }
268
+
269
+ /// De-constitute this type
270
+ pub fn into_parts(self) -> (C, ClientOptions) {
271
+ (self.client, self.options)
272
+ }
206
273
  }
207
274
 
208
- impl ServerGatewayOptions {
209
- /// Attempt to establish a connection to the Temporal server
275
+ // The configured client is effectively a "smart" (dumb) pointer
276
+ impl<C> Deref for ConfiguredClient<C> {
277
+ type Target = C;
278
+
279
+ fn deref(&self) -> &Self::Target {
280
+ &self.client
281
+ }
282
+ }
283
+ impl<C> DerefMut for ConfiguredClient<C> {
284
+ fn deref_mut(&mut self) -> &mut Self::Target {
285
+ &mut self.client
286
+ }
287
+ }
288
+
289
+ impl ClientOptions {
290
+ /// Attempt to establish a connection to the Temporal server in a specific namespace. The
291
+ /// returned client is bound to that namespace.
210
292
  pub async fn connect(
293
+ &self,
294
+ namespace: impl Into<String>,
295
+ metrics_meter: Option<&Meter>,
296
+ ) -> Result<RetryClient<Client>, ClientInitError> {
297
+ let client = self.connect_no_namespace(metrics_meter).await?.into_inner();
298
+ let client = Client::new(client, namespace.into());
299
+ let retry_client = RetryClient::new(client, self.retry_config.clone());
300
+ Ok(retry_client)
301
+ }
302
+
303
+ /// Attempt to establish a connection to the Temporal server and return a gRPC client which is
304
+ /// intercepted with retry, default headers functionality, and metrics if provided.
305
+ ///
306
+ /// See [RetryClient] for more
307
+ pub async fn connect_no_namespace(
211
308
  &self,
212
309
  metrics_meter: Option<&Meter>,
213
- ) -> Result<RetryGateway<ServerGateway>, GatewayInitError> {
310
+ ) -> Result<RetryClient<ConfiguredClient<WorkflowServiceClientWithMetrics>>, ClientInitError>
311
+ {
214
312
  let channel = Channel::from_shared(self.target_url.to_string())?;
215
313
  let channel = self.add_tls_to_channel(channel).await?;
216
314
  let channel = channel.connect().await?;
217
315
  let service = ServiceBuilder::new()
218
- .layer_fn(|channel| {
219
- GrpcMetricSvc::new(
220
- channel,
221
- metrics_meter.map(|mm| MetricsContext::top_level(self.namespace.clone(), mm)),
222
- )
316
+ .layer_fn(|channel| GrpcMetricSvc {
317
+ inner: channel,
318
+ metrics: metrics_meter.map(|mm| MetricsContext::new(vec![], mm)),
223
319
  })
224
320
  .service(channel);
225
321
  let interceptor = ServiceCallInterceptor { opts: self.clone() };
226
- let service = WorkflowServiceClient::with_interceptor(service, interceptor);
227
- let gateway = ServerGateway {
228
- service,
229
- opts: self.clone(),
322
+
323
+ let mut client = ConfiguredClient {
324
+ client: WorkflowServiceClient::with_interceptor(service, interceptor),
325
+ options: self.clone(),
326
+ capabilities: None,
230
327
  };
231
- let retry_gateway = RetryGateway::new(gateway, self.retry_config.clone());
232
- Ok(retry_gateway)
328
+ match client
329
+ .get_system_info(GetSystemInfoRequest::default())
330
+ .await
331
+ {
332
+ Ok(sysinfo) => {
333
+ client.capabilities = sysinfo.into_inner().capabilities;
334
+ }
335
+ Err(status) => match status.code() {
336
+ Code::Unimplemented => {}
337
+ _ => return Err(ClientInitError::SystemInfoCallError(status)),
338
+ },
339
+ };
340
+ Ok(RetryClient::new(client, self.retry_config.clone()))
233
341
  }
234
342
 
235
343
  /// If TLS is configured, set the appropriate options on the provided channel and return it.
@@ -280,9 +388,10 @@ pub struct WorkflowTaskCompletion {
280
388
  pub force_create_new_workflow_task: bool,
281
389
  }
282
390
 
391
+ /// Interceptor which attaches common metadata (like "client-name") to every outgoing call
283
392
  #[derive(Clone)]
284
- struct ServiceCallInterceptor {
285
- opts: ServerGatewayOptions,
393
+ pub struct ServiceCallInterceptor {
394
+ opts: ClientOptions,
286
395
  }
287
396
 
288
397
  impl Interceptor for ServiceCallInterceptor {
@@ -317,64 +426,67 @@ impl Interceptor for ServiceCallInterceptor {
317
426
  }
318
427
  }
319
428
 
320
- /// Implements metrics functionality for gRPC (really, any http) calls
321
- #[derive(derive_more::Constructor, Debug, Clone)]
322
- pub(crate) struct GrpcMetricSvc {
323
- inner: Channel,
324
- // If set to none, metrics are a no-op
325
- metrics: Option<MetricsContext>,
429
+ /// A [WorkflowServiceClient] with the default interceptors attached.
430
+ pub type WorkflowServiceClientWithMetrics = WorkflowServiceClient<InterceptedMetricsSvc>;
431
+ type InterceptedMetricsSvc = InterceptedService<GrpcMetricSvc, ServiceCallInterceptor>;
432
+
433
+ /// Contains an instance of a namespace-bound client for interacting with the Temporal server
434
+ #[derive(Debug)]
435
+ pub struct Client {
436
+ /// Client for interacting with workflow service
437
+ inner: ConfiguredClient<WorkflowServiceClientWithMetrics>,
438
+ /// The namespace this client interacts with
439
+ namespace: String,
326
440
  }
327
441
 
328
- impl Service<http::Request<BoxBody>> for GrpcMetricSvc {
329
- type Response = http::Response<tonic::transport::Body>;
330
- type Error = tonic::transport::Error;
331
- type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
332
-
333
- fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
334
- self.inner.poll_ready(cx).map_err(Into::into)
335
- }
336
-
337
- fn call(&mut self, req: http::Request<BoxBody>) -> Self::Future {
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())]);
342
- if LONG_POLL_METHOD_NAMES.contains(&method_name) {
343
- metrics.set_is_long_poll();
344
- }
345
- metrics.svc_request();
346
- metrics
347
- })
348
- });
349
- let callfut = self.inner.call(req);
350
- async move {
351
- let started = Instant::now();
352
- let res = callfut.await;
353
- if let Some(metrics) = metrics {
354
- metrics.record_svc_req_latency(started.elapsed());
355
- if res.is_err() {
356
- metrics.svc_request_failed();
357
- }
358
- }
359
- res
442
+ impl Client {
443
+ /// Create a new client from an existing configured lower level client and a namespace
444
+ pub fn new(
445
+ client: ConfiguredClient<WorkflowServiceClientWithMetrics>,
446
+ namespace: String,
447
+ ) -> Self {
448
+ Client {
449
+ inner: client,
450
+ namespace,
360
451
  }
361
- .boxed()
362
452
  }
363
- }
364
453
 
365
- /// Contains an instance of a client for interacting with the temporal server
366
- #[derive(Debug)]
367
- pub struct ServerGateway {
368
- /// Client for interacting with workflow service
369
- service: WorkflowServiceClient<InterceptedService<GrpcMetricSvc, ServiceCallInterceptor>>,
370
- /// Options gateway was initialized with
371
- pub opts: ServerGatewayOptions,
454
+ /// Return an auto-retrying version of the underling grpc client (instrumented with metrics
455
+ /// collection, if enabled).
456
+ ///
457
+ /// Note that it is reasonably cheap to clone the returned type if you need to own it. Such
458
+ /// clones will keep re-using the same channel.
459
+ pub fn raw_retry_client(&self) -> RetryClient<WorkflowServiceClientWithMetrics> {
460
+ RetryClient::new(
461
+ self.raw_client().clone(),
462
+ self.inner.options.retry_config.clone(),
463
+ )
464
+ }
465
+
466
+ /// Access the underling grpc client. This raw client is not bound to a specific namespace.
467
+ ///
468
+ /// Note that it is reasonably cheap to clone the returned type if you need to own it. Such
469
+ /// clones will keep re-using the same channel.
470
+ pub fn raw_client(&self) -> &WorkflowServiceClientWithMetrics {
471
+ self.inner.deref()
472
+ }
473
+
474
+ /// Return the options this client was initialized with
475
+ pub fn options(&self) -> &ClientOptions {
476
+ &self.inner.options
477
+ }
478
+
479
+ /// Used to access the client as a [WorkflowService] implementor rather than the raw struct
480
+ fn wf_svc(&self) -> impl RawClientLike<SvcType = InterceptedMetricsSvc> {
481
+ self.raw_client().clone()
482
+ }
372
483
  }
373
484
 
374
- /// This trait provides ways to call the temporal server
375
- #[cfg_attr(any(feature = "mocks", test), mockall::automock)]
485
+ /// This trait provides higher-level friendlier interaction with the server.
486
+ /// See the [WorkflowService] trait for a lower-level client.
487
+ #[cfg_attr(test, mockall::automock)]
376
488
  #[async_trait::async_trait]
377
- pub trait ServerGatewayApis {
489
+ pub trait WorkflowClientTrait {
378
490
  /// Starts workflow execution.
379
491
  async fn start_workflow(
380
492
  &self,
@@ -382,7 +494,7 @@ pub trait ServerGatewayApis {
382
494
  task_queue: String,
383
495
  workflow_id: String,
384
496
  workflow_type: String,
385
- task_timeout: Option<Duration>,
497
+ options: WorkflowOptions,
386
498
  ) -> Result<StartWorkflowExecutionResponse>;
387
499
 
388
500
  /// Fetch new workflow tasks from the provided queue. Should block indefinitely if there is no
@@ -395,8 +507,11 @@ pub trait ServerGatewayApis {
395
507
 
396
508
  /// Fetch new activity tasks from the provided queue. Should block indefinitely if there is no
397
509
  /// work.
398
- async fn poll_activity_task(&self, task_queue: String)
399
- -> Result<PollActivityTaskQueueResponse>;
510
+ async fn poll_activity_task(
511
+ &self,
512
+ task_queue: String,
513
+ max_tasks_per_sec: Option<f64>,
514
+ ) -> Result<PollActivityTaskQueueResponse>;
400
515
 
401
516
  /// Notifies the server that workflow tasks for a given workflow should be sent to the normal
402
517
  /// non-sticky task queue. This normally happens when workflow has been evicted from the cache.
@@ -514,27 +629,39 @@ pub trait ServerGatewayApis {
514
629
  /// Lists all available namespaces
515
630
  async fn list_namespaces(&self) -> Result<ListNamespacesResponse>;
516
631
 
517
- /// Returns options that were used to initialize the gateway
518
- fn get_options(&self) -> &ServerGatewayOptions;
632
+ /// Returns options that were used to initialize the client
633
+ fn get_options(&self) -> &ClientOptions;
634
+
635
+ /// Returns the namespace this client is bound to
636
+ fn namespace(&self) -> &str;
637
+ }
638
+
639
+ /// Optional fields supplied at the start of workflow execution
640
+ #[derive(Debug, Clone, Default)]
641
+ pub struct WorkflowOptions {
642
+ /// Optionally indicates the default task timeout for workflow tasks
643
+ pub task_timeout: Option<Duration>,
644
+
645
+ /// Optionally associate extra search attributes with a workflow
646
+ pub search_attributes: Option<HashMap<String, Payload>>,
519
647
  }
520
648
 
521
649
  #[async_trait::async_trait]
522
- impl ServerGatewayApis for ServerGateway {
650
+ impl WorkflowClientTrait for Client {
523
651
  async fn start_workflow(
524
652
  &self,
525
653
  input: Vec<Payload>,
526
654
  task_queue: String,
527
655
  workflow_id: String,
528
656
  workflow_type: String,
529
- task_timeout: Option<Duration>,
657
+ options: WorkflowOptions,
530
658
  ) -> Result<StartWorkflowExecutionResponse> {
531
659
  let request_id = Uuid::new_v4().to_string();
532
660
 
533
661
  Ok(self
534
- .service
535
- .clone()
662
+ .wf_svc()
536
663
  .start_workflow_execution(StartWorkflowExecutionRequest {
537
- namespace: self.opts.namespace.clone(),
664
+ namespace: self.namespace.clone(),
538
665
  input: input.into_payloads(),
539
666
  workflow_id,
540
667
  workflow_type: Some(WorkflowType {
@@ -545,7 +672,8 @@ impl ServerGatewayApis for ServerGateway {
545
672
  kind: 0,
546
673
  }),
547
674
  request_id,
548
- workflow_task_timeout: task_timeout.map(Into::into),
675
+ workflow_task_timeout: options.task_timeout.map(Into::into),
676
+ search_attributes: options.search_attributes.map(Into::into),
549
677
  ..Default::default()
550
678
  })
551
679
  .await?
@@ -557,8 +685,8 @@ impl ServerGatewayApis for ServerGateway {
557
685
  task_queue: String,
558
686
  is_sticky: bool,
559
687
  ) -> Result<PollWorkflowTaskQueueResponse> {
560
- let mut request = PollWorkflowTaskQueueRequest {
561
- namespace: self.opts.namespace.clone(),
688
+ let request = PollWorkflowTaskQueueRequest {
689
+ namespace: self.namespace.clone(),
562
690
  task_queue: Some(TaskQueue {
563
691
  name: task_queue,
564
692
  kind: if is_sticky {
@@ -567,15 +695,12 @@ impl ServerGatewayApis for ServerGateway {
567
695
  TaskQueueKind::Normal
568
696
  } as i32,
569
697
  }),
570
- identity: self.opts.identity.clone(),
571
- binary_checksum: self.opts.worker_binary_id.clone(),
572
- }
573
- .into_request();
574
- request.set_timeout(LONG_POLL_TIMEOUT);
698
+ identity: self.inner.options.identity.clone(),
699
+ binary_checksum: self.inner.options.worker_binary_id.clone(),
700
+ };
575
701
 
576
702
  Ok(self
577
- .service
578
- .clone()
703
+ .wf_svc()
579
704
  .poll_workflow_task_queue(request)
580
705
  .await?
581
706
  .into_inner())
@@ -584,22 +709,22 @@ impl ServerGatewayApis for ServerGateway {
584
709
  async fn poll_activity_task(
585
710
  &self,
586
711
  task_queue: String,
712
+ max_tasks_per_sec: Option<f64>,
587
713
  ) -> Result<PollActivityTaskQueueResponse> {
588
- let mut request = PollActivityTaskQueueRequest {
589
- namespace: self.opts.namespace.clone(),
714
+ let request = PollActivityTaskQueueRequest {
715
+ namespace: self.namespace.clone(),
590
716
  task_queue: Some(TaskQueue {
591
717
  name: task_queue,
592
718
  kind: TaskQueueKind::Normal as i32,
593
719
  }),
594
- identity: self.opts.identity.clone(),
595
- task_queue_metadata: None,
596
- }
597
- .into_request();
598
- request.set_timeout(LONG_POLL_TIMEOUT);
720
+ identity: self.inner.options.identity.clone(),
721
+ task_queue_metadata: max_tasks_per_sec.map(|tps| TaskQueueMetadata {
722
+ max_tasks_per_second: Some(tps),
723
+ }),
724
+ };
599
725
 
600
726
  Ok(self
601
- .service
602
- .clone()
727
+ .wf_svc()
603
728
  .poll_activity_task_queue(request)
604
729
  .await?
605
730
  .into_inner())
@@ -611,15 +736,14 @@ impl ServerGatewayApis for ServerGateway {
611
736
  run_id: String,
612
737
  ) -> Result<ResetStickyTaskQueueResponse> {
613
738
  let request = ResetStickyTaskQueueRequest {
614
- namespace: self.opts.namespace.clone(),
739
+ namespace: self.namespace.clone(),
615
740
  execution: Some(WorkflowExecution {
616
741
  workflow_id,
617
742
  run_id,
618
743
  }),
619
744
  };
620
745
  Ok(self
621
- .service
622
- .clone()
746
+ .wf_svc()
623
747
  .reset_sticky_task_queue(request)
624
748
  .await?
625
749
  .into_inner())
@@ -632,11 +756,11 @@ impl ServerGatewayApis for ServerGateway {
632
756
  let request = RespondWorkflowTaskCompletedRequest {
633
757
  task_token: request.task_token.into(),
634
758
  commands: request.commands,
635
- identity: self.opts.identity.clone(),
759
+ identity: self.inner.options.identity.clone(),
636
760
  sticky_attributes: request.sticky_attributes,
637
761
  return_new_workflow_task: request.return_new_workflow_task,
638
762
  force_create_new_workflow_task: request.force_create_new_workflow_task,
639
- binary_checksum: self.opts.worker_binary_id.clone(),
763
+ binary_checksum: self.inner.options.worker_binary_id.clone(),
640
764
  query_results: request
641
765
  .query_responses
642
766
  .into_iter()
@@ -652,11 +776,10 @@ impl ServerGatewayApis for ServerGateway {
652
776
  )
653
777
  })
654
778
  .collect(),
655
- namespace: self.opts.namespace.clone(),
779
+ namespace: self.namespace.clone(),
656
780
  };
657
781
  Ok(self
658
- .service
659
- .clone()
782
+ .wf_svc()
660
783
  .respond_workflow_task_completed(request)
661
784
  .await?
662
785
  .into_inner())
@@ -668,13 +791,12 @@ impl ServerGatewayApis for ServerGateway {
668
791
  result: Option<Payloads>,
669
792
  ) -> Result<RespondActivityTaskCompletedResponse> {
670
793
  Ok(self
671
- .service
672
- .clone()
794
+ .wf_svc()
673
795
  .respond_activity_task_completed(RespondActivityTaskCompletedRequest {
674
796
  task_token: task_token.0,
675
797
  result,
676
- identity: self.opts.identity.clone(),
677
- namespace: self.opts.namespace.clone(),
798
+ identity: self.inner.options.identity.clone(),
799
+ namespace: self.namespace.clone(),
678
800
  })
679
801
  .await?
680
802
  .into_inner())
@@ -686,13 +808,12 @@ impl ServerGatewayApis for ServerGateway {
686
808
  details: Option<Payloads>,
687
809
  ) -> Result<RecordActivityTaskHeartbeatResponse> {
688
810
  Ok(self
689
- .service
690
- .clone()
811
+ .wf_svc()
691
812
  .record_activity_task_heartbeat(RecordActivityTaskHeartbeatRequest {
692
813
  task_token: task_token.0,
693
814
  details,
694
- identity: self.opts.identity.clone(),
695
- namespace: self.opts.namespace.clone(),
815
+ identity: self.inner.options.identity.clone(),
816
+ namespace: self.namespace.clone(),
696
817
  })
697
818
  .await?
698
819
  .into_inner())
@@ -704,13 +825,12 @@ impl ServerGatewayApis for ServerGateway {
704
825
  details: Option<Payloads>,
705
826
  ) -> Result<RespondActivityTaskCanceledResponse> {
706
827
  Ok(self
707
- .service
708
- .clone()
828
+ .wf_svc()
709
829
  .respond_activity_task_canceled(RespondActivityTaskCanceledRequest {
710
830
  task_token: task_token.0,
711
831
  details,
712
- identity: self.opts.identity.clone(),
713
- namespace: self.opts.namespace.clone(),
832
+ identity: self.inner.options.identity.clone(),
833
+ namespace: self.namespace.clone(),
714
834
  })
715
835
  .await?
716
836
  .into_inner())
@@ -722,13 +842,12 @@ impl ServerGatewayApis for ServerGateway {
722
842
  failure: Option<Failure>,
723
843
  ) -> Result<RespondActivityTaskFailedResponse> {
724
844
  Ok(self
725
- .service
726
- .clone()
845
+ .wf_svc()
727
846
  .respond_activity_task_failed(RespondActivityTaskFailedRequest {
728
847
  task_token: task_token.0,
729
848
  failure,
730
- identity: self.opts.identity.clone(),
731
- namespace: self.opts.namespace.clone(),
849
+ identity: self.inner.options.identity.clone(),
850
+ namespace: self.namespace.clone(),
732
851
  })
733
852
  .await?
734
853
  .into_inner())
@@ -744,13 +863,12 @@ impl ServerGatewayApis for ServerGateway {
744
863
  task_token: task_token.0,
745
864
  cause: cause as i32,
746
865
  failure,
747
- identity: self.opts.identity.clone(),
748
- binary_checksum: self.opts.worker_binary_id.clone(),
749
- namespace: self.opts.namespace.clone(),
866
+ identity: self.inner.options.identity.clone(),
867
+ binary_checksum: self.inner.options.worker_binary_id.clone(),
868
+ namespace: self.namespace.clone(),
750
869
  };
751
870
  Ok(self
752
- .service
753
- .clone()
871
+ .wf_svc()
754
872
  .respond_workflow_task_failed(request)
755
873
  .await?
756
874
  .into_inner())
@@ -764,17 +882,16 @@ impl ServerGatewayApis for ServerGateway {
764
882
  payloads: Option<Payloads>,
765
883
  ) -> Result<SignalWorkflowExecutionResponse> {
766
884
  Ok(self
767
- .service
768
- .clone()
885
+ .wf_svc()
769
886
  .signal_workflow_execution(SignalWorkflowExecutionRequest {
770
- namespace: self.opts.namespace.clone(),
887
+ namespace: self.namespace.clone(),
771
888
  workflow_execution: Some(WorkflowExecution {
772
889
  workflow_id,
773
890
  run_id,
774
891
  }),
775
892
  signal_name,
776
893
  input: payloads,
777
- identity: self.opts.identity.clone(),
894
+ identity: self.inner.options.identity.clone(),
778
895
  ..Default::default()
779
896
  })
780
897
  .await?
@@ -788,10 +905,9 @@ impl ServerGatewayApis for ServerGateway {
788
905
  query: WorkflowQuery,
789
906
  ) -> Result<QueryWorkflowResponse> {
790
907
  Ok(self
791
- .service
792
- .clone()
908
+ .wf_svc()
793
909
  .query_workflow(QueryWorkflowRequest {
794
- namespace: self.opts.namespace.clone(),
910
+ namespace: self.namespace.clone(),
795
911
  execution: Some(WorkflowExecution {
796
912
  workflow_id,
797
913
  run_id,
@@ -809,10 +925,9 @@ impl ServerGatewayApis for ServerGateway {
809
925
  run_id: Option<String>,
810
926
  ) -> Result<DescribeWorkflowExecutionResponse> {
811
927
  Ok(self
812
- .service
813
- .clone()
928
+ .wf_svc()
814
929
  .describe_workflow_execution(DescribeWorkflowExecutionRequest {
815
- namespace: self.opts.namespace.clone(),
930
+ namespace: self.namespace.clone(),
816
931
  execution: Some(WorkflowExecution {
817
932
  workflow_id,
818
933
  run_id: run_id.unwrap_or_default(),
@@ -829,10 +944,9 @@ impl ServerGatewayApis for ServerGateway {
829
944
  page_token: Vec<u8>,
830
945
  ) -> Result<GetWorkflowExecutionHistoryResponse> {
831
946
  Ok(self
832
- .service
833
- .clone()
947
+ .wf_svc()
834
948
  .get_workflow_execution_history(GetWorkflowExecutionHistoryRequest {
835
- namespace: self.opts.namespace.clone(),
949
+ namespace: self.namespace.clone(),
836
950
  execution: Some(WorkflowExecution {
837
951
  workflow_id,
838
952
  run_id: run_id.unwrap_or_default(),
@@ -851,35 +965,32 @@ impl ServerGatewayApis for ServerGateway {
851
965
  ) -> Result<RespondQueryTaskCompletedResponse> {
852
966
  let (_, completed_type, query_result, error_message) = query_result.into_components();
853
967
  Ok(self
854
- .service
855
- .clone()
968
+ .wf_svc()
856
969
  .respond_query_task_completed(RespondQueryTaskCompletedRequest {
857
970
  task_token: task_token.into(),
858
971
  completed_type: completed_type as i32,
859
972
  query_result,
860
973
  error_message,
861
- namespace: self.opts.namespace.clone(),
974
+ namespace: self.namespace.clone(),
862
975
  })
863
976
  .await?
864
977
  .into_inner())
865
978
  }
866
979
 
867
- /// Cancel a currently executing workflow
868
980
  async fn cancel_workflow_execution(
869
981
  &self,
870
982
  workflow_id: String,
871
983
  run_id: Option<String>,
872
984
  ) -> Result<RequestCancelWorkflowExecutionResponse> {
873
985
  Ok(self
874
- .service
875
- .clone()
986
+ .wf_svc()
876
987
  .request_cancel_workflow_execution(RequestCancelWorkflowExecutionRequest {
877
- namespace: self.opts.namespace.clone(),
988
+ namespace: self.namespace.clone(),
878
989
  workflow_execution: Some(WorkflowExecution {
879
990
  workflow_id,
880
991
  run_id: run_id.unwrap_or_default(),
881
992
  }),
882
- identity: self.opts.identity.clone(),
993
+ identity: self.inner.options.identity.clone(),
883
994
  request_id: "".to_string(),
884
995
  first_execution_run_id: "".to_string(),
885
996
  })
@@ -887,24 +998,22 @@ impl ServerGatewayApis for ServerGateway {
887
998
  .into_inner())
888
999
  }
889
1000
 
890
- /// Terminate a currently executing workflow
891
1001
  async fn terminate_workflow_execution(
892
1002
  &self,
893
1003
  workflow_id: String,
894
1004
  run_id: Option<String>,
895
1005
  ) -> Result<TerminateWorkflowExecutionResponse> {
896
1006
  Ok(self
897
- .service
898
- .clone()
1007
+ .wf_svc()
899
1008
  .terminate_workflow_execution(TerminateWorkflowExecutionRequest {
900
- namespace: self.opts.namespace.clone(),
1009
+ namespace: self.namespace.clone(),
901
1010
  workflow_execution: Some(WorkflowExecution {
902
1011
  workflow_id,
903
1012
  run_id: run_id.unwrap_or_default(),
904
1013
  }),
905
1014
  reason: "".to_string(),
906
1015
  details: None,
907
- identity: self.opts.identity.clone(),
1016
+ identity: self.inner.options.identity.clone(),
908
1017
  first_execution_run_id: "".to_string(),
909
1018
  })
910
1019
  .await?
@@ -913,14 +1022,17 @@ impl ServerGatewayApis for ServerGateway {
913
1022
 
914
1023
  async fn list_namespaces(&self) -> Result<ListNamespacesResponse> {
915
1024
  Ok(self
916
- .service
917
- .clone()
1025
+ .wf_svc()
918
1026
  .list_namespaces(ListNamespacesRequest::default())
919
1027
  .await?
920
1028
  .into_inner())
921
1029
  }
922
1030
 
923
- fn get_options(&self) -> &ServerGatewayOptions {
924
- &self.opts
1031
+ fn get_options(&self) -> &ClientOptions {
1032
+ &self.inner.options
1033
+ }
1034
+
1035
+ fn namespace(&self) -> &str {
1036
+ &self.namespace
925
1037
  }
926
1038
  }