@temporalio/core-bridge 1.5.2 → 1.7.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.
- package/Cargo.lock +304 -112
- package/lib/index.d.ts +8 -6
- package/lib/index.js.map +1 -1
- package/package.json +9 -4
- 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 +2 -2
- package/sdk-core/.buildkite/docker/docker-compose.yaml +1 -1
- package/sdk-core/.buildkite/pipeline.yml +2 -4
- package/sdk-core/.cargo/config.toml +5 -2
- package/sdk-core/.github/workflows/heavy.yml +29 -0
- package/sdk-core/Cargo.toml +1 -1
- package/sdk-core/README.md +20 -10
- package/sdk-core/client/src/lib.rs +215 -39
- package/sdk-core/client/src/metrics.rs +17 -8
- package/sdk-core/client/src/raw.rs +4 -4
- package/sdk-core/client/src/retry.rs +32 -20
- package/sdk-core/core/Cargo.toml +25 -12
- package/sdk-core/core/src/abstractions/take_cell.rs +28 -0
- package/sdk-core/core/src/abstractions.rs +204 -14
- package/sdk-core/core/src/core_tests/activity_tasks.rs +143 -50
- package/sdk-core/core/src/core_tests/child_workflows.rs +6 -5
- package/sdk-core/core/src/core_tests/determinism.rs +165 -2
- package/sdk-core/core/src/core_tests/local_activities.rs +431 -43
- package/sdk-core/core/src/core_tests/queries.rs +34 -16
- package/sdk-core/core/src/core_tests/workers.rs +8 -5
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +588 -55
- package/sdk-core/core/src/ephemeral_server/mod.rs +113 -12
- package/sdk-core/core/src/internal_flags.rs +155 -0
- package/sdk-core/core/src/lib.rs +16 -9
- package/sdk-core/core/src/protosext/mod.rs +1 -1
- package/sdk-core/core/src/replay/mod.rs +16 -27
- package/sdk-core/core/src/telemetry/log_export.rs +1 -1
- package/sdk-core/core/src/telemetry/metrics.rs +69 -35
- package/sdk-core/core/src/telemetry/mod.rs +60 -21
- package/sdk-core/core/src/telemetry/prometheus_server.rs +19 -13
- package/sdk-core/core/src/test_help/mod.rs +73 -14
- package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +119 -160
- package/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +89 -0
- package/sdk-core/core/src/worker/activities/local_activities.rs +379 -129
- package/sdk-core/core/src/worker/activities.rs +350 -175
- package/sdk-core/core/src/worker/client/mocks.rs +22 -2
- package/sdk-core/core/src/worker/client.rs +18 -2
- package/sdk-core/core/src/worker/mod.rs +183 -64
- package/sdk-core/core/src/worker/workflow/bridge.rs +1 -3
- package/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -5
- package/sdk-core/core/src/worker/workflow/history_update.rs +916 -277
- package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +216 -183
- package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +9 -12
- package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +7 -9
- package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +160 -87
- package/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +13 -14
- package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +7 -9
- package/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +14 -17
- package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +242 -110
- package/sdk-core/core/src/worker/workflow/machines/mod.rs +27 -19
- package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +9 -11
- package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +321 -206
- package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +13 -18
- package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +20 -29
- package/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +2 -2
- package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +257 -51
- package/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +6 -17
- package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +310 -150
- package/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +17 -20
- package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +31 -15
- package/sdk-core/core/src/worker/workflow/managed_run.rs +1052 -380
- package/sdk-core/core/src/worker/workflow/mod.rs +598 -390
- package/sdk-core/core/src/worker/workflow/run_cache.rs +40 -57
- package/sdk-core/core/src/worker/workflow/wft_extraction.rs +137 -0
- package/sdk-core/core/src/worker/workflow/wft_poller.rs +1 -4
- package/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +117 -0
- package/sdk-core/core/src/worker/workflow/workflow_stream/tonic_status_serde.rs +24 -0
- package/sdk-core/core/src/worker/workflow/workflow_stream.rs +469 -718
- package/sdk-core/core-api/Cargo.toml +2 -1
- package/sdk-core/core-api/src/errors.rs +1 -34
- package/sdk-core/core-api/src/lib.rs +19 -9
- package/sdk-core/core-api/src/telemetry.rs +4 -6
- package/sdk-core/core-api/src/worker.rs +19 -1
- package/sdk-core/etc/deps.svg +115 -140
- package/sdk-core/etc/regen-depgraph.sh +5 -0
- package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +86 -61
- package/sdk-core/fsm/rustfsm_trait/src/lib.rs +29 -71
- package/sdk-core/histories/ends_empty_wft_complete.bin +0 -0
- package/sdk-core/histories/evict_while_la_running_no_interference-16_history.bin +0 -0
- package/sdk-core/histories/old_change_marker_format.bin +0 -0
- package/sdk-core/protos/api_upstream/.github/CODEOWNERS +2 -1
- package/sdk-core/protos/api_upstream/Makefile +6 -6
- package/sdk-core/protos/api_upstream/build/go.mod +7 -0
- package/sdk-core/protos/api_upstream/build/go.sum +5 -0
- package/sdk-core/protos/api_upstream/build/tools.go +29 -0
- package/sdk-core/protos/api_upstream/go.mod +6 -0
- package/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +9 -2
- package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +7 -26
- package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +13 -2
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +3 -2
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +3 -7
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +3 -2
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +8 -8
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +25 -2
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +2 -2
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/query.proto +2 -2
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +2 -2
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/schedule.proto +2 -2
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +2 -2
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +24 -19
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +2 -2
- package/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +2 -2
- package/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +2 -2
- package/sdk-core/protos/api_upstream/temporal/api/filter/v1/message.proto +2 -2
- package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +49 -26
- package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +4 -2
- package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +5 -2
- package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +2 -2
- package/sdk-core/protos/api_upstream/temporal/api/protocol/v1/message.proto +57 -0
- package/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +2 -2
- package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +2 -2
- package/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +2 -2
- package/sdk-core/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +63 -0
- package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +2 -2
- package/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +71 -6
- package/sdk-core/protos/api_upstream/temporal/api/version/v1/message.proto +2 -2
- package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +2 -2
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +64 -28
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +4 -4
- package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +7 -8
- package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +10 -7
- package/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +19 -30
- package/sdk-core/protos/local/temporal/sdk/core/common/common.proto +1 -0
- package/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +1 -0
- package/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +8 -0
- package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +67 -60
- package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +85 -84
- package/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +9 -3
- package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +2 -2
- package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +2 -2
- package/sdk-core/sdk/Cargo.toml +5 -4
- package/sdk-core/sdk/src/lib.rs +108 -26
- package/sdk-core/sdk/src/workflow_context/options.rs +7 -1
- package/sdk-core/sdk/src/workflow_context.rs +24 -17
- package/sdk-core/sdk/src/workflow_future.rs +16 -15
- package/sdk-core/sdk-core-protos/Cargo.toml +5 -2
- package/sdk-core/sdk-core-protos/build.rs +36 -2
- package/sdk-core/sdk-core-protos/src/history_builder.rs +138 -106
- package/sdk-core/sdk-core-protos/src/history_info.rs +10 -1
- package/sdk-core/sdk-core-protos/src/lib.rs +272 -87
- package/sdk-core/sdk-core-protos/src/task_token.rs +12 -2
- package/sdk-core/test-utils/Cargo.toml +3 -1
- package/sdk-core/test-utils/src/canned_histories.rs +106 -296
- package/sdk-core/test-utils/src/histfetch.rs +1 -1
- package/sdk-core/test-utils/src/lib.rs +82 -23
- package/sdk-core/test-utils/src/wf_input_saver.rs +50 -0
- package/sdk-core/test-utils/src/workflows.rs +29 -0
- package/sdk-core/tests/fuzzy_workflow.rs +130 -0
- package/sdk-core/tests/{load_tests.rs → heavy_tests.rs} +125 -51
- package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +25 -3
- package/sdk-core/tests/integ_tests/heartbeat_tests.rs +10 -5
- package/sdk-core/tests/integ_tests/metrics_tests.rs +218 -16
- package/sdk-core/tests/integ_tests/polling_tests.rs +4 -47
- package/sdk-core/tests/integ_tests/queries_tests.rs +5 -128
- package/sdk-core/tests/integ_tests/visibility_tests.rs +83 -25
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +161 -72
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -0
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +6 -13
- package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +80 -3
- package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +6 -2
- package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -10
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +94 -200
- package/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +2 -4
- package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +34 -28
- package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +76 -7
- package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -0
- package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +18 -14
- package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +6 -20
- package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +10 -21
- package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +7 -8
- package/sdk-core/tests/integ_tests/workflow_tests.rs +13 -14
- package/sdk-core/tests/main.rs +3 -13
- package/sdk-core/tests/runner.rs +75 -36
- package/sdk-core/tests/wf_input_replay.rs +32 -0
- package/src/conversions.rs +14 -8
- package/src/runtime.rs +9 -8
- package/ts/index.ts +8 -6
- package/sdk-core/bridge-ffi/Cargo.toml +0 -24
- package/sdk-core/bridge-ffi/LICENSE.txt +0 -23
- package/sdk-core/bridge-ffi/build.rs +0 -25
- package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +0 -224
- package/sdk-core/bridge-ffi/src/lib.rs +0 -746
- package/sdk-core/bridge-ffi/src/wrappers.rs +0 -221
- package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +0 -210
- package/sdk-core/sdk/src/conversions.rs +0 -8
|
@@ -10,8 +10,8 @@ use opentelemetry::{
|
|
|
10
10
|
},
|
|
11
11
|
Context, KeyValue,
|
|
12
12
|
};
|
|
13
|
-
use std::{sync::Arc, time::Duration};
|
|
14
|
-
use
|
|
13
|
+
use std::{ops::Deref, sync::Arc, time::Duration};
|
|
14
|
+
use temporal_client::ClientMetricProvider;
|
|
15
15
|
|
|
16
16
|
/// Used to track context associated with metrics, and record/update them
|
|
17
17
|
///
|
|
@@ -24,6 +24,46 @@ pub(crate) struct MetricsContext {
|
|
|
24
24
|
instruments: Arc<Instruments>,
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
/// Wraps OTel's [Meter] to ensure we name our metrics properly, or any other temporal-specific
|
|
28
|
+
/// metrics customizations
|
|
29
|
+
#[derive(derive_more::Constructor)]
|
|
30
|
+
pub struct TemporalMeter<'a> {
|
|
31
|
+
inner: &'a Meter,
|
|
32
|
+
metrics_prefix: &'static str,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
impl<'a> TemporalMeter<'a> {
|
|
36
|
+
pub(crate) fn counter(&self, name: &'static str) -> Counter<u64> {
|
|
37
|
+
self.inner
|
|
38
|
+
.u64_counter(self.metrics_prefix.to_string() + name)
|
|
39
|
+
.init()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
pub(crate) fn histogram(&self, name: &'static str) -> Histogram<u64> {
|
|
43
|
+
self.inner
|
|
44
|
+
.u64_histogram(self.metrics_prefix.to_string() + name)
|
|
45
|
+
.init()
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
impl<'a> ClientMetricProvider for TemporalMeter<'a> {
|
|
50
|
+
fn counter(&self, name: &'static str) -> Counter<u64> {
|
|
51
|
+
self.counter(name)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
fn histogram(&self, name: &'static str) -> Histogram<u64> {
|
|
55
|
+
self.histogram(name)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
impl<'a> Deref for TemporalMeter<'a> {
|
|
60
|
+
type Target = dyn ClientMetricProvider + 'a;
|
|
61
|
+
|
|
62
|
+
fn deref(&self) -> &Self::Target {
|
|
63
|
+
self as &Self::Target
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
27
67
|
struct Instruments {
|
|
28
68
|
wf_completed_counter: Counter<u64>,
|
|
29
69
|
wf_canceled_counter: Counter<u64>,
|
|
@@ -54,10 +94,10 @@ impl MetricsContext {
|
|
|
54
94
|
Self {
|
|
55
95
|
ctx: Default::default(),
|
|
56
96
|
kvs: Default::default(),
|
|
57
|
-
instruments: Arc::new(Instruments::new_explicit(
|
|
97
|
+
instruments: Arc::new(Instruments::new_explicit(TemporalMeter::new(
|
|
58
98
|
&NoopMeterProvider::new().meter("fakemeter"),
|
|
59
99
|
"fakemetrics",
|
|
60
|
-
)),
|
|
100
|
+
))),
|
|
61
101
|
}
|
|
62
102
|
}
|
|
63
103
|
|
|
@@ -257,42 +297,36 @@ impl Instruments {
|
|
|
257
297
|
meter
|
|
258
298
|
} else {
|
|
259
299
|
no_op_meter = NoopMeterProvider::default().meter("no_op");
|
|
260
|
-
&no_op_meter
|
|
300
|
+
TemporalMeter::new(&no_op_meter, "fakemetrics")
|
|
261
301
|
};
|
|
262
|
-
Self::new_explicit(meter
|
|
302
|
+
Self::new_explicit(meter)
|
|
263
303
|
}
|
|
264
304
|
|
|
265
|
-
fn new_explicit(meter:
|
|
266
|
-
let ctr = |name: &'static str| -> Counter<u64> {
|
|
267
|
-
meter.u64_counter(metric_prefix.to_string() + name).init()
|
|
268
|
-
};
|
|
269
|
-
let hst = |name: &'static str| -> Histogram<u64> {
|
|
270
|
-
meter.u64_histogram(metric_prefix.to_string() + name).init()
|
|
271
|
-
};
|
|
305
|
+
fn new_explicit(meter: TemporalMeter) -> Self {
|
|
272
306
|
Self {
|
|
273
|
-
wf_completed_counter:
|
|
274
|
-
wf_canceled_counter:
|
|
275
|
-
wf_failed_counter:
|
|
276
|
-
wf_cont_counter:
|
|
277
|
-
wf_e2e_latency:
|
|
278
|
-
wf_task_queue_poll_empty_counter:
|
|
279
|
-
wf_task_queue_poll_succeed_counter:
|
|
280
|
-
wf_task_execution_failure_counter:
|
|
281
|
-
wf_task_sched_to_start_latency:
|
|
282
|
-
wf_task_replay_latency:
|
|
283
|
-
wf_task_execution_latency:
|
|
284
|
-
act_poll_no_task:
|
|
285
|
-
act_task_received_counter:
|
|
286
|
-
act_execution_failed:
|
|
287
|
-
act_sched_to_start_latency:
|
|
288
|
-
act_exec_latency:
|
|
307
|
+
wf_completed_counter: meter.counter("workflow_completed"),
|
|
308
|
+
wf_canceled_counter: meter.counter("workflow_canceled"),
|
|
309
|
+
wf_failed_counter: meter.counter("workflow_failed"),
|
|
310
|
+
wf_cont_counter: meter.counter("workflow_continue_as_new"),
|
|
311
|
+
wf_e2e_latency: meter.histogram(WF_E2E_LATENCY_NAME),
|
|
312
|
+
wf_task_queue_poll_empty_counter: meter.counter("workflow_task_queue_poll_empty"),
|
|
313
|
+
wf_task_queue_poll_succeed_counter: meter.counter("workflow_task_queue_poll_succeed"),
|
|
314
|
+
wf_task_execution_failure_counter: meter.counter("workflow_task_execution_failed"),
|
|
315
|
+
wf_task_sched_to_start_latency: meter.histogram(WF_TASK_SCHED_TO_START_LATENCY_NAME),
|
|
316
|
+
wf_task_replay_latency: meter.histogram(WF_TASK_REPLAY_LATENCY_NAME),
|
|
317
|
+
wf_task_execution_latency: meter.histogram(WF_TASK_EXECUTION_LATENCY_NAME),
|
|
318
|
+
act_poll_no_task: meter.counter("activity_poll_no_task"),
|
|
319
|
+
act_task_received_counter: meter.counter("activity_task_received"),
|
|
320
|
+
act_execution_failed: meter.counter("activity_execution_failed"),
|
|
321
|
+
act_sched_to_start_latency: meter.histogram(ACT_SCHED_TO_START_LATENCY_NAME),
|
|
322
|
+
act_exec_latency: meter.histogram(ACT_EXEC_LATENCY_NAME),
|
|
289
323
|
// name kept as worker start for compat with old sdk / what users expect
|
|
290
|
-
worker_registered:
|
|
291
|
-
num_pollers:
|
|
292
|
-
task_slots_available:
|
|
293
|
-
sticky_cache_hit:
|
|
294
|
-
sticky_cache_miss:
|
|
295
|
-
sticky_cache_size:
|
|
324
|
+
worker_registered: meter.counter("worker_start"),
|
|
325
|
+
num_pollers: meter.histogram(NUM_POLLERS_NAME),
|
|
326
|
+
task_slots_available: meter.histogram(TASK_SLOTS_AVAILABLE_NAME),
|
|
327
|
+
sticky_cache_hit: meter.counter("sticky_cache_hit"),
|
|
328
|
+
sticky_cache_miss: meter.counter("sticky_cache_miss"),
|
|
329
|
+
sticky_cache_size: meter.histogram(STICKY_CACHE_SIZE_NAME),
|
|
296
330
|
}
|
|
297
331
|
}
|
|
298
332
|
}
|
|
@@ -25,7 +25,18 @@ use opentelemetry::{
|
|
|
25
25
|
};
|
|
26
26
|
use opentelemetry_otlp::WithExportConfig;
|
|
27
27
|
use parking_lot::Mutex;
|
|
28
|
-
use std::{
|
|
28
|
+
use std::{
|
|
29
|
+
cell::RefCell,
|
|
30
|
+
collections::{HashMap, VecDeque},
|
|
31
|
+
convert::TryInto,
|
|
32
|
+
env,
|
|
33
|
+
net::SocketAddr,
|
|
34
|
+
sync::{
|
|
35
|
+
atomic::{AtomicBool, Ordering},
|
|
36
|
+
Arc,
|
|
37
|
+
},
|
|
38
|
+
time::Duration,
|
|
39
|
+
};
|
|
29
40
|
use temporal_sdk_core_api::telemetry::{
|
|
30
41
|
CoreLog, CoreTelemetry, Logger, MetricTemporality, MetricsExporter, OtelCollectorOptions,
|
|
31
42
|
TelemetryOptions, TraceExporter,
|
|
@@ -37,12 +48,10 @@ use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Layer};
|
|
|
37
48
|
const TELEM_SERVICE_NAME: &str = "temporal-core-sdk";
|
|
38
49
|
|
|
39
50
|
/// Help you construct an [EnvFilter] compatible filter string which will forward all core module
|
|
40
|
-
/// traces at `core_level` and all others (from 3rd party modules, etc) at `
|
|
51
|
+
/// traces at `core_level` and all others (from 3rd party modules, etc) at `other_level`.
|
|
41
52
|
pub fn construct_filter_string(core_level: Level, other_level: Level) -> String {
|
|
42
53
|
format!(
|
|
43
|
-
"{
|
|
44
|
-
o = other_level,
|
|
45
|
-
l = core_level
|
|
54
|
+
"{other_level},temporal_sdk_core={core_level},temporal_client={core_level},temporal_sdk={core_level}"
|
|
46
55
|
)
|
|
47
56
|
}
|
|
48
57
|
|
|
@@ -52,6 +61,7 @@ pub struct TelemetryInstance {
|
|
|
52
61
|
logs_out: Option<Mutex<CoreLogsOut>>,
|
|
53
62
|
metrics: Option<(Box<dyn MeterProvider + Send + Sync + 'static>, Meter)>,
|
|
54
63
|
trace_subscriber: Arc<dyn Subscriber + Send + Sync>,
|
|
64
|
+
prom_binding: Option<SocketAddr>,
|
|
55
65
|
_keepalive_rx: Receiver<()>,
|
|
56
66
|
}
|
|
57
67
|
|
|
@@ -61,6 +71,7 @@ impl TelemetryInstance {
|
|
|
61
71
|
logs_out: Option<Mutex<CoreLogsOut>>,
|
|
62
72
|
metric_prefix: &'static str,
|
|
63
73
|
mut meter_provider: Option<Box<dyn MeterProvider + Send + Sync + 'static>>,
|
|
74
|
+
prom_binding: Option<SocketAddr>,
|
|
64
75
|
keepalive_rx: Receiver<()>,
|
|
65
76
|
) -> Self {
|
|
66
77
|
let metrics = meter_provider.take().map(|mp| {
|
|
@@ -72,6 +83,7 @@ impl TelemetryInstance {
|
|
|
72
83
|
logs_out,
|
|
73
84
|
metrics,
|
|
74
85
|
trace_subscriber,
|
|
86
|
+
prom_binding,
|
|
75
87
|
_keepalive_rx: keepalive_rx,
|
|
76
88
|
}
|
|
77
89
|
}
|
|
@@ -81,6 +93,18 @@ impl TelemetryInstance {
|
|
|
81
93
|
pub fn trace_subscriber(&self) -> Arc<dyn Subscriber + Send + Sync> {
|
|
82
94
|
self.trace_subscriber.clone()
|
|
83
95
|
}
|
|
96
|
+
|
|
97
|
+
/// Returns the address the Prometheus server is bound to if it is running
|
|
98
|
+
pub fn prom_port(&self) -> Option<SocketAddr> {
|
|
99
|
+
self.prom_binding
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/// Returns our wrapper for OTel metric meters, can be used to, ex: initialize clients
|
|
103
|
+
pub fn get_metric_meter(&self) -> Option<TemporalMeter> {
|
|
104
|
+
self.metrics
|
|
105
|
+
.as_ref()
|
|
106
|
+
.map(|(_, m)| TemporalMeter::new(m, self.metric_prefix))
|
|
107
|
+
}
|
|
84
108
|
}
|
|
85
109
|
|
|
86
110
|
thread_local! {
|
|
@@ -121,10 +145,6 @@ impl CoreTelemetry for TelemetryInstance {
|
|
|
121
145
|
vec![]
|
|
122
146
|
}
|
|
123
147
|
}
|
|
124
|
-
|
|
125
|
-
fn get_metric_meter(&self) -> Option<&Meter> {
|
|
126
|
-
self.metrics.as_ref().map(|(_, m)| m)
|
|
127
|
-
}
|
|
128
148
|
}
|
|
129
149
|
|
|
130
150
|
/// Initialize tracing subscribers/output and logging export, returning a [TelemetryInstance]
|
|
@@ -151,6 +171,7 @@ pub fn telemetry_init(opts: TelemetryOptions) -> Result<TelemetryInstance, anyho
|
|
|
151
171
|
// Parts of telem dat ====
|
|
152
172
|
let mut logs_out = None;
|
|
153
173
|
let metric_prefix = metric_prefix(&opts);
|
|
174
|
+
let mut prom_binding = None;
|
|
154
175
|
// =======================
|
|
155
176
|
|
|
156
177
|
// Tracing subscriber layers =========
|
|
@@ -200,11 +221,15 @@ pub fn telemetry_init(opts: TelemetryOptions) -> Result<TelemetryInstance, anyho
|
|
|
200
221
|
let aggregator = SDKAggSelector { metric_prefix };
|
|
201
222
|
match metrics {
|
|
202
223
|
MetricsExporter::Prometheus(addr) => {
|
|
203
|
-
let srv =
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
224
|
+
let srv = runtime.block_on(async {
|
|
225
|
+
PromServer::new(
|
|
226
|
+
*addr,
|
|
227
|
+
aggregator,
|
|
228
|
+
metric_temporality_to_selector(opts.metric_temporality),
|
|
229
|
+
&opts.global_tags,
|
|
230
|
+
)
|
|
231
|
+
})?;
|
|
232
|
+
prom_binding = Some(srv.bound_addr());
|
|
208
233
|
let mp = srv.exporter.meter_provider()?;
|
|
209
234
|
runtime.spawn(async move { srv.run().await });
|
|
210
235
|
Some(Box::new(mp) as Box<dyn MeterProvider + Send + Sync>)
|
|
@@ -221,7 +246,7 @@ pub fn telemetry_init(opts: TelemetryOptions) -> Result<TelemetryInstance, anyho
|
|
|
221
246
|
runtime::Tokio,
|
|
222
247
|
)
|
|
223
248
|
.with_period(metric_periodicity.unwrap_or_else(|| Duration::from_secs(1)))
|
|
224
|
-
.with_resource(default_resource())
|
|
249
|
+
.with_resource(default_resource(&opts.global_tags))
|
|
225
250
|
.with_exporter(
|
|
226
251
|
opentelemetry_otlp::new_exporter()
|
|
227
252
|
.tonic()
|
|
@@ -242,7 +267,8 @@ pub fn telemetry_init(opts: TelemetryOptions) -> Result<TelemetryInstance, anyho
|
|
|
242
267
|
match &tracing.exporter {
|
|
243
268
|
TraceExporter::Otel(OtelCollectorOptions { url, headers, .. }) => {
|
|
244
269
|
runtime.block_on(async {
|
|
245
|
-
let tracer_cfg =
|
|
270
|
+
let tracer_cfg =
|
|
271
|
+
Config::default().with_resource(default_resource(&opts.global_tags));
|
|
246
272
|
let tracer = opentelemetry_otlp::new_pipeline()
|
|
247
273
|
.tracing()
|
|
248
274
|
.with_exporter(
|
|
@@ -276,6 +302,7 @@ pub fn telemetry_init(opts: TelemetryOptions) -> Result<TelemetryInstance, anyho
|
|
|
276
302
|
logs_out,
|
|
277
303
|
metric_prefix,
|
|
278
304
|
meter_provider,
|
|
305
|
+
prom_binding,
|
|
279
306
|
keepalive_rx,
|
|
280
307
|
))
|
|
281
308
|
.expect("Must be able to send telem instance out of thread");
|
|
@@ -295,10 +322,17 @@ pub fn telemetry_init(opts: TelemetryOptions) -> Result<TelemetryInstance, anyho
|
|
|
295
322
|
}
|
|
296
323
|
}
|
|
297
324
|
|
|
298
|
-
/// Initialize telemetry/tracing globally. Useful for testing.
|
|
325
|
+
/// Initialize telemetry/tracing globally. Useful for testing. Only takes affect when called
|
|
326
|
+
/// the first time. Subsequent calls are ignored.
|
|
299
327
|
pub fn telemetry_init_global(opts: TelemetryOptions) -> Result<(), anyhow::Error> {
|
|
300
|
-
|
|
301
|
-
|
|
328
|
+
static INITTED: AtomicBool = AtomicBool::new(false);
|
|
329
|
+
if INITTED
|
|
330
|
+
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
|
|
331
|
+
.is_ok()
|
|
332
|
+
{
|
|
333
|
+
let ti = telemetry_init(opts)?;
|
|
334
|
+
tracing::subscriber::set_global_default(ti.trace_subscriber())?;
|
|
335
|
+
}
|
|
302
336
|
Ok(())
|
|
303
337
|
}
|
|
304
338
|
|
|
@@ -306,8 +340,12 @@ fn default_resource_kvs() -> &'static [KeyValue] {
|
|
|
306
340
|
static INSTANCE: OnceCell<[KeyValue; 1]> = OnceCell::new();
|
|
307
341
|
INSTANCE.get_or_init(|| [KeyValue::new("service.name", TELEM_SERVICE_NAME)])
|
|
308
342
|
}
|
|
309
|
-
|
|
310
|
-
|
|
343
|
+
|
|
344
|
+
fn default_resource(override_values: &HashMap<String, String>) -> Resource {
|
|
345
|
+
let override_kvs = override_values
|
|
346
|
+
.iter()
|
|
347
|
+
.map(|(k, v)| KeyValue::new(k.clone(), v.clone()));
|
|
348
|
+
Resource::new(default_resource_kvs().iter().cloned()).merge(&Resource::new(override_kvs))
|
|
311
349
|
}
|
|
312
350
|
|
|
313
351
|
fn metric_temporality_to_selector(
|
|
@@ -360,6 +398,7 @@ pub mod test_initters {
|
|
|
360
398
|
.unwrap();
|
|
361
399
|
}
|
|
362
400
|
}
|
|
401
|
+
use crate::telemetry::metrics::TemporalMeter;
|
|
363
402
|
#[cfg(test)]
|
|
364
403
|
pub use test_initters::*;
|
|
365
404
|
|
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
use crate::telemetry::default_resource;
|
|
2
2
|
use hyper::{
|
|
3
3
|
header::CONTENT_TYPE,
|
|
4
|
+
server::conn::AddrIncoming,
|
|
4
5
|
service::{make_service_fn, service_fn},
|
|
5
6
|
Body, Method, Request, Response, Server,
|
|
6
7
|
};
|
|
7
|
-
use opentelemetry::{
|
|
8
|
-
metrics::
|
|
9
|
-
|
|
10
|
-
export::metrics::{aggregation::TemporalitySelector, AggregatorSelector},
|
|
11
|
-
metrics::{controllers, processors},
|
|
12
|
-
},
|
|
8
|
+
use opentelemetry::sdk::{
|
|
9
|
+
export::metrics::{aggregation::TemporalitySelector, AggregatorSelector},
|
|
10
|
+
metrics::{controllers, processors},
|
|
13
11
|
};
|
|
14
12
|
use opentelemetry_prometheus::{ExporterBuilder, PrometheusExporter};
|
|
15
13
|
use prometheus::{Encoder, TextEncoder};
|
|
16
|
-
use std::{convert::Infallible, net::SocketAddr, sync::Arc};
|
|
14
|
+
use std::{collections::HashMap, convert::Infallible, net::SocketAddr, sync::Arc, time::Duration};
|
|
17
15
|
|
|
18
16
|
/// Exposes prometheus metrics for scraping
|
|
19
17
|
pub(super) struct PromServer {
|
|
20
|
-
|
|
18
|
+
bound_addr: AddrIncoming,
|
|
21
19
|
pub exporter: Arc<PrometheusExporter>,
|
|
22
20
|
}
|
|
23
21
|
|
|
@@ -26,19 +24,23 @@ impl PromServer {
|
|
|
26
24
|
addr: SocketAddr,
|
|
27
25
|
aggregation: impl AggregatorSelector + Send + Sync + 'static,
|
|
28
26
|
temporality: impl TemporalitySelector + Send + Sync + 'static,
|
|
29
|
-
|
|
27
|
+
tags: &HashMap<String, String>,
|
|
28
|
+
) -> Result<Self, anyhow::Error> {
|
|
30
29
|
let controller =
|
|
31
30
|
controllers::basic(processors::factory(aggregation, temporality).with_memory(true))
|
|
32
|
-
|
|
31
|
+
// Because Prom is pull-based, make this always refresh
|
|
32
|
+
.with_collect_period(Duration::from_secs(0))
|
|
33
|
+
.with_resource(default_resource(tags))
|
|
33
34
|
.build();
|
|
34
35
|
let exporter = ExporterBuilder::new(controller).try_init()?;
|
|
36
|
+
let bound_addr = AddrIncoming::bind(&addr)?;
|
|
35
37
|
Ok(Self {
|
|
36
38
|
exporter: Arc::new(exporter),
|
|
37
|
-
|
|
39
|
+
bound_addr,
|
|
38
40
|
})
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
pub async fn run(
|
|
43
|
+
pub async fn run(self) -> hyper::Result<()> {
|
|
42
44
|
// Spin up hyper server to serve metrics for scraping. We use hyper since we already depend
|
|
43
45
|
// on it via Tonic.
|
|
44
46
|
let expclone = self.exporter.clone();
|
|
@@ -46,9 +48,13 @@ impl PromServer {
|
|
|
46
48
|
let expclone = expclone.clone();
|
|
47
49
|
async move { Ok::<_, Infallible>(service_fn(move |req| metrics_req(req, expclone.clone()))) }
|
|
48
50
|
});
|
|
49
|
-
let server = Server::
|
|
51
|
+
let server = Server::builder(self.bound_addr).serve(svc);
|
|
50
52
|
server.await
|
|
51
53
|
}
|
|
54
|
+
|
|
55
|
+
pub fn bound_addr(&self) -> SocketAddr {
|
|
56
|
+
self.bound_addr.local_addr()
|
|
57
|
+
}
|
|
52
58
|
}
|
|
53
59
|
|
|
54
60
|
/// Serves prometheus metrics in the expected format for scraping
|
|
@@ -14,6 +14,7 @@ use crate::{
|
|
|
14
14
|
},
|
|
15
15
|
TaskToken, Worker, WorkerConfig, WorkerConfigBuilder,
|
|
16
16
|
};
|
|
17
|
+
use async_trait::async_trait;
|
|
17
18
|
use bimap::BiMap;
|
|
18
19
|
use futures::{future::BoxFuture, stream, stream::BoxStream, FutureExt, Stream, StreamExt};
|
|
19
20
|
use mockall::TimesRange;
|
|
@@ -29,7 +30,10 @@ use std::{
|
|
|
29
30
|
task::{Context, Poll},
|
|
30
31
|
time::Duration,
|
|
31
32
|
};
|
|
32
|
-
use temporal_sdk_core_api::
|
|
33
|
+
use temporal_sdk_core_api::{
|
|
34
|
+
errors::{PollActivityError, PollWfError},
|
|
35
|
+
Worker as WorkerTrait,
|
|
36
|
+
};
|
|
33
37
|
use temporal_sdk_core_protos::{
|
|
34
38
|
coresdk::{
|
|
35
39
|
workflow_activation::WorkflowActivation,
|
|
@@ -52,7 +56,6 @@ use tokio_stream::wrappers::UnboundedReceiverStream;
|
|
|
52
56
|
use tokio_util::sync::CancellationToken;
|
|
53
57
|
|
|
54
58
|
pub const TEST_Q: &str = "q";
|
|
55
|
-
pub static NO_MORE_WORK_ERROR_MSG: &str = "No more work to do";
|
|
56
59
|
|
|
57
60
|
pub fn test_worker_cfg() -> WorkerConfigBuilder {
|
|
58
61
|
let mut wcb = WorkerConfigBuilder::default();
|
|
@@ -148,6 +151,7 @@ pub(crate) fn mock_worker(mocks: MocksHolder) -> Worker {
|
|
|
148
151
|
mocks.inputs.wft_stream,
|
|
149
152
|
act_poller,
|
|
150
153
|
MetricsContext::no_op(),
|
|
154
|
+
None,
|
|
151
155
|
CancellationToken::new(),
|
|
152
156
|
)
|
|
153
157
|
}
|
|
@@ -301,7 +305,7 @@ where
|
|
|
301
305
|
}
|
|
302
306
|
.boxed()
|
|
303
307
|
} else {
|
|
304
|
-
async {
|
|
308
|
+
async { None }.boxed()
|
|
305
309
|
}
|
|
306
310
|
});
|
|
307
311
|
Box::new(mock_poller) as BoxedPoller<T>
|
|
@@ -375,10 +379,12 @@ pub(crate) struct MockPollCfg {
|
|
|
375
379
|
pub expect_fail_wft_matcher:
|
|
376
380
|
Box<dyn Fn(&TaskToken, &WorkflowTaskFailedCause, &Option<Failure>) -> bool + Send>,
|
|
377
381
|
pub completion_asserts: Option<Box<dyn Fn(&WorkflowTaskCompletion) + Send>>,
|
|
382
|
+
pub num_expected_completions: Option<TimesRange>,
|
|
378
383
|
/// If being used with the Rust SDK, this is set true. It ensures pollers will not error out
|
|
379
384
|
/// early with no work, since we cannot know the exact number of times polling will happen.
|
|
380
385
|
/// Instead, they will just block forever.
|
|
381
386
|
pub using_rust_sdk: bool,
|
|
387
|
+
pub make_poll_stream_interminable: bool,
|
|
382
388
|
}
|
|
383
389
|
|
|
384
390
|
impl MockPollCfg {
|
|
@@ -395,7 +401,9 @@ impl MockPollCfg {
|
|
|
395
401
|
mock_client: mock_workflow_client(),
|
|
396
402
|
expect_fail_wft_matcher: Box::new(|_, _, _| true),
|
|
397
403
|
completion_asserts: None,
|
|
404
|
+
num_expected_completions: None,
|
|
398
405
|
using_rust_sdk: false,
|
|
406
|
+
make_poll_stream_interminable: false,
|
|
399
407
|
}
|
|
400
408
|
}
|
|
401
409
|
pub fn from_resp_batches(
|
|
@@ -416,7 +424,9 @@ impl MockPollCfg {
|
|
|
416
424
|
mock_client,
|
|
417
425
|
expect_fail_wft_matcher: Box::new(|_, _, _| true),
|
|
418
426
|
completion_asserts: None,
|
|
427
|
+
num_expected_completions: None,
|
|
419
428
|
using_rust_sdk: false,
|
|
429
|
+
make_poll_stream_interminable: false,
|
|
420
430
|
}
|
|
421
431
|
}
|
|
422
432
|
}
|
|
@@ -584,16 +594,20 @@ pub(crate) fn build_mock_pollers(mut cfg: MockPollCfg) -> MocksHolder {
|
|
|
584
594
|
);
|
|
585
595
|
|
|
586
596
|
let outstanding = outstanding_wf_task_tokens.clone();
|
|
587
|
-
cfg.mock_client
|
|
588
|
-
|
|
589
|
-
.
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
+
let expect_completes = cfg.mock_client.expect_complete_workflow_task();
|
|
598
|
+
if let Some(range) = cfg.num_expected_completions {
|
|
599
|
+
expect_completes.times(range);
|
|
600
|
+
} else if cfg.completion_asserts.is_some() {
|
|
601
|
+
expect_completes.times(1..);
|
|
602
|
+
}
|
|
603
|
+
expect_completes.returning(move |comp| {
|
|
604
|
+
if let Some(ass) = cfg.completion_asserts.as_ref() {
|
|
605
|
+
// tee hee
|
|
606
|
+
ass(&comp)
|
|
607
|
+
}
|
|
608
|
+
outstanding.release_token(&comp.task_token);
|
|
609
|
+
Ok(RespondWorkflowTaskCompletedResponse::default())
|
|
610
|
+
});
|
|
597
611
|
let outstanding = outstanding_wf_task_tokens.clone();
|
|
598
612
|
cfg.mock_client
|
|
599
613
|
.expect_fail_workflow_task()
|
|
@@ -612,11 +626,15 @@ pub(crate) fn build_mock_pollers(mut cfg: MockPollCfg) -> MocksHolder {
|
|
|
612
626
|
Ok(Default::default())
|
|
613
627
|
});
|
|
614
628
|
|
|
615
|
-
MocksHolder {
|
|
629
|
+
let mut mh = MocksHolder {
|
|
616
630
|
client: Arc::new(cfg.mock_client),
|
|
617
631
|
inputs: mock_worker,
|
|
618
632
|
outstanding_task_map: Some(outstanding_wf_task_tokens),
|
|
633
|
+
};
|
|
634
|
+
if cfg.make_poll_stream_interminable {
|
|
635
|
+
mh.make_wft_stream_interminable();
|
|
619
636
|
}
|
|
637
|
+
mh
|
|
620
638
|
}
|
|
621
639
|
|
|
622
640
|
pub struct QueueResponse<T> {
|
|
@@ -835,6 +853,7 @@ pub(crate) fn gen_assert_and_fail(asserter: &dyn Fn(&WorkflowActivation)) -> Ass
|
|
|
835
853
|
message: "Intentional test failure".to_string(),
|
|
836
854
|
..Default::default()
|
|
837
855
|
}),
|
|
856
|
+
..Default::default()
|
|
838
857
|
}
|
|
839
858
|
.into(),
|
|
840
859
|
)
|
|
@@ -880,3 +899,43 @@ macro_rules! prost_dur {
|
|
|
880
899
|
.expect("test duration fits")
|
|
881
900
|
};
|
|
882
901
|
}
|
|
902
|
+
|
|
903
|
+
#[async_trait]
|
|
904
|
+
pub(crate) trait WorkerExt {
|
|
905
|
+
/// Initiate shutdown, drain the pollers, and wait for shutdown to complete.
|
|
906
|
+
async fn drain_pollers_and_shutdown(self);
|
|
907
|
+
/// Initiate shutdown, drain the *activity* poller, and wait for shutdown to complete.
|
|
908
|
+
/// Takes a ref because of that one test that needs it.
|
|
909
|
+
async fn drain_activity_poller_and_shutdown(&self);
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
#[async_trait]
|
|
913
|
+
impl WorkerExt for Worker {
|
|
914
|
+
async fn drain_pollers_and_shutdown(self) {
|
|
915
|
+
self.initiate_shutdown();
|
|
916
|
+
tokio::join!(
|
|
917
|
+
async {
|
|
918
|
+
assert_matches!(
|
|
919
|
+
self.poll_activity_task().await.unwrap_err(),
|
|
920
|
+
PollActivityError::ShutDown
|
|
921
|
+
);
|
|
922
|
+
},
|
|
923
|
+
async {
|
|
924
|
+
assert_matches!(
|
|
925
|
+
self.poll_workflow_activation().await.unwrap_err(),
|
|
926
|
+
PollWfError::ShutDown
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
);
|
|
930
|
+
self.finalize_shutdown().await;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
async fn drain_activity_poller_and_shutdown(&self) {
|
|
934
|
+
self.initiate_shutdown();
|
|
935
|
+
assert_matches!(
|
|
936
|
+
self.poll_activity_task().await.unwrap_err(),
|
|
937
|
+
PollActivityError::ShutDown
|
|
938
|
+
);
|
|
939
|
+
self.shutdown().await;
|
|
940
|
+
}
|
|
941
|
+
}
|