@temporalio/core-bridge 1.4.4 → 1.5.1
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.
- package/Cargo.lock +327 -419
- package/Cargo.toml +1 -1
- package/index.js +25 -2
- package/lib/errors.d.ts +22 -0
- package/lib/errors.js +65 -0
- package/lib/errors.js.map +1 -0
- package/lib/index.d.ts +440 -0
- package/lib/index.js +8 -0
- package/lib/index.js.map +1 -0
- package/package.json +11 -5
- package/releases/aarch64-apple-darwin/index.node +0 -0
- package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
- package/releases/x86_64-apple-darwin/index.node +0 -0
- package/releases/x86_64-pc-windows-msvc/index.node +0 -0
- package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
- package/sdk-core/.buildkite/docker/Dockerfile +1 -1
- package/sdk-core/.buildkite/docker/docker-compose.yaml +2 -2
- package/sdk-core/bridge-ffi/Cargo.toml +1 -1
- package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +0 -25
- package/sdk-core/bridge-ffi/src/lib.rs +29 -108
- package/sdk-core/bridge-ffi/src/wrappers.rs +35 -25
- package/sdk-core/client/Cargo.toml +1 -1
- package/sdk-core/client/src/lib.rs +12 -20
- package/sdk-core/client/src/raw.rs +9 -8
- package/sdk-core/client/src/retry.rs +100 -23
- package/sdk-core/core/Cargo.toml +5 -5
- package/sdk-core/core/benches/workflow_replay.rs +13 -10
- package/sdk-core/core/src/abstractions.rs +22 -22
- package/sdk-core/core/src/core_tests/activity_tasks.rs +1 -1
- package/sdk-core/core/src/core_tests/local_activities.rs +228 -6
- package/sdk-core/core/src/core_tests/queries.rs +247 -89
- package/sdk-core/core/src/core_tests/workers.rs +2 -2
- package/sdk-core/core/src/core_tests/workflow_cancels.rs +1 -1
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +46 -27
- package/sdk-core/core/src/lib.rs +139 -32
- package/sdk-core/core/src/replay/mod.rs +185 -41
- package/sdk-core/core/src/telemetry/log_export.rs +190 -0
- package/sdk-core/core/src/telemetry/metrics.rs +184 -139
- package/sdk-core/core/src/telemetry/mod.rs +296 -318
- package/sdk-core/core/src/telemetry/prometheus_server.rs +4 -3
- package/sdk-core/core/src/test_help/mod.rs +9 -7
- package/sdk-core/core/src/worker/activities/local_activities.rs +2 -1
- package/sdk-core/core/src/worker/activities.rs +40 -23
- package/sdk-core/core/src/worker/client/mocks.rs +1 -1
- package/sdk-core/core/src/worker/client.rs +30 -4
- package/sdk-core/core/src/worker/mod.rs +22 -18
- package/sdk-core/core/src/worker/workflow/driven_workflow.rs +10 -19
- package/sdk-core/core/src/worker/workflow/history_update.rs +99 -25
- package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +1 -5
- package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +1 -5
- package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +1 -5
- package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +1 -5
- package/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +1 -5
- package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +2 -6
- package/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +1 -5
- package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +18 -21
- package/sdk-core/core/src/worker/workflow/machines/mod.rs +12 -38
- package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +178 -0
- package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +1 -5
- package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +1 -5
- package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +1 -5
- package/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +8 -2
- package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +1 -5
- package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +232 -216
- package/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +1 -6
- package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +4 -4
- package/sdk-core/core/src/worker/workflow/managed_run.rs +13 -5
- package/sdk-core/core/src/worker/workflow/mod.rs +61 -9
- package/sdk-core/core/src/worker/workflow/wft_poller.rs +2 -2
- package/sdk-core/core/src/worker/workflow/workflow_stream.rs +56 -11
- package/sdk-core/core-api/Cargo.toml +4 -3
- package/sdk-core/core-api/src/lib.rs +1 -43
- package/sdk-core/core-api/src/telemetry.rs +147 -0
- package/sdk-core/core-api/src/worker.rs +13 -0
- package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +1 -1
- package/sdk-core/histories/evict_while_la_running_no_interference-23_history.bin +0 -0
- package/sdk-core/histories/evict_while_la_running_no_interference-85_history.bin +0 -0
- package/sdk-core/protos/api_upstream/.github/CODEOWNERS +1 -1
- package/sdk-core/protos/api_upstream/buf.yaml +0 -3
- package/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +3 -7
- package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +8 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +1 -2
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +2 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +3 -0
- package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +13 -0
- package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +19 -59
- package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +0 -19
- package/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +108 -29
- package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +2 -2
- package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +1 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +47 -8
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +15 -1
- package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +2 -0
- package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +8 -1
- package/sdk-core/sdk/src/interceptors.rs +36 -3
- package/sdk-core/sdk/src/lib.rs +7 -4
- package/sdk-core/sdk/src/workflow_context.rs +13 -2
- package/sdk-core/sdk-core-protos/src/history_builder.rs +47 -1
- package/sdk-core/sdk-core-protos/src/history_info.rs +22 -22
- package/sdk-core/sdk-core-protos/src/lib.rs +49 -27
- package/sdk-core/test-utils/Cargo.toml +1 -0
- package/sdk-core/test-utils/src/lib.rs +81 -29
- package/sdk-core/tests/integ_tests/metrics_tests.rs +37 -0
- package/sdk-core/tests/integ_tests/polling_tests.rs +0 -13
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +145 -4
- package/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +53 -0
- package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +106 -20
- package/sdk-core/tests/integ_tests/workflow_tests.rs +18 -8
- package/sdk-core/tests/main.rs +6 -4
- package/src/conversions.rs +52 -47
- package/src/errors.rs +28 -86
- package/src/helpers.rs +3 -4
- package/src/lib.rs +2 -2
- package/src/runtime.rs +132 -61
- package/src/testing.rs +7 -4
- package/src/worker.rs +67 -50
- package/ts/errors.ts +55 -0
- package/{index.d.ts → ts/index.ts} +121 -15
- package/sdk-core/core/src/log_export.rs +0 -62
- package/sdk-core/core/src/worker/workflow/machines/mutable_side_effect_state_machine.rs +0 -127
- package/sdk-core/core/src/worker/workflow/machines/side_effect_state_machine.rs +0 -71
- package/sdk-core/protos/api_upstream/temporal/api/cluster/v1/message.proto +0 -83
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/cluster.proto +0 -40
|
@@ -6,7 +6,7 @@ extern crate tracing;
|
|
|
6
6
|
|
|
7
7
|
pub mod canned_histories;
|
|
8
8
|
|
|
9
|
-
use crate::stream::TryStreamExt;
|
|
9
|
+
use crate::stream::{Stream, TryStreamExt};
|
|
10
10
|
use futures::{future, stream, stream::FuturesUnordered, StreamExt};
|
|
11
11
|
use parking_lot::Mutex;
|
|
12
12
|
use prost::Message;
|
|
@@ -18,14 +18,23 @@ use std::{
|
|
|
18
18
|
use temporal_client::{
|
|
19
19
|
Client, RetryClient, WorkflowClientTrait, WorkflowExecutionInfo, WorkflowOptions,
|
|
20
20
|
};
|
|
21
|
-
use temporal_sdk::{
|
|
21
|
+
use temporal_sdk::{
|
|
22
|
+
interceptors::{FailOnNondeterminismInterceptor, WorkerInterceptor},
|
|
23
|
+
IntoActivityFunc, Worker, WorkflowFunction,
|
|
24
|
+
};
|
|
22
25
|
use temporal_sdk_core::{
|
|
23
26
|
ephemeral_server::{EphemeralExe, EphemeralExeVersion},
|
|
24
|
-
init_replay_worker, init_worker,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
init_replay_worker, init_worker,
|
|
28
|
+
replay::HistoryForReplay,
|
|
29
|
+
ClientOptions, ClientOptionsBuilder, CoreRuntime, WorkerConfig, WorkerConfigBuilder,
|
|
30
|
+
};
|
|
31
|
+
use temporal_sdk_core_api::{
|
|
32
|
+
telemetry::{
|
|
33
|
+
Logger, MetricsExporter, OtelCollectorOptions, TelemetryOptions, TelemetryOptionsBuilder,
|
|
34
|
+
TraceExportConfig, TraceExporter,
|
|
35
|
+
},
|
|
36
|
+
Worker as CoreWorker,
|
|
27
37
|
};
|
|
28
|
-
use temporal_sdk_core_api::Worker as CoreWorker;
|
|
29
38
|
use temporal_sdk_core_protos::{
|
|
30
39
|
coresdk::{
|
|
31
40
|
workflow_commands::{
|
|
@@ -61,21 +70,43 @@ pub async fn init_core_and_create_wf(test_name: &str) -> CoreWfStarter {
|
|
|
61
70
|
starter
|
|
62
71
|
}
|
|
63
72
|
|
|
64
|
-
/// Create a worker replay instance preloaded with
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
73
|
+
/// Create a worker replay instance preloaded with provided histories. Returns the worker impl.
|
|
74
|
+
pub fn init_core_replay_preloaded<I>(test_name: &str, histories: I) -> Arc<dyn CoreWorker>
|
|
75
|
+
where
|
|
76
|
+
I: IntoIterator<Item = HistoryForReplay> + 'static,
|
|
77
|
+
<I as IntoIterator>::IntoIter: Send,
|
|
78
|
+
{
|
|
79
|
+
init_core_replay_stream(test_name, stream::iter(histories))
|
|
80
|
+
}
|
|
81
|
+
pub fn init_core_replay_stream<I>(test_name: &str, histories: I) -> Arc<dyn CoreWorker>
|
|
82
|
+
where
|
|
83
|
+
I: Stream<Item = HistoryForReplay> + Send + 'static,
|
|
84
|
+
{
|
|
71
85
|
let worker_cfg = WorkerConfigBuilder::default()
|
|
72
86
|
.namespace(NAMESPACE)
|
|
73
87
|
.task_queue(test_name)
|
|
74
88
|
.worker_build_id("test_bin_id")
|
|
75
89
|
.build()
|
|
76
90
|
.expect("Configuration options construct properly");
|
|
77
|
-
let worker =
|
|
78
|
-
|
|
91
|
+
let worker =
|
|
92
|
+
init_replay_worker(worker_cfg, histories).expect("Replay worker must init properly");
|
|
93
|
+
Arc::new(worker)
|
|
94
|
+
}
|
|
95
|
+
pub fn replay_sdk_worker<I>(histories: I) -> Worker
|
|
96
|
+
where
|
|
97
|
+
I: IntoIterator<Item = HistoryForReplay> + 'static,
|
|
98
|
+
<I as IntoIterator>::IntoIter: Send,
|
|
99
|
+
{
|
|
100
|
+
replay_sdk_worker_stream(stream::iter(histories))
|
|
101
|
+
}
|
|
102
|
+
pub fn replay_sdk_worker_stream<I>(histories: I) -> Worker
|
|
103
|
+
where
|
|
104
|
+
I: Stream<Item = HistoryForReplay> + Send + 'static,
|
|
105
|
+
{
|
|
106
|
+
let core = init_core_replay_stream("replay_worker_test", histories);
|
|
107
|
+
let mut worker = Worker::new_from_core(core, "replay_q".to_string());
|
|
108
|
+
worker.set_worker_interceptor(Box::new(FailOnNondeterminismInterceptor {}));
|
|
109
|
+
worker
|
|
79
110
|
}
|
|
80
111
|
|
|
81
112
|
/// Load history from a file containing the protobuf serialization of it
|
|
@@ -87,11 +118,21 @@ pub async fn history_from_proto_binary(path_from_root: &str) -> Result<History,
|
|
|
87
118
|
Ok(History::decode(&*bytes)?)
|
|
88
119
|
}
|
|
89
120
|
|
|
121
|
+
static INTEG_TESTS_RT: once_cell::sync::OnceCell<CoreRuntime> = once_cell::sync::OnceCell::new();
|
|
122
|
+
pub fn init_integ_telem() {
|
|
123
|
+
INTEG_TESTS_RT.get_or_init(|| {
|
|
124
|
+
let telemetry_options = get_integ_telem_options();
|
|
125
|
+
let rt =
|
|
126
|
+
CoreRuntime::new_assume_tokio(telemetry_options).expect("Core runtime inits cleanly");
|
|
127
|
+
let _ = tracing::subscriber::set_global_default(rt.trace_subscriber());
|
|
128
|
+
rt
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
90
132
|
/// Implements a builder pattern to help integ tests initialize core and create workflows
|
|
91
133
|
pub struct CoreWfStarter {
|
|
92
134
|
/// Used for both the task queue and workflow id
|
|
93
135
|
task_queue_name: String,
|
|
94
|
-
telemetry_options: TelemetryOptions,
|
|
95
136
|
pub worker_config: WorkerConfig,
|
|
96
137
|
wft_timeout: Option<Duration>,
|
|
97
138
|
initted_worker: OnceCell<InitializedWorker>,
|
|
@@ -110,9 +151,9 @@ impl CoreWfStarter {
|
|
|
110
151
|
}
|
|
111
152
|
|
|
112
153
|
pub fn new_tq_name(task_queue: &str) -> Self {
|
|
154
|
+
init_integ_telem();
|
|
113
155
|
Self {
|
|
114
156
|
task_queue_name: task_queue.to_owned(),
|
|
115
|
-
telemetry_options: get_integ_telem_options(),
|
|
116
157
|
worker_config: WorkerConfigBuilder::default()
|
|
117
158
|
.namespace(NAMESPACE)
|
|
118
159
|
.task_queue(task_queue)
|
|
@@ -181,19 +222,21 @@ impl CoreWfStarter {
|
|
|
181
222
|
&mut self,
|
|
182
223
|
wf_id: impl Into<String>,
|
|
183
224
|
run_id: impl Into<String>,
|
|
184
|
-
// TODO: Need not be passed in
|
|
185
225
|
worker: &mut Worker,
|
|
186
226
|
) -> Result<(), anyhow::Error> {
|
|
227
|
+
let wf_id = wf_id.into();
|
|
187
228
|
// Fetch history and replay it
|
|
188
229
|
let history = self
|
|
189
230
|
.get_client()
|
|
190
231
|
.await
|
|
191
|
-
.get_workflow_execution_history(wf_id.
|
|
232
|
+
.get_workflow_execution_history(wf_id.clone(), Some(run_id.into()), vec![])
|
|
192
233
|
.await?
|
|
193
234
|
.history
|
|
194
235
|
.expect("history field must be populated");
|
|
195
|
-
let
|
|
236
|
+
let with_id = HistoryForReplay::new(history, wf_id);
|
|
237
|
+
let replay_worker = init_core_replay_preloaded(worker.task_queue(), [with_id]);
|
|
196
238
|
worker.with_new_core_worker(replay_worker);
|
|
239
|
+
worker.set_worker_interceptor(Box::new(FailOnNondeterminismInterceptor {}));
|
|
197
240
|
worker.run().await.unwrap();
|
|
198
241
|
Ok(())
|
|
199
242
|
}
|
|
@@ -239,14 +282,18 @@ impl CoreWfStarter {
|
|
|
239
282
|
async fn get_or_init(&mut self) -> &InitializedWorker {
|
|
240
283
|
self.initted_worker
|
|
241
284
|
.get_or_init(|| async {
|
|
242
|
-
telemetry_init(&self.telemetry_options).expect("Telemetry inits cleanly");
|
|
243
285
|
let client = Arc::new(
|
|
244
286
|
get_integ_server_options()
|
|
245
287
|
.connect(self.worker_config.namespace.clone(), None, None)
|
|
246
288
|
.await
|
|
247
289
|
.expect("Must connect"),
|
|
248
290
|
);
|
|
249
|
-
let worker = init_worker(
|
|
291
|
+
let worker = init_worker(
|
|
292
|
+
INTEG_TESTS_RT.get().unwrap(),
|
|
293
|
+
self.worker_config.clone(),
|
|
294
|
+
client.clone(),
|
|
295
|
+
)
|
|
296
|
+
.expect("Worker inits cleanly");
|
|
250
297
|
InitializedWorker {
|
|
251
298
|
worker: Arc::new(worker),
|
|
252
299
|
client,
|
|
@@ -442,7 +489,6 @@ impl WorkerInterceptor for TestWorkerCompletionIceptor {
|
|
|
442
489
|
|
|
443
490
|
/// Returns the client options used to connect to the server used for integration tests.
|
|
444
491
|
pub fn get_integ_server_options() -> ClientOptions {
|
|
445
|
-
telemetry_init(&get_integ_telem_options()).expect("Telemetry inits cleanly");
|
|
446
492
|
let temporal_server_address = match env::var(INTEG_SERVER_TARGET_ENV_VAR) {
|
|
447
493
|
Ok(addr) => addr,
|
|
448
494
|
Err(_) => "http://localhost:7233".to_owned(),
|
|
@@ -459,6 +505,8 @@ pub fn get_integ_server_options() -> ClientOptions {
|
|
|
459
505
|
|
|
460
506
|
pub fn get_integ_telem_options() -> TelemetryOptions {
|
|
461
507
|
let mut ob = TelemetryOptionsBuilder::default();
|
|
508
|
+
let filter_string =
|
|
509
|
+
env::var("RUST_LOG").unwrap_or_else(|_| "temporal_sdk_core=INFO".to_string());
|
|
462
510
|
if let Some(url) = env::var(OTEL_URL_ENV_VAR)
|
|
463
511
|
.ok()
|
|
464
512
|
.map(|x| x.parse::<Url>().unwrap())
|
|
@@ -466,8 +514,12 @@ pub fn get_integ_telem_options() -> TelemetryOptions {
|
|
|
466
514
|
let opts = OtelCollectorOptions {
|
|
467
515
|
url,
|
|
468
516
|
headers: Default::default(),
|
|
517
|
+
metric_periodicity: None,
|
|
469
518
|
};
|
|
470
|
-
ob.tracing(
|
|
519
|
+
ob.tracing(TraceExportConfig {
|
|
520
|
+
filter: filter_string.clone(),
|
|
521
|
+
exporter: TraceExporter::Otel(opts.clone()),
|
|
522
|
+
});
|
|
471
523
|
ob.metrics(MetricsExporter::Otel(opts));
|
|
472
524
|
}
|
|
473
525
|
if let Some(addr) = env::var(PROM_ENABLE_ENV_VAR)
|
|
@@ -476,10 +528,11 @@ pub fn get_integ_telem_options() -> TelemetryOptions {
|
|
|
476
528
|
{
|
|
477
529
|
ob.metrics(MetricsExporter::Prometheus(addr));
|
|
478
530
|
}
|
|
479
|
-
ob.
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
531
|
+
ob.logging(Logger::Console {
|
|
532
|
+
filter: filter_string,
|
|
533
|
+
})
|
|
534
|
+
.build()
|
|
535
|
+
.unwrap()
|
|
483
536
|
}
|
|
484
537
|
|
|
485
538
|
pub fn default_cached_download() -> EphemeralExe {
|
|
@@ -504,7 +557,6 @@ pub fn schedule_activity_cmd(
|
|
|
504
557
|
seq,
|
|
505
558
|
activity_id: activity_id.to_string(),
|
|
506
559
|
activity_type: "test_activity".to_string(),
|
|
507
|
-
namespace: NAMESPACE.to_owned(),
|
|
508
560
|
task_queue: task_q.to_owned(),
|
|
509
561
|
schedule_to_start_timeout: Some(activity_timeout.try_into().expect("duration fits")),
|
|
510
562
|
start_to_close_timeout: Some(activity_timeout.try_into().expect("duration fits")),
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
use temporal_client::WorkflowService;
|
|
2
|
+
use temporal_sdk_core::CoreRuntime;
|
|
3
|
+
use temporal_sdk_core_api::telemetry::MetricsExporter;
|
|
4
|
+
use temporal_sdk_core_protos::temporal::api::workflowservice::v1::ListNamespacesRequest;
|
|
5
|
+
use temporal_sdk_core_test_utils::{get_integ_server_options, get_integ_telem_options};
|
|
6
|
+
|
|
7
|
+
#[tokio::test]
|
|
8
|
+
async fn prometheus_metrics_exported() {
|
|
9
|
+
let mut telemopts = get_integ_telem_options();
|
|
10
|
+
let addr = "127.0.0.1:10919";
|
|
11
|
+
telemopts.metrics = Some(MetricsExporter::Prometheus(addr.parse().unwrap()));
|
|
12
|
+
let rt = CoreRuntime::new_assume_tokio(telemopts).unwrap();
|
|
13
|
+
let opts = get_integ_server_options();
|
|
14
|
+
let mut raw_client = opts
|
|
15
|
+
.connect_no_namespace(rt.metric_meter(), None)
|
|
16
|
+
.await
|
|
17
|
+
.unwrap();
|
|
18
|
+
assert!(raw_client.get_client().capabilities().is_some());
|
|
19
|
+
|
|
20
|
+
let _ = raw_client
|
|
21
|
+
.list_namespaces(ListNamespacesRequest::default())
|
|
22
|
+
.await
|
|
23
|
+
.unwrap();
|
|
24
|
+
|
|
25
|
+
let body = reqwest::get(format!("http://{}/metrics", addr))
|
|
26
|
+
.await
|
|
27
|
+
.unwrap()
|
|
28
|
+
.text()
|
|
29
|
+
.await
|
|
30
|
+
.unwrap();
|
|
31
|
+
assert!(body.contains(
|
|
32
|
+
"request_latency_count{operation=\"ListNamespaces\",service_name=\"temporal-core-sdk\"} 1"
|
|
33
|
+
));
|
|
34
|
+
assert!(body.contains(
|
|
35
|
+
"request_latency_count{operation=\"GetSystemInfo\",service_name=\"temporal-core-sdk\"} 1"
|
|
36
|
+
));
|
|
37
|
+
}
|
|
@@ -3,7 +3,6 @@ use futures::future::join_all;
|
|
|
3
3
|
use std::time::Duration;
|
|
4
4
|
use temporal_client::WorkflowOptions;
|
|
5
5
|
use temporal_sdk::{WfContext, WorkflowResult};
|
|
6
|
-
use temporal_sdk_core_api::errors::PollWfError;
|
|
7
6
|
use temporal_sdk_core_protos::coresdk::{
|
|
8
7
|
activity_task::activity_task as act_task,
|
|
9
8
|
workflow_activation::{workflow_activation_job, FireTimer, WorkflowActivationJob},
|
|
@@ -132,15 +131,3 @@ async fn can_paginate_long_history() {
|
|
|
132
131
|
.unwrap();
|
|
133
132
|
worker.run_until_done().await.unwrap();
|
|
134
133
|
}
|
|
135
|
-
|
|
136
|
-
// TODO: Takes ages now, fix somehow
|
|
137
|
-
#[tokio::test]
|
|
138
|
-
async fn poll_of_nonexistent_namespace_is_fatal() {
|
|
139
|
-
let mut starter = CoreWfStarter::new("whatever_yo");
|
|
140
|
-
starter.worker_config.namespace = "I do not exist".to_string();
|
|
141
|
-
let worker = starter.get_worker().await;
|
|
142
|
-
assert_matches!(
|
|
143
|
-
worker.poll_workflow_activation().await,
|
|
144
|
-
Err(PollWfError::TonicError(_))
|
|
145
|
-
);
|
|
146
|
-
}
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
use anyhow::anyhow;
|
|
2
2
|
use futures::future::join_all;
|
|
3
|
+
use futures_util::stream::{FuturesUnordered, StreamExt};
|
|
3
4
|
use std::time::Duration;
|
|
4
|
-
use temporal_client::WorkflowOptions;
|
|
5
|
+
use temporal_client::{WorkflowClientTrait, WorkflowOptions};
|
|
5
6
|
use temporal_sdk::{
|
|
6
|
-
interceptors::WorkerInterceptor, ActContext, ActivityCancelledError,
|
|
7
|
-
LocalActivityOptions, WfContext, WorkflowResult,
|
|
7
|
+
interceptors::WorkerInterceptor, ActContext, ActivityCancelledError, ActivityOptions,
|
|
8
|
+
CancellableFuture, LocalActivityOptions, WfContext, WorkflowResult,
|
|
8
9
|
};
|
|
10
|
+
use temporal_sdk_core::replay::HistoryForReplay;
|
|
9
11
|
use temporal_sdk_core_protos::{
|
|
10
12
|
coresdk::{
|
|
11
13
|
workflow_commands::ActivityCancellationType,
|
|
12
14
|
workflow_completion::WorkflowActivationCompletion, AsJsonPayloadExt,
|
|
13
15
|
},
|
|
14
16
|
temporal::api::common::v1::RetryPolicy,
|
|
17
|
+
TestHistoryBuilder,
|
|
18
|
+
};
|
|
19
|
+
use temporal_sdk_core_test_utils::{
|
|
20
|
+
history_from_proto_binary, init_integ_telem, replay_sdk_worker, CoreWfStarter,
|
|
15
21
|
};
|
|
16
|
-
use temporal_sdk_core_test_utils::CoreWfStarter;
|
|
17
22
|
use tokio_util::sync::CancellationToken;
|
|
18
23
|
|
|
19
24
|
pub async fn echo(_ctx: ActContext, e: String) -> anyhow::Result<String> {
|
|
@@ -632,3 +637,139 @@ async fn repro_nondeterminism_with_timer_bug() {
|
|
|
632
637
|
.await
|
|
633
638
|
.unwrap();
|
|
634
639
|
}
|
|
640
|
+
|
|
641
|
+
async fn la_problem_workflow(ctx: WfContext) -> WorkflowResult<()> {
|
|
642
|
+
ctx.local_activity(LocalActivityOptions {
|
|
643
|
+
activity_type: "delay".to_string(),
|
|
644
|
+
input: "hi".as_json_payload().expect("serializes fine"),
|
|
645
|
+
retry_policy: RetryPolicy {
|
|
646
|
+
initial_interval: Some(prost_dur!(from_micros(15))),
|
|
647
|
+
backoff_coefficient: 1_000.,
|
|
648
|
+
maximum_interval: Some(prost_dur!(from_millis(1500))),
|
|
649
|
+
maximum_attempts: 4,
|
|
650
|
+
non_retryable_error_types: vec![],
|
|
651
|
+
},
|
|
652
|
+
timer_backoff_threshold: Some(Duration::from_secs(1)),
|
|
653
|
+
..Default::default()
|
|
654
|
+
})
|
|
655
|
+
.await;
|
|
656
|
+
ctx.activity(ActivityOptions {
|
|
657
|
+
activity_type: "delay".to_string(),
|
|
658
|
+
start_to_close_timeout: Some(Duration::from_secs(20)),
|
|
659
|
+
input: "hi!".as_json_payload().expect("serializes fine"),
|
|
660
|
+
..Default::default()
|
|
661
|
+
})
|
|
662
|
+
.await;
|
|
663
|
+
Ok(().into())
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// Expensive to run - worth enabling on a stress/regression pipeline.
|
|
667
|
+
#[ignore]
|
|
668
|
+
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
|
|
669
|
+
async fn evict_while_la_running_no_interference() {
|
|
670
|
+
let wf_name = "evict_while_la_running_no_interference";
|
|
671
|
+
let mut starter = CoreWfStarter::new(wf_name);
|
|
672
|
+
starter.max_local_at(20);
|
|
673
|
+
starter.max_cached_workflows(20);
|
|
674
|
+
let mut worker = starter.worker().await;
|
|
675
|
+
|
|
676
|
+
worker.register_wf(wf_name.to_owned(), la_problem_workflow);
|
|
677
|
+
worker.register_activity("delay", |_: ActContext, _: String| async {
|
|
678
|
+
tokio::time::sleep(Duration::from_secs(15)).await;
|
|
679
|
+
Ok(())
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
let client = starter.get_client().await;
|
|
683
|
+
let subfs = FuturesUnordered::new();
|
|
684
|
+
for i in 1..100 {
|
|
685
|
+
let wf_id = format!("{}-{}", wf_name, i);
|
|
686
|
+
let run_id = worker
|
|
687
|
+
.submit_wf(
|
|
688
|
+
&wf_id,
|
|
689
|
+
wf_name.to_owned(),
|
|
690
|
+
vec![],
|
|
691
|
+
WorkflowOptions::default(),
|
|
692
|
+
)
|
|
693
|
+
.await
|
|
694
|
+
.unwrap();
|
|
695
|
+
let cw = worker.core_worker.clone();
|
|
696
|
+
let client = client.clone();
|
|
697
|
+
subfs.push(async move {
|
|
698
|
+
// Evict the workflow
|
|
699
|
+
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
700
|
+
cw.request_workflow_eviction(&run_id);
|
|
701
|
+
// Wake up workflow by sending signal
|
|
702
|
+
client
|
|
703
|
+
.signal_workflow_execution(
|
|
704
|
+
wf_id,
|
|
705
|
+
run_id.clone(),
|
|
706
|
+
"whaatever".to_string(),
|
|
707
|
+
None,
|
|
708
|
+
None,
|
|
709
|
+
)
|
|
710
|
+
.await
|
|
711
|
+
.unwrap();
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
let runf = async {
|
|
715
|
+
worker.run_until_done().await.unwrap();
|
|
716
|
+
};
|
|
717
|
+
tokio::join!(subfs.collect::<Vec<_>>(), runf);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
#[rstest::rstest]
|
|
721
|
+
#[tokio::test]
|
|
722
|
+
async fn weird_la_nondeterminism_repro(#[values(true, false)] fix_hist: bool) {
|
|
723
|
+
init_integ_telem();
|
|
724
|
+
let mut hist = history_from_proto_binary(
|
|
725
|
+
"histories/evict_while_la_running_no_interference-85_history.bin",
|
|
726
|
+
)
|
|
727
|
+
.await
|
|
728
|
+
.unwrap();
|
|
729
|
+
if fix_hist {
|
|
730
|
+
// Replace broken ending with accurate ending
|
|
731
|
+
hist.events.truncate(20);
|
|
732
|
+
let mut thb = TestHistoryBuilder::from_history(hist.events);
|
|
733
|
+
thb.add_workflow_task_completed();
|
|
734
|
+
thb.add_workflow_execution_completed();
|
|
735
|
+
hist = thb.get_full_history_info().unwrap().into();
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
let mut worker = replay_sdk_worker([HistoryForReplay::new(hist, "fake".to_owned())]);
|
|
739
|
+
worker.register_wf(
|
|
740
|
+
"evict_while_la_running_no_interference",
|
|
741
|
+
la_problem_workflow,
|
|
742
|
+
);
|
|
743
|
+
worker.register_activity("delay", |_: ActContext, _: String| async {
|
|
744
|
+
tokio::time::sleep(Duration::from_secs(15)).await;
|
|
745
|
+
Ok(())
|
|
746
|
+
});
|
|
747
|
+
worker.run().await.unwrap();
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
#[tokio::test]
|
|
751
|
+
async fn second_weird_la_nondeterminism_repro() {
|
|
752
|
+
init_integ_telem();
|
|
753
|
+
let mut hist = history_from_proto_binary(
|
|
754
|
+
"histories/evict_while_la_running_no_interference-23_history.bin",
|
|
755
|
+
)
|
|
756
|
+
.await
|
|
757
|
+
.unwrap();
|
|
758
|
+
// Chop off uninteresting ending
|
|
759
|
+
hist.events.truncate(24);
|
|
760
|
+
let mut thb = TestHistoryBuilder::from_history(hist.events);
|
|
761
|
+
// thb.add_workflow_task_completed();
|
|
762
|
+
thb.add_workflow_execution_completed();
|
|
763
|
+
hist = thb.get_full_history_info().unwrap().into();
|
|
764
|
+
|
|
765
|
+
let mut worker = replay_sdk_worker([HistoryForReplay::new(hist, "fake".to_owned())]);
|
|
766
|
+
worker.register_wf(
|
|
767
|
+
"evict_while_la_running_no_interference",
|
|
768
|
+
la_problem_workflow,
|
|
769
|
+
);
|
|
770
|
+
worker.register_activity("delay", |_: ActContext, _: String| async {
|
|
771
|
+
tokio::time::sleep(Duration::from_secs(15)).await;
|
|
772
|
+
Ok(())
|
|
773
|
+
});
|
|
774
|
+
worker.run().await.unwrap();
|
|
775
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
use temporal_client::WorkflowClientTrait;
|
|
2
|
+
use temporal_sdk::{WfContext, WorkflowResult};
|
|
3
|
+
use temporal_sdk_core_protos::coresdk::{AsJsonPayloadExt, FromJsonPayloadExt};
|
|
4
|
+
use temporal_sdk_core_test_utils::CoreWfStarter;
|
|
5
|
+
use uuid::Uuid;
|
|
6
|
+
|
|
7
|
+
static FIELD_A: &str = "cat_name";
|
|
8
|
+
static FIELD_B: &str = "cute_level";
|
|
9
|
+
|
|
10
|
+
async fn memo_upserter(ctx: WfContext) -> WorkflowResult<()> {
|
|
11
|
+
ctx.upsert_memo([
|
|
12
|
+
(FIELD_A.to_string(), "enchi".as_json_payload().unwrap()),
|
|
13
|
+
(FIELD_B.to_string(), 9001.as_json_payload().unwrap()),
|
|
14
|
+
]);
|
|
15
|
+
Ok(().into())
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
#[tokio::test]
|
|
19
|
+
async fn sends_modify_wf_props() {
|
|
20
|
+
let wf_name = "can_upsert_memo";
|
|
21
|
+
let wf_id = Uuid::new_v4();
|
|
22
|
+
let mut starter = CoreWfStarter::new(wf_name);
|
|
23
|
+
let mut worker = starter.worker().await;
|
|
24
|
+
|
|
25
|
+
worker.register_wf(wf_name, memo_upserter);
|
|
26
|
+
let run_id = worker
|
|
27
|
+
.submit_wf(wf_id.to_string(), wf_name, vec![], Default::default())
|
|
28
|
+
.await
|
|
29
|
+
.unwrap();
|
|
30
|
+
worker.run_until_done().await.unwrap();
|
|
31
|
+
|
|
32
|
+
let memo = starter
|
|
33
|
+
.get_client()
|
|
34
|
+
.await
|
|
35
|
+
.describe_workflow_execution(wf_id.to_string(), Some(run_id))
|
|
36
|
+
.await
|
|
37
|
+
.unwrap()
|
|
38
|
+
.workflow_execution_info
|
|
39
|
+
.unwrap()
|
|
40
|
+
.memo
|
|
41
|
+
.unwrap()
|
|
42
|
+
.fields;
|
|
43
|
+
let catname = memo.get(FIELD_A).unwrap();
|
|
44
|
+
let cuteness = memo.get(FIELD_B).unwrap();
|
|
45
|
+
for payload in [catname, cuteness] {
|
|
46
|
+
assert_eq!(
|
|
47
|
+
&b"json/plain".to_vec(),
|
|
48
|
+
payload.metadata.get("encoding").unwrap()
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
assert_eq!("enchi", String::from_json_payload(catname).unwrap());
|
|
52
|
+
assert_eq!(9001, usize::from_json_payload(cuteness).unwrap());
|
|
53
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
use assert_matches::assert_matches;
|
|
2
|
-
use
|
|
3
|
-
use
|
|
2
|
+
use parking_lot::Mutex;
|
|
3
|
+
use std::{collections::HashSet, sync::Arc, time::Duration};
|
|
4
|
+
use temporal_sdk::{interceptors::WorkerInterceptor, WfContext, Worker, WorkflowFunction};
|
|
5
|
+
use temporal_sdk_core::replay::{HistoryFeeder, HistoryForReplay};
|
|
4
6
|
use temporal_sdk_core_api::errors::{PollActivityError, PollWfError};
|
|
5
7
|
use temporal_sdk_core_protos::{
|
|
6
8
|
coresdk::{
|
|
@@ -8,20 +10,29 @@ use temporal_sdk_core_protos::{
|
|
|
8
10
|
workflow_commands::{ScheduleActivity, StartTimer},
|
|
9
11
|
workflow_completion::WorkflowActivationCompletion,
|
|
10
12
|
},
|
|
11
|
-
DEFAULT_WORKFLOW_TYPE,
|
|
13
|
+
TestHistoryBuilder, DEFAULT_WORKFLOW_TYPE,
|
|
12
14
|
};
|
|
13
15
|
use temporal_sdk_core_test_utils::{
|
|
14
|
-
canned_histories, history_from_proto_binary, init_core_replay_preloaded,
|
|
16
|
+
canned_histories, history_from_proto_binary, init_core_replay_preloaded, replay_sdk_worker,
|
|
17
|
+
replay_sdk_worker_stream, WorkerTestHelpers,
|
|
15
18
|
};
|
|
16
19
|
use tokio::join;
|
|
17
20
|
|
|
21
|
+
fn test_hist_to_replay(t: TestHistoryBuilder) -> HistoryForReplay {
|
|
22
|
+
let hi = t.get_full_history_info().unwrap().into();
|
|
23
|
+
HistoryForReplay::new(hi, "fake".to_string())
|
|
24
|
+
}
|
|
25
|
+
|
|
18
26
|
#[tokio::test]
|
|
19
27
|
async fn timer_workflow_replay() {
|
|
20
|
-
let
|
|
28
|
+
let core = init_core_replay_preloaded(
|
|
21
29
|
"timer_workflow_replay",
|
|
22
|
-
|
|
23
|
-
.
|
|
24
|
-
|
|
30
|
+
[HistoryForReplay::new(
|
|
31
|
+
history_from_proto_binary("histories/timer_workflow_history.bin")
|
|
32
|
+
.await
|
|
33
|
+
.unwrap(),
|
|
34
|
+
"fake".to_owned(),
|
|
35
|
+
)],
|
|
25
36
|
);
|
|
26
37
|
let task = core.poll_workflow_activation().await.unwrap();
|
|
27
38
|
core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
|
|
@@ -43,6 +54,14 @@ async fn timer_workflow_replay() {
|
|
|
43
54
|
);
|
|
44
55
|
};
|
|
45
56
|
let poll_fut = async {
|
|
57
|
+
let evict_task = core
|
|
58
|
+
.poll_workflow_activation()
|
|
59
|
+
.await
|
|
60
|
+
.expect("Should be an eviction activation");
|
|
61
|
+
assert!(evict_task.eviction_reason().is_some());
|
|
62
|
+
core.complete_workflow_activation(WorkflowActivationCompletion::empty(evict_task.run_id))
|
|
63
|
+
.await
|
|
64
|
+
.unwrap();
|
|
46
65
|
assert_matches!(
|
|
47
66
|
core.poll_workflow_activation().await,
|
|
48
67
|
Err(PollWfError::ShutDown)
|
|
@@ -64,11 +83,14 @@ async fn timer_workflow_replay() {
|
|
|
64
83
|
|
|
65
84
|
#[tokio::test]
|
|
66
85
|
async fn workflow_nondeterministic_replay() {
|
|
67
|
-
let
|
|
86
|
+
let core = init_core_replay_preloaded(
|
|
68
87
|
"timer_workflow_replay",
|
|
69
|
-
|
|
70
|
-
.
|
|
71
|
-
|
|
88
|
+
[HistoryForReplay::new(
|
|
89
|
+
history_from_proto_binary("histories/timer_workflow_history.bin")
|
|
90
|
+
.await
|
|
91
|
+
.unwrap(),
|
|
92
|
+
"fake".to_owned(),
|
|
93
|
+
)],
|
|
72
94
|
);
|
|
73
95
|
let task = core.poll_workflow_activation().await.unwrap();
|
|
74
96
|
core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
|
|
@@ -102,9 +124,7 @@ async fn replay_using_wf_function() {
|
|
|
102
124
|
let num_timers = 10;
|
|
103
125
|
let t = canned_histories::long_sequential_timers(num_timers as usize);
|
|
104
126
|
let func = timers_wf(num_timers);
|
|
105
|
-
let
|
|
106
|
-
init_core_replay_preloaded("replay_bench", &t.get_full_history_info().unwrap().into());
|
|
107
|
-
let mut worker = Worker::new_from_core(worker, "replay_bench".to_string());
|
|
127
|
+
let mut worker = replay_sdk_worker([test_hist_to_replay(t)]);
|
|
108
128
|
worker.register_wf(DEFAULT_WORKFLOW_TYPE, func);
|
|
109
129
|
worker.run().await.unwrap();
|
|
110
130
|
}
|
|
@@ -117,16 +137,69 @@ async fn replay_ok_ending_with_terminated_or_timed_out() {
|
|
|
117
137
|
t2.add_workflow_execution_timed_out();
|
|
118
138
|
for t in [t1, t2] {
|
|
119
139
|
let func = timers_wf(1);
|
|
120
|
-
let
|
|
121
|
-
"replay_ok_terminate",
|
|
122
|
-
&t.get_full_history_info().unwrap().into(),
|
|
123
|
-
);
|
|
124
|
-
let mut worker = Worker::new_from_core(worker, "replay_ok_terminate".to_string());
|
|
140
|
+
let mut worker = replay_sdk_worker([test_hist_to_replay(t)]);
|
|
125
141
|
worker.register_wf(DEFAULT_WORKFLOW_TYPE, func);
|
|
126
142
|
worker.run().await.unwrap();
|
|
127
143
|
}
|
|
128
144
|
}
|
|
129
145
|
|
|
146
|
+
#[rstest::rstest]
|
|
147
|
+
#[tokio::test]
|
|
148
|
+
async fn multiple_histories_replay(#[values(false, true)] use_feeder: bool) {
|
|
149
|
+
let num_timers = 10;
|
|
150
|
+
let seq_timer_wf = timers_wf(num_timers);
|
|
151
|
+
let one_timer_wf = timers_wf(1);
|
|
152
|
+
let mut one_timer_hist = canned_histories::single_timer("1");
|
|
153
|
+
one_timer_hist.set_wf_type("onetimer");
|
|
154
|
+
let mut seq_timer_hist = canned_histories::long_sequential_timers(num_timers as usize);
|
|
155
|
+
seq_timer_hist.set_wf_type("seqtimer");
|
|
156
|
+
let (feeder, stream) = HistoryFeeder::new(1);
|
|
157
|
+
let mut worker = if use_feeder {
|
|
158
|
+
replay_sdk_worker_stream(stream)
|
|
159
|
+
} else {
|
|
160
|
+
replay_sdk_worker([
|
|
161
|
+
test_hist_to_replay(one_timer_hist.clone()),
|
|
162
|
+
test_hist_to_replay(seq_timer_hist.clone()),
|
|
163
|
+
])
|
|
164
|
+
};
|
|
165
|
+
let runs_ctr_i = UniqueRunsCounter::default();
|
|
166
|
+
let runs_ctr = runs_ctr_i.runs.clone();
|
|
167
|
+
worker.set_worker_interceptor(Box::new(runs_ctr_i));
|
|
168
|
+
worker.register_wf("onetimer", one_timer_wf);
|
|
169
|
+
worker.register_wf("seqtimer", seq_timer_wf);
|
|
170
|
+
|
|
171
|
+
if use_feeder {
|
|
172
|
+
let feed_fut = async move {
|
|
173
|
+
feeder
|
|
174
|
+
.feed(test_hist_to_replay(one_timer_hist))
|
|
175
|
+
.await
|
|
176
|
+
.unwrap();
|
|
177
|
+
feeder
|
|
178
|
+
.feed(test_hist_to_replay(seq_timer_hist))
|
|
179
|
+
.await
|
|
180
|
+
.unwrap();
|
|
181
|
+
};
|
|
182
|
+
let (_, runr) = join!(feed_fut, worker.run());
|
|
183
|
+
runr.unwrap();
|
|
184
|
+
} else {
|
|
185
|
+
worker.run().await.unwrap();
|
|
186
|
+
}
|
|
187
|
+
assert_eq!(runs_ctr.lock().len(), 2);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
#[tokio::test]
|
|
191
|
+
async fn multiple_histories_can_handle_dupe_run_ids() {
|
|
192
|
+
let mut hist1 = canned_histories::single_timer("1");
|
|
193
|
+
hist1.set_wf_type("onetimer");
|
|
194
|
+
let mut worker = replay_sdk_worker([
|
|
195
|
+
test_hist_to_replay(hist1.clone()),
|
|
196
|
+
test_hist_to_replay(hist1.clone()),
|
|
197
|
+
test_hist_to_replay(hist1),
|
|
198
|
+
]);
|
|
199
|
+
worker.register_wf("onetimer", timers_wf(1));
|
|
200
|
+
worker.run().await.unwrap();
|
|
201
|
+
}
|
|
202
|
+
|
|
130
203
|
fn timers_wf(num_timers: u32) -> WorkflowFunction {
|
|
131
204
|
WorkflowFunction::new(move |ctx: WfContext| async move {
|
|
132
205
|
for _ in 1..=num_timers {
|
|
@@ -135,3 +208,16 @@ fn timers_wf(num_timers: u32) -> WorkflowFunction {
|
|
|
135
208
|
Ok(().into())
|
|
136
209
|
})
|
|
137
210
|
}
|
|
211
|
+
|
|
212
|
+
#[derive(Default)]
|
|
213
|
+
struct UniqueRunsCounter {
|
|
214
|
+
runs: Arc<Mutex<HashSet<String>>>,
|
|
215
|
+
}
|
|
216
|
+
#[async_trait::async_trait(?Send)]
|
|
217
|
+
impl WorkerInterceptor for UniqueRunsCounter {
|
|
218
|
+
async fn on_workflow_activation_completion(&self, completion: &WorkflowActivationCompletion) {
|
|
219
|
+
self.runs.lock().insert(completion.run_id.clone());
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
fn on_shutdown(&self, _: &Worker) {}
|
|
223
|
+
}
|