temporalio 0.0.2 → 0.1.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.
- checksums.yaml +4 -4
- data/README.md +25 -23
- data/bridge/Cargo.lock +168 -59
- data/bridge/Cargo.toml +4 -2
- data/bridge/sdk-core/README.md +19 -6
- data/bridge/sdk-core/client/src/lib.rs +215 -39
- data/bridge/sdk-core/client/src/metrics.rs +17 -8
- data/bridge/sdk-core/client/src/raw.rs +4 -4
- data/bridge/sdk-core/client/src/retry.rs +32 -20
- data/bridge/sdk-core/core/Cargo.toml +22 -9
- data/bridge/sdk-core/core/src/abstractions.rs +203 -14
- data/bridge/sdk-core/core/src/core_tests/activity_tasks.rs +76 -41
- data/bridge/sdk-core/core/src/core_tests/determinism.rs +165 -2
- data/bridge/sdk-core/core/src/core_tests/local_activities.rs +204 -83
- data/bridge/sdk-core/core/src/core_tests/queries.rs +3 -4
- data/bridge/sdk-core/core/src/core_tests/workers.rs +1 -3
- data/bridge/sdk-core/core/src/core_tests/workflow_tasks.rs +397 -54
- data/bridge/sdk-core/core/src/ephemeral_server/mod.rs +106 -12
- data/bridge/sdk-core/core/src/internal_flags.rs +136 -0
- data/bridge/sdk-core/core/src/lib.rs +16 -9
- data/bridge/sdk-core/core/src/telemetry/log_export.rs +1 -1
- data/bridge/sdk-core/core/src/telemetry/metrics.rs +69 -35
- data/bridge/sdk-core/core/src/telemetry/mod.rs +29 -13
- data/bridge/sdk-core/core/src/telemetry/prometheus_server.rs +17 -12
- data/bridge/sdk-core/core/src/test_help/mod.rs +62 -12
- data/bridge/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +112 -156
- data/bridge/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +89 -0
- data/bridge/sdk-core/core/src/worker/activities/local_activities.rs +352 -122
- data/bridge/sdk-core/core/src/worker/activities.rs +233 -157
- data/bridge/sdk-core/core/src/worker/client/mocks.rs +22 -2
- data/bridge/sdk-core/core/src/worker/client.rs +18 -2
- data/bridge/sdk-core/core/src/worker/mod.rs +165 -58
- data/bridge/sdk-core/core/src/worker/workflow/bridge.rs +1 -3
- data/bridge/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -5
- data/bridge/sdk-core/core/src/worker/workflow/history_update.rs +856 -277
- data/bridge/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +100 -43
- data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +7 -7
- data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +5 -4
- data/bridge/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +87 -27
- data/bridge/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +5 -4
- data/bridge/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +5 -4
- data/bridge/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +5 -4
- data/bridge/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +137 -62
- data/bridge/sdk-core/core/src/worker/workflow/machines/mod.rs +25 -17
- data/bridge/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +7 -6
- data/bridge/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +103 -152
- data/bridge/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +7 -7
- data/bridge/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +9 -9
- data/bridge/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +2 -2
- data/bridge/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +14 -7
- data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +5 -16
- data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +201 -121
- data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +11 -14
- data/bridge/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +30 -15
- data/bridge/sdk-core/core/src/worker/workflow/managed_run.rs +1026 -376
- data/bridge/sdk-core/core/src/worker/workflow/mod.rs +460 -384
- data/bridge/sdk-core/core/src/worker/workflow/run_cache.rs +40 -57
- data/bridge/sdk-core/core/src/worker/workflow/wft_extraction.rs +125 -0
- data/bridge/sdk-core/core/src/worker/workflow/wft_poller.rs +1 -4
- data/bridge/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +117 -0
- data/bridge/sdk-core/core/src/worker/workflow/workflow_stream/tonic_status_serde.rs +24 -0
- data/bridge/sdk-core/core/src/worker/workflow/workflow_stream.rs +448 -718
- data/bridge/sdk-core/core-api/Cargo.toml +2 -1
- data/bridge/sdk-core/core-api/src/errors.rs +1 -34
- data/bridge/sdk-core/core-api/src/lib.rs +6 -2
- data/bridge/sdk-core/core-api/src/telemetry.rs +0 -6
- data/bridge/sdk-core/core-api/src/worker.rs +14 -1
- data/bridge/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +18 -15
- data/bridge/sdk-core/fsm/rustfsm_trait/src/lib.rs +8 -3
- data/bridge/sdk-core/histories/evict_while_la_running_no_interference-16_history.bin +0 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +5 -17
- data/bridge/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +11 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +1 -6
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +6 -6
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +5 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +22 -6
- data/bridge/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +48 -19
- data/bridge/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +2 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +3 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/{enums/v1/interaction_type.proto → protocol/v1/message.proto} +29 -11
- data/bridge/sdk-core/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +63 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +111 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +59 -28
- data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +2 -2
- data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +1 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +1 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +1 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/common/common.proto +1 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +1 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +1 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +7 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +1 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +6 -0
- data/bridge/sdk-core/sdk/Cargo.toml +3 -2
- data/bridge/sdk-core/sdk/src/lib.rs +87 -20
- data/bridge/sdk-core/sdk/src/workflow_future.rs +9 -8
- data/bridge/sdk-core/sdk-core-protos/Cargo.toml +5 -2
- data/bridge/sdk-core/sdk-core-protos/build.rs +36 -1
- data/bridge/sdk-core/sdk-core-protos/src/history_builder.rs +100 -87
- data/bridge/sdk-core/sdk-core-protos/src/history_info.rs +5 -1
- data/bridge/sdk-core/sdk-core-protos/src/lib.rs +175 -57
- data/bridge/sdk-core/sdk-core-protos/src/task_token.rs +12 -2
- data/bridge/sdk-core/test-utils/Cargo.toml +3 -1
- data/bridge/sdk-core/test-utils/src/canned_histories.rs +106 -296
- data/bridge/sdk-core/test-utils/src/histfetch.rs +1 -1
- data/bridge/sdk-core/test-utils/src/lib.rs +82 -23
- data/bridge/sdk-core/test-utils/src/wf_input_saver.rs +50 -0
- data/bridge/sdk-core/test-utils/src/workflows.rs +29 -0
- data/bridge/sdk-core/tests/fuzzy_workflow.rs +130 -0
- data/bridge/sdk-core/tests/{load_tests.rs → heavy_tests.rs} +125 -51
- data/bridge/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +25 -3
- data/bridge/sdk-core/tests/integ_tests/heartbeat_tests.rs +5 -3
- data/bridge/sdk-core/tests/integ_tests/metrics_tests.rs +218 -16
- data/bridge/sdk-core/tests/integ_tests/polling_tests.rs +4 -47
- data/bridge/sdk-core/tests/integ_tests/queries_tests.rs +5 -128
- data/bridge/sdk-core/tests/integ_tests/visibility_tests.rs +83 -25
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/activities.rs +93 -69
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +6 -13
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +1 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +6 -2
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -10
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +72 -191
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +1 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/patches.rs +7 -28
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/replay.rs +12 -7
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/signals.rs +18 -14
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +6 -20
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/timers.rs +10 -21
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +6 -4
- data/bridge/sdk-core/tests/integ_tests/workflow_tests.rs +10 -11
- data/bridge/sdk-core/tests/main.rs +3 -13
- data/bridge/sdk-core/tests/runner.rs +75 -36
- data/bridge/sdk-core/tests/wf_input_replay.rs +32 -0
- data/bridge/src/connection.rs +41 -25
- data/bridge/src/lib.rs +269 -14
- data/bridge/src/runtime.rs +1 -1
- data/bridge/src/test_server.rs +153 -0
- data/bridge/src/worker.rs +89 -16
- data/lib/gen/temporal/api/command/v1/message_pb.rb +4 -18
- data/lib/gen/temporal/api/common/v1/message_pb.rb +4 -0
- data/lib/gen/temporal/api/enums/v1/command_type_pb.rb +1 -3
- data/lib/gen/temporal/api/enums/v1/event_type_pb.rb +3 -3
- data/lib/gen/temporal/api/enums/v1/failed_cause_pb.rb +2 -0
- data/lib/gen/temporal/api/enums/v1/update_pb.rb +6 -4
- data/lib/gen/temporal/api/history/v1/message_pb.rb +27 -19
- data/lib/gen/temporal/api/namespace/v1/message_pb.rb +1 -0
- data/lib/gen/temporal/api/operatorservice/v1/request_response_pb.rb +3 -0
- data/lib/gen/temporal/api/protocol/v1/message_pb.rb +30 -0
- data/lib/gen/temporal/api/sdk/v1/task_complete_metadata_pb.rb +23 -0
- data/lib/gen/temporal/api/testservice/v1/request_response_pb.rb +49 -0
- data/lib/gen/temporal/api/testservice/v1/service_pb.rb +21 -0
- data/lib/gen/temporal/api/update/v1/message_pb.rb +72 -0
- data/lib/gen/temporal/api/workflowservice/v1/request_response_pb.rb +26 -16
- data/lib/gen/temporal/sdk/core/activity_result/activity_result_pb.rb +13 -9
- data/lib/gen/temporal/sdk/core/activity_task/activity_task_pb.rb +10 -6
- data/lib/gen/temporal/sdk/core/child_workflow/child_workflow_pb.rb +13 -9
- data/lib/gen/temporal/sdk/core/common/common_pb.rb +7 -3
- data/lib/gen/temporal/sdk/core/core_interface_pb.rb +9 -3
- data/lib/gen/temporal/sdk/core/external_data/external_data_pb.rb +7 -3
- data/lib/gen/temporal/sdk/core/workflow_activation/workflow_activation_pb.rb +27 -21
- data/lib/gen/temporal/sdk/core/workflow_commands/workflow_commands_pb.rb +28 -24
- data/lib/gen/temporal/sdk/core/workflow_completion/workflow_completion_pb.rb +12 -5
- data/lib/temporalio/activity/context.rb +13 -8
- data/lib/temporalio/activity/info.rb +1 -1
- data/lib/temporalio/bridge/connect_options.rb +15 -0
- data/lib/temporalio/bridge/retry_config.rb +24 -0
- data/lib/temporalio/bridge/tls_options.rb +19 -0
- data/lib/temporalio/client/implementation.rb +8 -8
- data/lib/temporalio/connection/retry_config.rb +44 -0
- data/lib/temporalio/connection/service.rb +20 -0
- data/lib/temporalio/connection/test_service.rb +92 -0
- data/lib/temporalio/connection/tls_options.rb +51 -0
- data/lib/temporalio/connection/workflow_service.rb +731 -0
- data/lib/temporalio/connection.rb +55 -720
- data/lib/temporalio/interceptor/activity_inbound.rb +22 -0
- data/lib/temporalio/interceptor/activity_outbound.rb +24 -0
- data/lib/temporalio/interceptor/chain.rb +5 -5
- data/lib/temporalio/interceptor/client.rb +8 -4
- data/lib/temporalio/interceptor.rb +22 -0
- data/lib/temporalio/retry_policy.rb +13 -3
- data/lib/temporalio/testing/time_skipping_handle.rb +32 -0
- data/lib/temporalio/testing/time_skipping_interceptor.rb +23 -0
- data/lib/temporalio/testing/workflow_environment.rb +112 -0
- data/lib/temporalio/testing.rb +175 -0
- data/lib/temporalio/version.rb +1 -1
- data/lib/temporalio/worker/activity_runner.rb +26 -4
- data/lib/temporalio/worker/activity_worker.rb +44 -18
- data/lib/temporalio/worker/sync_worker.rb +47 -11
- data/lib/temporalio/worker.rb +27 -21
- data/lib/temporalio/workflow/async.rb +46 -0
- data/lib/temporalio/workflow/future.rb +138 -0
- data/lib/temporalio/workflow/info.rb +76 -0
- data/temporalio.gemspec +4 -3
- metadata +67 -17
- data/bridge/sdk-core/Cargo.lock +0 -2606
- data/bridge/sdk-core/protos/api_upstream/temporal/api/interaction/v1/message.proto +0 -87
- data/lib/bridge.so +0 -0
- data/lib/gen/temporal/api/enums/v1/interaction_type_pb.rb +0 -25
- data/lib/gen/temporal/api/interaction/v1/message_pb.rb +0 -49
- data/lib/gen/temporal/sdk/core/bridge/bridge_pb.rb +0 -222
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
//! This module contains very generic helpers that can be used codebase-wide
|
|
2
2
|
|
|
3
3
|
use crate::MetricsContext;
|
|
4
|
+
use derive_more::DebugCustom;
|
|
4
5
|
use futures::{stream, Stream, StreamExt};
|
|
6
|
+
use std::sync::atomic::AtomicBool;
|
|
5
7
|
use std::{
|
|
6
8
|
fmt::{Debug, Formatter},
|
|
7
|
-
sync::
|
|
9
|
+
sync::{
|
|
10
|
+
atomic::{AtomicUsize, Ordering},
|
|
11
|
+
Arc,
|
|
12
|
+
},
|
|
8
13
|
};
|
|
9
14
|
use tokio::sync::{AcquireError, OwnedSemaphorePermit, Semaphore, TryAcquireError};
|
|
15
|
+
use tokio_util::sync::CancellationToken;
|
|
10
16
|
|
|
11
17
|
/// Wraps a [Semaphore] with a function call that is fed the available permits any time a permit is
|
|
12
18
|
/// acquired or restored through the provided methods
|
|
13
19
|
#[derive(Clone)]
|
|
14
20
|
pub(crate) struct MeteredSemaphore {
|
|
15
21
|
sem: Arc<Semaphore>,
|
|
22
|
+
/// The number of permit owners who have acquired a permit from the semaphore, but are not yet
|
|
23
|
+
/// meaningfully using that permit. This is useful for giving a more semantically accurate count
|
|
24
|
+
/// of used task slots, since we typically wait for a permit first before polling, but that slot
|
|
25
|
+
/// isn't used in the sense the user expects until we actually also get the corresponding task.
|
|
26
|
+
unused_claimants: Arc<AtomicUsize>,
|
|
16
27
|
metrics_ctx: MetricsContext,
|
|
17
28
|
record_fn: fn(&MetricsContext, usize),
|
|
18
29
|
}
|
|
@@ -25,6 +36,7 @@ impl MeteredSemaphore {
|
|
|
25
36
|
) -> Self {
|
|
26
37
|
Self {
|
|
27
38
|
sem: Arc::new(Semaphore::new(inital_permits)),
|
|
39
|
+
unused_claimants: Arc::new(AtomicUsize::new(0)),
|
|
28
40
|
metrics_ctx,
|
|
29
41
|
record_fn,
|
|
30
42
|
}
|
|
@@ -36,42 +48,154 @@ impl MeteredSemaphore {
|
|
|
36
48
|
|
|
37
49
|
pub async fn acquire_owned(&self) -> Result<OwnedMeteredSemPermit, AcquireError> {
|
|
38
50
|
let res = self.sem.clone().acquire_owned().await?;
|
|
39
|
-
self.
|
|
40
|
-
Ok(OwnedMeteredSemPermit {
|
|
41
|
-
inner: res,
|
|
42
|
-
record_fn: self.record_drop_owned(),
|
|
43
|
-
})
|
|
51
|
+
Ok(self.build_owned(res))
|
|
44
52
|
}
|
|
45
53
|
|
|
46
54
|
pub fn try_acquire_owned(&self) -> Result<OwnedMeteredSemPermit, TryAcquireError> {
|
|
47
55
|
let res = self.sem.clone().try_acquire_owned()?;
|
|
56
|
+
Ok(self.build_owned(res))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
fn build_owned(&self, res: OwnedSemaphorePermit) -> OwnedMeteredSemPermit {
|
|
60
|
+
self.unused_claimants.fetch_add(1, Ordering::Release);
|
|
48
61
|
self.record();
|
|
49
|
-
|
|
62
|
+
OwnedMeteredSemPermit {
|
|
50
63
|
inner: res,
|
|
51
|
-
|
|
52
|
-
|
|
64
|
+
unused_claimants: Some(self.unused_claimants.clone()),
|
|
65
|
+
record_fn: self.record_owned(),
|
|
66
|
+
}
|
|
53
67
|
}
|
|
54
68
|
|
|
55
69
|
fn record(&self) {
|
|
56
|
-
(self.record_fn)(
|
|
70
|
+
(self.record_fn)(
|
|
71
|
+
&self.metrics_ctx,
|
|
72
|
+
self.sem.available_permits() + self.unused_claimants.load(Ordering::Acquire),
|
|
73
|
+
);
|
|
57
74
|
}
|
|
58
75
|
|
|
59
|
-
fn
|
|
76
|
+
fn record_owned(&self) -> Box<dyn Fn(bool) + Send + Sync> {
|
|
60
77
|
let rcf = self.record_fn;
|
|
61
78
|
let mets = self.metrics_ctx.clone();
|
|
62
79
|
let sem = self.sem.clone();
|
|
63
|
-
|
|
80
|
+
let uc = self.unused_claimants.clone();
|
|
81
|
+
// When being called from the drop impl, the semaphore permit isn't actually dropped yet,
|
|
82
|
+
// so account for that.
|
|
83
|
+
Box::new(move |add_one: bool| {
|
|
84
|
+
let extra = usize::from(add_one);
|
|
85
|
+
rcf(
|
|
86
|
+
&mets,
|
|
87
|
+
sem.available_permits() + uc.load(Ordering::Acquire) + extra,
|
|
88
|
+
)
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/// A version of [MeteredSemaphore] that can be closed and supports waiting for close to complete.
|
|
94
|
+
/// Once closed, no permits will be handed out.
|
|
95
|
+
/// Close completes when all permits have been returned.
|
|
96
|
+
pub(crate) struct ClosableMeteredSemaphore {
|
|
97
|
+
inner: Arc<MeteredSemaphore>,
|
|
98
|
+
outstanding_permits: AtomicUsize,
|
|
99
|
+
close_requested: AtomicBool,
|
|
100
|
+
close_complete_token: CancellationToken,
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
impl ClosableMeteredSemaphore {
|
|
104
|
+
pub fn new_arc(sem: Arc<MeteredSemaphore>) -> Arc<Self> {
|
|
105
|
+
Arc::new(Self {
|
|
106
|
+
inner: sem,
|
|
107
|
+
outstanding_permits: Default::default(),
|
|
108
|
+
close_requested: AtomicBool::new(false),
|
|
109
|
+
close_complete_token: CancellationToken::new(),
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
impl ClosableMeteredSemaphore {
|
|
115
|
+
#[cfg(test)]
|
|
116
|
+
pub fn available_permits(&self) -> usize {
|
|
117
|
+
self.inner.available_permits()
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/// Request to close the semaphore and prevent new permits from being acquired.
|
|
121
|
+
pub fn close(&self) {
|
|
122
|
+
self.close_requested.store(true, Ordering::Release);
|
|
123
|
+
if self.outstanding_permits.load(Ordering::Acquire) == 0 {
|
|
124
|
+
self.close_complete_token.cancel();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/// Returns after close has been requested and all outstanding permits have been returned.
|
|
129
|
+
pub async fn close_complete(&self) {
|
|
130
|
+
self.close_complete_token.cancelled().await;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/// Acquire a permit if one is available and close was not requested.
|
|
134
|
+
pub fn try_acquire_owned(
|
|
135
|
+
self: &Arc<Self>,
|
|
136
|
+
) -> Result<TrackedOwnedMeteredSemPermit, TryAcquireError> {
|
|
137
|
+
if self.close_requested.load(Ordering::Acquire) {
|
|
138
|
+
return Err(TryAcquireError::Closed);
|
|
139
|
+
}
|
|
140
|
+
self.outstanding_permits.fetch_add(1, Ordering::Release);
|
|
141
|
+
let res = self.inner.try_acquire_owned();
|
|
142
|
+
if res.is_err() {
|
|
143
|
+
self.outstanding_permits.fetch_sub(1, Ordering::Release);
|
|
144
|
+
}
|
|
145
|
+
res.map(|permit| TrackedOwnedMeteredSemPermit {
|
|
146
|
+
inner: Some(permit),
|
|
147
|
+
on_drop: self.on_permit_dropped(),
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
fn on_permit_dropped(self: &Arc<Self>) -> Box<dyn Fn() + Send + Sync> {
|
|
152
|
+
let sem = self.clone();
|
|
153
|
+
Box::new(move || {
|
|
154
|
+
sem.outstanding_permits.fetch_sub(1, Ordering::Release);
|
|
155
|
+
if sem.close_requested.load(Ordering::Acquire)
|
|
156
|
+
&& sem.outstanding_permits.load(Ordering::Acquire) == 0
|
|
157
|
+
{
|
|
158
|
+
sem.close_complete_token.cancel();
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/// Tracks an OwnedMeteredSemPermit and calls on_drop when dropped.
|
|
165
|
+
#[derive(DebugCustom)]
|
|
166
|
+
#[debug(fmt = "Tracked({inner:?})")]
|
|
167
|
+
pub(crate) struct TrackedOwnedMeteredSemPermit {
|
|
168
|
+
inner: Option<OwnedMeteredSemPermit>,
|
|
169
|
+
on_drop: Box<dyn Fn() + Send + Sync>,
|
|
170
|
+
}
|
|
171
|
+
impl From<TrackedOwnedMeteredSemPermit> for OwnedMeteredSemPermit {
|
|
172
|
+
fn from(mut value: TrackedOwnedMeteredSemPermit) -> Self {
|
|
173
|
+
value
|
|
174
|
+
.inner
|
|
175
|
+
.take()
|
|
176
|
+
.expect("Inner permit should be available")
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
impl Drop for TrackedOwnedMeteredSemPermit {
|
|
180
|
+
fn drop(&mut self) {
|
|
181
|
+
(self.on_drop)();
|
|
64
182
|
}
|
|
65
183
|
}
|
|
66
184
|
|
|
67
185
|
/// Wraps an [OwnedSemaphorePermit] to update metrics when it's dropped
|
|
68
186
|
pub(crate) struct OwnedMeteredSemPermit {
|
|
69
187
|
inner: OwnedSemaphorePermit,
|
|
70
|
-
|
|
188
|
+
/// See [MeteredSemaphore::unused_claimants]. If present when dropping, used to decrement the
|
|
189
|
+
/// count.
|
|
190
|
+
unused_claimants: Option<Arc<AtomicUsize>>,
|
|
191
|
+
record_fn: Box<dyn Fn(bool) + Send + Sync>,
|
|
71
192
|
}
|
|
72
193
|
impl Drop for OwnedMeteredSemPermit {
|
|
73
194
|
fn drop(&mut self) {
|
|
74
|
-
(self.
|
|
195
|
+
if let Some(uc) = self.unused_claimants.take() {
|
|
196
|
+
uc.fetch_sub(1, Ordering::Release);
|
|
197
|
+
}
|
|
198
|
+
(self.record_fn)(true)
|
|
75
199
|
}
|
|
76
200
|
}
|
|
77
201
|
impl Debug for OwnedMeteredSemPermit {
|
|
@@ -79,6 +203,32 @@ impl Debug for OwnedMeteredSemPermit {
|
|
|
79
203
|
self.inner.fmt(f)
|
|
80
204
|
}
|
|
81
205
|
}
|
|
206
|
+
impl OwnedMeteredSemPermit {
|
|
207
|
+
/// Should be called once this permit is actually being "used" for the work it was meant to
|
|
208
|
+
/// permit.
|
|
209
|
+
pub(crate) fn into_used(mut self) -> UsedMeteredSemPermit {
|
|
210
|
+
if let Some(uc) = self.unused_claimants.take() {
|
|
211
|
+
uc.fetch_sub(1, Ordering::Release);
|
|
212
|
+
(self.record_fn)(false)
|
|
213
|
+
}
|
|
214
|
+
UsedMeteredSemPermit(self)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
#[derive(Debug)]
|
|
219
|
+
pub(crate) struct UsedMeteredSemPermit(OwnedMeteredSemPermit);
|
|
220
|
+
impl UsedMeteredSemPermit {
|
|
221
|
+
#[cfg(feature = "save_wf_inputs")]
|
|
222
|
+
pub(crate) fn fake_deserialized() -> Self {
|
|
223
|
+
let sem = Arc::new(Semaphore::new(1));
|
|
224
|
+
let inner = sem.try_acquire_owned().unwrap();
|
|
225
|
+
Self(OwnedMeteredSemPermit {
|
|
226
|
+
inner,
|
|
227
|
+
unused_claimants: None,
|
|
228
|
+
record_fn: Box::new(|_| {}),
|
|
229
|
+
})
|
|
230
|
+
}
|
|
231
|
+
}
|
|
82
232
|
|
|
83
233
|
/// From the input stream, create a new stream which only pulls from the input stream when allowed.
|
|
84
234
|
/// When allowed is determined by the passed in `proceeder` emitting an item. The input stream is
|
|
@@ -163,4 +313,43 @@ mod tests {
|
|
|
163
313
|
allow_tx.send(()).unwrap();
|
|
164
314
|
assert_eq!(when_allowed.poll_next_unpin(&mut cx), Poll::Ready(None));
|
|
165
315
|
}
|
|
316
|
+
|
|
317
|
+
#[tokio::test]
|
|
318
|
+
async fn closable_semaphore_permit_drop_returns_permit() {
|
|
319
|
+
let inner = MeteredSemaphore::new(2, MetricsContext::no_op(), |_, _| {});
|
|
320
|
+
let sem = ClosableMeteredSemaphore::new_arc(Arc::new(inner));
|
|
321
|
+
let perm = sem.try_acquire_owned().unwrap();
|
|
322
|
+
let permits = sem.outstanding_permits.load(Ordering::Acquire);
|
|
323
|
+
assert_eq!(permits, 1);
|
|
324
|
+
drop(perm);
|
|
325
|
+
let permits = sem.outstanding_permits.load(Ordering::Acquire);
|
|
326
|
+
assert_eq!(permits, 0);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
#[tokio::test]
|
|
330
|
+
async fn closable_semaphore_permit_drop_after_close_resolves_close_complete() {
|
|
331
|
+
let inner = MeteredSemaphore::new(2, MetricsContext::no_op(), |_, _| {});
|
|
332
|
+
let sem = ClosableMeteredSemaphore::new_arc(Arc::new(inner));
|
|
333
|
+
let perm = sem.try_acquire_owned().unwrap();
|
|
334
|
+
sem.close();
|
|
335
|
+
drop(perm);
|
|
336
|
+
sem.close_complete().await;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
#[tokio::test]
|
|
340
|
+
async fn closable_semaphore_close_complete_ready_if_unused() {
|
|
341
|
+
let inner = MeteredSemaphore::new(2, MetricsContext::no_op(), |_, _| {});
|
|
342
|
+
let sem = ClosableMeteredSemaphore::new_arc(Arc::new(inner));
|
|
343
|
+
sem.close();
|
|
344
|
+
sem.close_complete().await;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
#[tokio::test]
|
|
348
|
+
async fn closable_semaphore_does_not_hand_out_permits_after_closed() {
|
|
349
|
+
let inner = MeteredSemaphore::new(2, MetricsContext::no_op(), |_, _| {});
|
|
350
|
+
let sem = ClosableMeteredSemaphore::new_arc(Arc::new(inner));
|
|
351
|
+
sem.close();
|
|
352
|
+
let perm = sem.try_acquire_owned().unwrap_err();
|
|
353
|
+
assert_matches!(perm, TryAcquireError::Closed);
|
|
354
|
+
}
|
|
166
355
|
}
|
|
@@ -4,7 +4,7 @@ use crate::{
|
|
|
4
4
|
build_fake_worker, build_mock_pollers, canned_histories, gen_assert_and_reply,
|
|
5
5
|
mock_manual_poller, mock_poller, mock_poller_from_resps, mock_worker, poll_and_reply,
|
|
6
6
|
single_hist_mock_sg, test_worker_cfg, MockPollCfg, MockWorkerInputs, MocksHolder,
|
|
7
|
-
ResponseType, WorkflowCachingPolicy, TEST_Q,
|
|
7
|
+
QueueResponse, ResponseType, WorkerExt, WorkflowCachingPolicy, TEST_Q,
|
|
8
8
|
},
|
|
9
9
|
worker::client::mocks::{mock_manual_workflow_client, mock_workflow_client},
|
|
10
10
|
ActivityHeartbeat, Worker, WorkerConfigBuilder,
|
|
@@ -14,6 +14,7 @@ use itertools::Itertools;
|
|
|
14
14
|
use std::{
|
|
15
15
|
cell::RefCell,
|
|
16
16
|
collections::{hash_map::Entry, HashMap, VecDeque},
|
|
17
|
+
future,
|
|
17
18
|
rc::Rc,
|
|
18
19
|
sync::{
|
|
19
20
|
atomic::{AtomicUsize, Ordering},
|
|
@@ -23,7 +24,10 @@ use std::{
|
|
|
23
24
|
};
|
|
24
25
|
use temporal_client::WorkflowOptions;
|
|
25
26
|
use temporal_sdk::{ActivityOptions, WfContext};
|
|
26
|
-
use temporal_sdk_core_api::{
|
|
27
|
+
use temporal_sdk_core_api::{
|
|
28
|
+
errors::{CompleteActivityError, PollActivityError},
|
|
29
|
+
Worker as WorkerTrait,
|
|
30
|
+
};
|
|
27
31
|
use temporal_sdk_core_protos::{
|
|
28
32
|
coresdk::{
|
|
29
33
|
activity_result::{
|
|
@@ -42,6 +46,9 @@ use temporal_sdk_core_protos::{
|
|
|
42
46
|
temporal::api::{
|
|
43
47
|
command::v1::{command::Attributes, ScheduleActivityTaskCommandAttributes},
|
|
44
48
|
enums::v1::EventType,
|
|
49
|
+
history::v1::{
|
|
50
|
+
history_event::Attributes as EventAttributes, ActivityTaskScheduledEventAttributes,
|
|
51
|
+
},
|
|
45
52
|
workflowservice::v1::{
|
|
46
53
|
PollActivityTaskQueueResponse, RecordActivityTaskHeartbeatResponse,
|
|
47
54
|
RespondActivityTaskCanceledResponse, RespondActivityTaskCompletedResponse,
|
|
@@ -52,6 +59,7 @@ use temporal_sdk_core_protos::{
|
|
|
52
59
|
};
|
|
53
60
|
use temporal_sdk_core_test_utils::{fanout_tasks, start_timer_cmd, TestWorker};
|
|
54
61
|
use tokio::{sync::Barrier, time::sleep};
|
|
62
|
+
use tokio_util::sync::CancellationToken;
|
|
55
63
|
|
|
56
64
|
#[tokio::test]
|
|
57
65
|
async fn max_activities_respected() {
|
|
@@ -121,7 +129,7 @@ async fn activity_not_found_returns_ok() {
|
|
|
121
129
|
})
|
|
122
130
|
.await
|
|
123
131
|
.unwrap();
|
|
124
|
-
core.
|
|
132
|
+
core.drain_activity_poller_and_shutdown().await;
|
|
125
133
|
}
|
|
126
134
|
|
|
127
135
|
#[tokio::test]
|
|
@@ -217,12 +225,14 @@ async fn heartbeats_report_cancels_only_once() {
|
|
|
217
225
|
})
|
|
218
226
|
.await
|
|
219
227
|
.unwrap();
|
|
220
|
-
core.
|
|
228
|
+
core.drain_activity_poller_and_shutdown().await;
|
|
221
229
|
}
|
|
222
230
|
|
|
223
231
|
#[tokio::test]
|
|
224
232
|
async fn activity_cancel_interrupts_poll() {
|
|
225
233
|
let mut mock_poller = mock_manual_poller();
|
|
234
|
+
let shutdown_token = CancellationToken::new();
|
|
235
|
+
let shutdown_token_clone = shutdown_token.clone();
|
|
226
236
|
let mut poll_resps = VecDeque::from(vec![
|
|
227
237
|
async {
|
|
228
238
|
Some(Ok(PollActivityTaskQueueResponse {
|
|
@@ -237,10 +247,15 @@ async fn activity_cancel_interrupts_poll() {
|
|
|
237
247
|
Some(Ok(Default::default()))
|
|
238
248
|
}
|
|
239
249
|
.boxed(),
|
|
250
|
+
async move {
|
|
251
|
+
shutdown_token.cancelled().await;
|
|
252
|
+
None
|
|
253
|
+
}
|
|
254
|
+
.boxed(),
|
|
240
255
|
]);
|
|
241
256
|
mock_poller
|
|
242
257
|
.expect_poll()
|
|
243
|
-
.times(
|
|
258
|
+
.times(3)
|
|
244
259
|
.returning(move || poll_resps.pop_front().unwrap());
|
|
245
260
|
|
|
246
261
|
let mut mock_client = mock_manual_workflow_client();
|
|
@@ -289,11 +304,12 @@ async fn activity_cancel_interrupts_poll() {
|
|
|
289
304
|
}
|
|
290
305
|
).await.unwrap();
|
|
291
306
|
last_finisher.store(2, Ordering::SeqCst);
|
|
307
|
+
shutdown_token_clone.cancel();
|
|
292
308
|
}
|
|
293
309
|
};
|
|
294
310
|
// So that we know we blocked
|
|
295
311
|
assert_eq!(last_finisher.load(Ordering::Acquire), 2);
|
|
296
|
-
core.
|
|
312
|
+
core.drain_activity_poller_and_shutdown().await;
|
|
297
313
|
}
|
|
298
314
|
|
|
299
315
|
#[tokio::test]
|
|
@@ -342,13 +358,10 @@ async fn many_concurrent_heartbeat_cancels() {
|
|
|
342
358
|
})
|
|
343
359
|
.collect::<Vec<_>>(),
|
|
344
360
|
);
|
|
345
|
-
// Because the mock is so fast, it's possible it can return before the cancel channel in
|
|
346
|
-
// the activity task poll selector. So, the final poll when there are no more tasks must
|
|
347
|
-
// take a while.
|
|
348
361
|
poll_resps.push_back(
|
|
349
362
|
async {
|
|
350
|
-
|
|
351
|
-
unreachable!(
|
|
363
|
+
future::pending::<()>().await;
|
|
364
|
+
unreachable!()
|
|
352
365
|
}
|
|
353
366
|
.boxed(),
|
|
354
367
|
);
|
|
@@ -431,7 +444,7 @@ async fn many_concurrent_heartbeat_cancels() {
|
|
|
431
444
|
})
|
|
432
445
|
.await;
|
|
433
446
|
|
|
434
|
-
worker.
|
|
447
|
+
worker.drain_activity_poller_and_shutdown().await;
|
|
435
448
|
}
|
|
436
449
|
|
|
437
450
|
#[tokio::test]
|
|
@@ -483,7 +496,7 @@ async fn activity_timeout_no_double_resolve() {
|
|
|
483
496
|
)
|
|
484
497
|
.await;
|
|
485
498
|
|
|
486
|
-
core.
|
|
499
|
+
core.drain_pollers_and_shutdown().await;
|
|
487
500
|
}
|
|
488
501
|
|
|
489
502
|
#[tokio::test]
|
|
@@ -529,7 +542,7 @@ async fn can_heartbeat_acts_during_shutdown() {
|
|
|
529
542
|
})
|
|
530
543
|
.await
|
|
531
544
|
.unwrap();
|
|
532
|
-
|
|
545
|
+
core.drain_activity_poller_and_shutdown().await;
|
|
533
546
|
}
|
|
534
547
|
|
|
535
548
|
/// Verifies that if a user has tried to record a heartbeat and then immediately after failed the
|
|
@@ -580,7 +593,7 @@ async fn complete_act_with_fail_flushes_heartbeat() {
|
|
|
580
593
|
})
|
|
581
594
|
.await
|
|
582
595
|
.unwrap();
|
|
583
|
-
core.
|
|
596
|
+
core.drain_activity_poller_and_shutdown().await;
|
|
584
597
|
|
|
585
598
|
// Verify the last seen call to record a heartbeat had the last detail payload
|
|
586
599
|
let last_seen_payload = &last_seen_payload.take().unwrap().payloads[0];
|
|
@@ -686,7 +699,7 @@ async fn no_eager_activities_requested_when_worker_options_disable_remote_activi
|
|
|
686
699
|
.await
|
|
687
700
|
.unwrap();
|
|
688
701
|
|
|
689
|
-
core.
|
|
702
|
+
core.drain_pollers_and_shutdown().await;
|
|
690
703
|
|
|
691
704
|
assert_eq!(num_eager_requested.load(Ordering::Relaxed), 0);
|
|
692
705
|
}
|
|
@@ -703,7 +716,7 @@ async fn activity_tasks_from_completion_are_delivered() {
|
|
|
703
716
|
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
704
717
|
t.add_full_wf_task();
|
|
705
718
|
let act_same_queue_scheduled_ids = (1..4)
|
|
706
|
-
.map(|i| t.add_activity_task_scheduled(format!("act_id_{}_same_queue"
|
|
719
|
+
.map(|i| t.add_activity_task_scheduled(format!("act_id_{i}_same_queue")))
|
|
707
720
|
.collect_vec();
|
|
708
721
|
t.add_activity_task_scheduled("act_id_same_queue_not_eager");
|
|
709
722
|
t.add_activity_task_scheduled("act_id_different_queue");
|
|
@@ -742,7 +755,7 @@ async fn activity_tasks_from_completion_are_delivered() {
|
|
|
742
755
|
activity_tasks: (1..4)
|
|
743
756
|
.map(|i| PollActivityTaskQueueResponse {
|
|
744
757
|
task_token: vec![i],
|
|
745
|
-
activity_id: format!("act_id_{}_same_queue"
|
|
758
|
+
activity_id: format!("act_id_{i}_same_queue"),
|
|
746
759
|
..Default::default()
|
|
747
760
|
})
|
|
748
761
|
.collect_vec(),
|
|
@@ -753,11 +766,8 @@ async fn activity_tasks_from_completion_are_delivered() {
|
|
|
753
766
|
.times(3)
|
|
754
767
|
.returning(|_, _| Ok(RespondActivityTaskCompletedResponse::default()));
|
|
755
768
|
let mut mock = single_hist_mock_sg(wfid, t, [1], mock, true);
|
|
756
|
-
let
|
|
757
|
-
|
|
758
|
-
.expect_poll()
|
|
759
|
-
.returning(|| futures::future::pending().boxed());
|
|
760
|
-
mock.set_act_poller(Box::new(mock_poller));
|
|
769
|
+
let act_tasks: Vec<QueueResponse<PollActivityTaskQueueResponse>> = vec![];
|
|
770
|
+
mock.set_act_poller(mock_poller_from_resps(act_tasks));
|
|
761
771
|
mock.worker_cfg(|wc| wc.max_cached_workflows = 2);
|
|
762
772
|
let core = mock_worker(mock);
|
|
763
773
|
|
|
@@ -767,7 +777,7 @@ async fn activity_tasks_from_completion_are_delivered() {
|
|
|
767
777
|
.map(|seq| {
|
|
768
778
|
ScheduleActivity {
|
|
769
779
|
seq,
|
|
770
|
-
activity_id: format!("act_id_{}_same_queue"
|
|
780
|
+
activity_id: format!("act_id_{seq}_same_queue"),
|
|
771
781
|
task_queue: TEST_Q.to_string(),
|
|
772
782
|
cancellation_type: ActivityCancellationType::TryCancel as i32,
|
|
773
783
|
..Default::default()
|
|
@@ -816,7 +826,7 @@ async fn activity_tasks_from_completion_are_delivered() {
|
|
|
816
826
|
.unwrap();
|
|
817
827
|
}
|
|
818
828
|
|
|
819
|
-
core.
|
|
829
|
+
core.drain_activity_poller_and_shutdown().await;
|
|
820
830
|
|
|
821
831
|
// Verify only a single eager activity was scheduled (the one on our worker's task queue)
|
|
822
832
|
assert_eq!(num_eager_requested.load(Ordering::Relaxed), 3);
|
|
@@ -828,11 +838,23 @@ async fn activity_tasks_from_completion_reserve_slots() {
|
|
|
828
838
|
let mut t = TestHistoryBuilder::default();
|
|
829
839
|
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
830
840
|
t.add_full_wf_task();
|
|
831
|
-
let schedid = t.
|
|
841
|
+
let schedid = t.add(EventAttributes::ActivityTaskScheduledEventAttributes(
|
|
842
|
+
ActivityTaskScheduledEventAttributes {
|
|
843
|
+
activity_id: "1".to_string(),
|
|
844
|
+
activity_type: Some("act1".into()),
|
|
845
|
+
..Default::default()
|
|
846
|
+
},
|
|
847
|
+
));
|
|
832
848
|
let startid = t.add_activity_task_started(schedid);
|
|
833
849
|
t.add_activity_task_completed(schedid, startid, b"hi".into());
|
|
834
850
|
t.add_full_wf_task();
|
|
835
|
-
let schedid = t.
|
|
851
|
+
let schedid = t.add(EventAttributes::ActivityTaskScheduledEventAttributes(
|
|
852
|
+
ActivityTaskScheduledEventAttributes {
|
|
853
|
+
activity_id: "2".to_string(),
|
|
854
|
+
activity_type: Some("act2".into()),
|
|
855
|
+
..Default::default()
|
|
856
|
+
},
|
|
857
|
+
));
|
|
836
858
|
let startid = t.add_activity_task_started(schedid);
|
|
837
859
|
t.add_activity_task_completed(schedid, startid, b"hi".into());
|
|
838
860
|
t.add_full_wf_task();
|
|
@@ -902,19 +924,25 @@ async fn activity_tasks_from_completion_reserve_slots() {
|
|
|
902
924
|
// First poll for activities twice, occupying both slots
|
|
903
925
|
let at1 = core.poll_activity_task().await.unwrap();
|
|
904
926
|
let at2 = core.poll_activity_task().await.unwrap();
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
927
|
+
let workflow_complete_token = CancellationToken::new();
|
|
928
|
+
let workflow_complete_token_clone = workflow_complete_token.clone();
|
|
929
|
+
|
|
930
|
+
worker.register_wf(DEFAULT_WORKFLOW_TYPE, move |ctx: WfContext| {
|
|
931
|
+
let complete_token = workflow_complete_token.clone();
|
|
932
|
+
async move {
|
|
933
|
+
ctx.activity(ActivityOptions {
|
|
934
|
+
activity_type: "act1".to_string(),
|
|
935
|
+
..Default::default()
|
|
936
|
+
})
|
|
937
|
+
.await;
|
|
938
|
+
ctx.activity(ActivityOptions {
|
|
939
|
+
activity_type: "act2".to_string(),
|
|
940
|
+
..Default::default()
|
|
941
|
+
})
|
|
942
|
+
.await;
|
|
943
|
+
complete_token.cancel();
|
|
944
|
+
Ok(().into())
|
|
945
|
+
}
|
|
918
946
|
});
|
|
919
947
|
|
|
920
948
|
worker
|
|
@@ -941,6 +969,13 @@ async fn activity_tasks_from_completion_reserve_slots() {
|
|
|
941
969
|
.await
|
|
942
970
|
.unwrap();
|
|
943
971
|
barr.wait().await;
|
|
972
|
+
// Wait for workflow to complete in order for all eager activities to be requested before shutting down.
|
|
973
|
+
// After shutdown, no eager activities slots can be allocated.
|
|
974
|
+
workflow_complete_token_clone.cancelled().await;
|
|
975
|
+
core.initiate_shutdown();
|
|
976
|
+
// Even though this test requests eager activity tasks, none are returned in poll responses.
|
|
977
|
+
let err = core.poll_activity_task().await.unwrap_err();
|
|
978
|
+
assert_matches!(err, PollActivityError::ShutDown);
|
|
944
979
|
};
|
|
945
980
|
// This wf poll should *not* set the flag that it wants tasks back since both slots are
|
|
946
981
|
// occupied
|
|
@@ -974,7 +1009,7 @@ async fn retryable_net_error_exhaustion_is_nonfatal() {
|
|
|
974
1009
|
})
|
|
975
1010
|
.await
|
|
976
1011
|
.unwrap();
|
|
977
|
-
core.
|
|
1012
|
+
core.drain_activity_poller_and_shutdown().await;
|
|
978
1013
|
}
|
|
979
1014
|
|
|
980
1015
|
#[tokio::test]
|