@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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
use crate::{
|
|
2
|
+
abstractions::take_cell::TakeCell,
|
|
2
3
|
worker::{activities::PendingActivityCancel, client::WorkerClient},
|
|
3
4
|
TaskToken,
|
|
4
5
|
};
|
|
@@ -17,7 +18,7 @@ use temporal_sdk_core_protos::{
|
|
|
17
18
|
use tokio::{
|
|
18
19
|
sync::{
|
|
19
20
|
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
|
|
20
|
-
|
|
21
|
+
Notify,
|
|
21
22
|
},
|
|
22
23
|
task::JoinHandle,
|
|
23
24
|
};
|
|
@@ -26,12 +27,9 @@ use tokio_util::sync::CancellationToken;
|
|
|
26
27
|
/// Used to supply new heartbeat events to the activity heartbeat manager, or to send a shutdown
|
|
27
28
|
/// request.
|
|
28
29
|
pub(crate) struct ActivityHeartbeatManager {
|
|
29
|
-
/// Cancellations that have been received when heartbeating are queued here and can be consumed
|
|
30
|
-
/// by [fetch_cancellations]
|
|
31
|
-
incoming_cancels: Mutex<UnboundedReceiver<PendingActivityCancel>>,
|
|
32
30
|
shutdown_token: CancellationToken,
|
|
33
31
|
/// Used during `shutdown` to await until all inflight requests are sent.
|
|
34
|
-
join_handle:
|
|
32
|
+
join_handle: TakeCell<JoinHandle<()>>,
|
|
35
33
|
heartbeat_tx: UnboundedSender<HeartbeatAction>,
|
|
36
34
|
}
|
|
37
35
|
|
|
@@ -74,15 +72,115 @@ pub enum ActivityHeartbeatError {
|
|
|
74
72
|
/// to heartbeat.
|
|
75
73
|
#[error("Unable to parse activity heartbeat timeout.")]
|
|
76
74
|
InvalidHeartbeatTimeout,
|
|
77
|
-
/// Core is shutting down and thus new heartbeats are not accepted
|
|
78
|
-
#[error("New heartbeat requests are not accepted while shutting down")]
|
|
79
|
-
ShuttingDown,
|
|
80
75
|
}
|
|
81
76
|
|
|
82
77
|
/// Manages activity heartbeating for a worker. Allows sending new heartbeats or requesting and
|
|
83
78
|
/// awaiting for the shutdown. When shutdown is requested, signal gets sent to all processors, which
|
|
84
79
|
/// allows them to complete gracefully.
|
|
85
80
|
impl ActivityHeartbeatManager {
|
|
81
|
+
/// Creates a new instance of an activity heartbeat manager and returns a handle to the user,
|
|
82
|
+
/// which allows to send new heartbeats and initiate the shutdown.
|
|
83
|
+
/// Returns the manager and a channel that buffers cancellation notifications to be sent to Lang.
|
|
84
|
+
pub(super) fn new(
|
|
85
|
+
client: Arc<dyn WorkerClient>,
|
|
86
|
+
cancels_tx: UnboundedSender<PendingActivityCancel>,
|
|
87
|
+
) -> Self {
|
|
88
|
+
let (heartbeat_stream_state, heartbeat_tx_source, shutdown_token) =
|
|
89
|
+
HeartbeatStreamState::new();
|
|
90
|
+
let heartbeat_tx = heartbeat_tx_source.clone();
|
|
91
|
+
|
|
92
|
+
let join_handle = tokio::spawn(
|
|
93
|
+
// The stream of incoming heartbeats uses unfold to carry state across each item in the
|
|
94
|
+
// stream. The closure checks if, for any given activity, we should heartbeat or not
|
|
95
|
+
// depending on its delay and when we last issued a heartbeat for it.
|
|
96
|
+
futures::stream::unfold(heartbeat_stream_state, move |mut hb_states| {
|
|
97
|
+
async move {
|
|
98
|
+
let hb = tokio::select! {
|
|
99
|
+
biased;
|
|
100
|
+
|
|
101
|
+
_ = hb_states.cancellation_token.cancelled() => {
|
|
102
|
+
return None
|
|
103
|
+
}
|
|
104
|
+
hb = hb_states.incoming_hbs.recv() => match hb {
|
|
105
|
+
None => return None,
|
|
106
|
+
Some(hb) => hb,
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
Some((
|
|
111
|
+
match hb {
|
|
112
|
+
HeartbeatAction::SendHeartbeat(hb) => hb_states.record(hb),
|
|
113
|
+
HeartbeatAction::CompleteReport(tt) => hb_states.handle_report_completed(tt),
|
|
114
|
+
HeartbeatAction::CompleteThrottle(tt) => hb_states.handle_throttle_completed(tt),
|
|
115
|
+
HeartbeatAction::Evict{ token, on_complete } => hb_states.evict(token, on_complete),
|
|
116
|
+
},
|
|
117
|
+
hb_states,
|
|
118
|
+
))
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
// Filters out `None`s
|
|
122
|
+
.filter_map(|opt| async { opt })
|
|
123
|
+
.for_each_concurrent(None, move |action| {
|
|
124
|
+
let heartbeat_tx = heartbeat_tx_source.clone();
|
|
125
|
+
let sg = client.clone();
|
|
126
|
+
let cancels_tx = cancels_tx.clone();
|
|
127
|
+
async move {
|
|
128
|
+
match action {
|
|
129
|
+
HeartbeatExecutorAction::Sleep(tt, duration, cancellation_token) => {
|
|
130
|
+
tokio::select! {
|
|
131
|
+
_ = cancellation_token.cancelled() => (),
|
|
132
|
+
_ = tokio::time::sleep(duration) => {
|
|
133
|
+
let _ = heartbeat_tx.send(HeartbeatAction::CompleteThrottle(tt));
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
HeartbeatExecutorAction::Report { task_token: tt, details } => {
|
|
138
|
+
match sg
|
|
139
|
+
.record_activity_heartbeat(tt.clone(), details.into_payloads())
|
|
140
|
+
.await
|
|
141
|
+
{
|
|
142
|
+
Ok(RecordActivityTaskHeartbeatResponse { cancel_requested }) => {
|
|
143
|
+
if cancel_requested {
|
|
144
|
+
cancels_tx
|
|
145
|
+
.send(PendingActivityCancel::new(
|
|
146
|
+
tt.clone(),
|
|
147
|
+
ActivityCancelReason::Cancelled,
|
|
148
|
+
))
|
|
149
|
+
.expect(
|
|
150
|
+
"Receive half of heartbeat cancels not blocked",
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Send cancels for any activity that learns its workflow already
|
|
155
|
+
// finished (which is one thing not found implies - other reasons
|
|
156
|
+
// would seem equally valid).
|
|
157
|
+
Err(s) if s.code() == tonic::Code::NotFound => {
|
|
158
|
+
debug!(task_token = %tt,
|
|
159
|
+
"Activity not found when recording heartbeat");
|
|
160
|
+
cancels_tx
|
|
161
|
+
.send(PendingActivityCancel::new(
|
|
162
|
+
tt.clone(),
|
|
163
|
+
ActivityCancelReason::NotFound,
|
|
164
|
+
))
|
|
165
|
+
.expect("Receive half of heartbeat cancels not blocked");
|
|
166
|
+
}
|
|
167
|
+
Err(e) => {
|
|
168
|
+
warn!("Error when recording heartbeat: {:?}", e);
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
let _ = heartbeat_tx.send(HeartbeatAction::CompleteReport(tt));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}),
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
Self {
|
|
179
|
+
join_handle: TakeCell::new(join_handle),
|
|
180
|
+
shutdown_token,
|
|
181
|
+
heartbeat_tx,
|
|
182
|
+
}
|
|
183
|
+
}
|
|
86
184
|
/// Records a new heartbeat, the first call will result in an immediate call to the server,
|
|
87
185
|
/// while rapid successive calls would accumulate for up to `delay` and then latest heartbeat
|
|
88
186
|
/// details will be sent to the server.
|
|
@@ -95,9 +193,6 @@ impl ActivityHeartbeatManager {
|
|
|
95
193
|
hb: ActivityHeartbeat,
|
|
96
194
|
throttle_interval: Duration,
|
|
97
195
|
) -> Result<(), ActivityHeartbeatError> {
|
|
98
|
-
if self.shutdown_token.is_cancelled() {
|
|
99
|
-
return Err(ActivityHeartbeatError::ShuttingDown);
|
|
100
|
-
}
|
|
101
196
|
self.heartbeat_tx
|
|
102
197
|
.send(HeartbeatAction::SendHeartbeat(ValidActivityHeartbeat {
|
|
103
198
|
task_token: TaskToken(hb.task_token),
|
|
@@ -121,19 +216,11 @@ impl ActivityHeartbeatManager {
|
|
|
121
216
|
completed.notified().await;
|
|
122
217
|
}
|
|
123
218
|
|
|
124
|
-
/// Returns a future that resolves any time there is a new activity cancel that must be
|
|
125
|
-
/// dispatched to lang
|
|
126
|
-
pub(super) async fn next_pending_cancel(&self) -> Option<PendingActivityCancel> {
|
|
127
|
-
self.incoming_cancels.lock().await.recv().await
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// TODO: Can own self now!
|
|
131
219
|
/// Initiates shutdown procedure by stopping lifecycle loop and awaiting for all in-flight
|
|
132
220
|
/// heartbeat requests to be flushed to the server.
|
|
133
221
|
pub(super) async fn shutdown(&self) {
|
|
134
222
|
self.shutdown_token.cancel();
|
|
135
|
-
let
|
|
136
|
-
if let Some(h) = handle.take() {
|
|
223
|
+
if let Some(h) = self.join_handle.take_once() {
|
|
137
224
|
let handle_r = h.await;
|
|
138
225
|
if let Err(e) = handle_r {
|
|
139
226
|
if !e.is_cancelled() {
|
|
@@ -301,110 +388,6 @@ impl HeartbeatStreamState {
|
|
|
301
388
|
}
|
|
302
389
|
}
|
|
303
390
|
|
|
304
|
-
impl ActivityHeartbeatManager {
|
|
305
|
-
/// Creates a new instance of an activity heartbeat manager and returns a handle to the user,
|
|
306
|
-
/// which allows to send new heartbeats and initiate the shutdown.
|
|
307
|
-
pub fn new(client: Arc<dyn WorkerClient>) -> Self {
|
|
308
|
-
let (heartbeat_stream_state, heartbeat_tx_source, shutdown_token) =
|
|
309
|
-
HeartbeatStreamState::new();
|
|
310
|
-
let (cancels_tx, cancels_rx) = unbounded_channel();
|
|
311
|
-
let heartbeat_tx = heartbeat_tx_source.clone();
|
|
312
|
-
|
|
313
|
-
let join_handle = tokio::spawn(
|
|
314
|
-
// The stream of incoming heartbeats uses unfold to carry state across each item in the
|
|
315
|
-
// stream. The closure checks if, for any given activity, we should heartbeat or not
|
|
316
|
-
// depending on its delay and when we last issued a heartbeat for it.
|
|
317
|
-
futures::stream::unfold(heartbeat_stream_state, move |mut hb_states| {
|
|
318
|
-
async move {
|
|
319
|
-
let hb = tokio::select! {
|
|
320
|
-
biased;
|
|
321
|
-
|
|
322
|
-
_ = hb_states.cancellation_token.cancelled() => {
|
|
323
|
-
return None
|
|
324
|
-
}
|
|
325
|
-
hb = hb_states.incoming_hbs.recv() => match hb {
|
|
326
|
-
None => return None,
|
|
327
|
-
Some(hb) => hb,
|
|
328
|
-
}
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
Some((
|
|
332
|
-
match hb {
|
|
333
|
-
HeartbeatAction::SendHeartbeat(hb) => hb_states.record(hb),
|
|
334
|
-
HeartbeatAction::CompleteReport(tt) => hb_states.handle_report_completed(tt),
|
|
335
|
-
HeartbeatAction::CompleteThrottle(tt) => hb_states.handle_throttle_completed(tt),
|
|
336
|
-
HeartbeatAction::Evict{ token, on_complete } => hb_states.evict(token, on_complete),
|
|
337
|
-
},
|
|
338
|
-
hb_states,
|
|
339
|
-
))
|
|
340
|
-
}
|
|
341
|
-
})
|
|
342
|
-
// Filters out `None`s
|
|
343
|
-
.filter_map(|opt| async { opt })
|
|
344
|
-
.for_each_concurrent(None, move |action| {
|
|
345
|
-
let heartbeat_tx = heartbeat_tx_source.clone();
|
|
346
|
-
let sg = client.clone();
|
|
347
|
-
let cancels_tx = cancels_tx.clone();
|
|
348
|
-
async move {
|
|
349
|
-
match action {
|
|
350
|
-
HeartbeatExecutorAction::Sleep(tt, duration, cancellation_token) => {
|
|
351
|
-
tokio::select! {
|
|
352
|
-
_ = cancellation_token.cancelled() => (),
|
|
353
|
-
_ = tokio::time::sleep(duration) => {
|
|
354
|
-
let _ = heartbeat_tx.send(HeartbeatAction::CompleteThrottle(tt));
|
|
355
|
-
},
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
HeartbeatExecutorAction::Report { task_token: tt, details } => {
|
|
359
|
-
match sg
|
|
360
|
-
.record_activity_heartbeat(tt.clone(), details.into_payloads())
|
|
361
|
-
.await
|
|
362
|
-
{
|
|
363
|
-
Ok(RecordActivityTaskHeartbeatResponse { cancel_requested }) => {
|
|
364
|
-
if cancel_requested {
|
|
365
|
-
cancels_tx
|
|
366
|
-
.send(PendingActivityCancel::new(
|
|
367
|
-
tt.clone(),
|
|
368
|
-
ActivityCancelReason::Cancelled,
|
|
369
|
-
))
|
|
370
|
-
.expect(
|
|
371
|
-
"Receive half of heartbeat cancels not blocked",
|
|
372
|
-
);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
// Send cancels for any activity that learns its workflow already
|
|
376
|
-
// finished (which is one thing not found implies - other reasons
|
|
377
|
-
// would seem equally valid).
|
|
378
|
-
Err(s) if s.code() == tonic::Code::NotFound => {
|
|
379
|
-
debug!(task_token = %tt,
|
|
380
|
-
"Activity not found when recording heartbeat");
|
|
381
|
-
cancels_tx
|
|
382
|
-
.send(PendingActivityCancel::new(
|
|
383
|
-
tt.clone(),
|
|
384
|
-
ActivityCancelReason::NotFound,
|
|
385
|
-
))
|
|
386
|
-
.expect("Receive half of heartbeat cancels not blocked");
|
|
387
|
-
}
|
|
388
|
-
Err(e) => {
|
|
389
|
-
warn!("Error when recording heartbeat: {:?}", e);
|
|
390
|
-
}
|
|
391
|
-
};
|
|
392
|
-
let _ = heartbeat_tx.send(HeartbeatAction::CompleteReport(tt));
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
}),
|
|
397
|
-
);
|
|
398
|
-
|
|
399
|
-
Self {
|
|
400
|
-
incoming_cancels: Mutex::new(cancels_rx),
|
|
401
|
-
join_handle: Mutex::new(Some(join_handle)),
|
|
402
|
-
shutdown_token,
|
|
403
|
-
heartbeat_tx,
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
|
|
408
391
|
#[cfg(test)]
|
|
409
392
|
mod test {
|
|
410
393
|
use super::*;
|
|
@@ -425,7 +408,8 @@ mod test {
|
|
|
425
408
|
.expect_record_activity_heartbeat()
|
|
426
409
|
.returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
|
|
427
410
|
.times(2);
|
|
428
|
-
let
|
|
411
|
+
let (cancel_tx, _cancel_rx) = unbounded_channel();
|
|
412
|
+
let hm = ActivityHeartbeatManager::new(Arc::new(mock_client), cancel_tx);
|
|
429
413
|
let fake_task_token = vec![1, 2, 3];
|
|
430
414
|
// Send 2 heartbeat requests for 20ms apart.
|
|
431
415
|
// The first heartbeat should be sent right away, and
|
|
@@ -446,14 +430,14 @@ mod test {
|
|
|
446
430
|
.expect_record_activity_heartbeat()
|
|
447
431
|
.returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
|
|
448
432
|
.times(3);
|
|
449
|
-
let
|
|
433
|
+
let (cancel_tx, _cancel_rx) = unbounded_channel();
|
|
434
|
+
let hm = ActivityHeartbeatManager::new(Arc::new(mock_client), cancel_tx);
|
|
450
435
|
let fake_task_token = vec![1, 2, 3];
|
|
451
436
|
// Heartbeats always get sent if recorded less frequently than the throttle interval
|
|
452
437
|
for i in 0_u8..3 {
|
|
453
438
|
record_heartbeat(&hm, fake_task_token.clone(), i, Duration::from_millis(10));
|
|
454
439
|
sleep(Duration::from_millis(20)).await;
|
|
455
440
|
}
|
|
456
|
-
// sleep again to let heartbeats be flushed
|
|
457
441
|
hm.shutdown().await;
|
|
458
442
|
}
|
|
459
443
|
|
|
@@ -466,7 +450,8 @@ mod test {
|
|
|
466
450
|
.expect_record_activity_heartbeat()
|
|
467
451
|
.returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
|
|
468
452
|
.times(1);
|
|
469
|
-
let
|
|
453
|
+
let (cancel_tx, _cancel_rx) = unbounded_channel();
|
|
454
|
+
let hm = ActivityHeartbeatManager::new(Arc::new(mock_client), cancel_tx);
|
|
470
455
|
let fake_task_token = vec![1, 2, 3];
|
|
471
456
|
// Send a whole bunch of heartbeats very fast. We should still only send one total.
|
|
472
457
|
for i in 0_u8..50 {
|
|
@@ -485,7 +470,8 @@ mod test {
|
|
|
485
470
|
.expect_record_activity_heartbeat()
|
|
486
471
|
.returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
|
|
487
472
|
.times(2);
|
|
488
|
-
let
|
|
473
|
+
let (cancel_tx, _cancel_rx) = unbounded_channel();
|
|
474
|
+
let hm = ActivityHeartbeatManager::new(Arc::new(mock_client), cancel_tx);
|
|
489
475
|
let fake_task_token = vec![1, 2, 3];
|
|
490
476
|
record_heartbeat(&hm, fake_task_token.clone(), 0, Duration::from_millis(100));
|
|
491
477
|
sleep(Duration::from_millis(500)).await;
|
|
@@ -502,7 +488,8 @@ mod test {
|
|
|
502
488
|
.expect_record_activity_heartbeat()
|
|
503
489
|
.returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
|
|
504
490
|
.times(2);
|
|
505
|
-
let
|
|
491
|
+
let (cancel_tx, _cancel_rx) = unbounded_channel();
|
|
492
|
+
let hm = ActivityHeartbeatManager::new(Arc::new(mock_client), cancel_tx);
|
|
506
493
|
let fake_task_token = vec![1, 2, 3];
|
|
507
494
|
record_heartbeat(&hm, fake_task_token.clone(), 0, Duration::from_millis(100));
|
|
508
495
|
// Let it propagate
|
|
@@ -522,42 +509,14 @@ mod test {
|
|
|
522
509
|
.expect_record_activity_heartbeat()
|
|
523
510
|
.returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
|
|
524
511
|
.times(1);
|
|
525
|
-
let
|
|
512
|
+
let (cancel_tx, _cancel_rx) = unbounded_channel();
|
|
513
|
+
let hm = ActivityHeartbeatManager::new(Arc::new(mock_client), cancel_tx);
|
|
526
514
|
let fake_task_token = vec![1, 2, 3];
|
|
527
515
|
record_heartbeat(&hm, fake_task_token.clone(), 0, Duration::from_millis(100));
|
|
528
516
|
hm.evict(fake_task_token.clone().into()).await;
|
|
529
517
|
hm.shutdown().await;
|
|
530
518
|
}
|
|
531
519
|
|
|
532
|
-
/// Recording new heartbeats after shutdown is not allowed, and will result in error.
|
|
533
|
-
#[tokio::test]
|
|
534
|
-
async fn record_after_shutdown() {
|
|
535
|
-
let mut mock_client = mock_workflow_client();
|
|
536
|
-
mock_client
|
|
537
|
-
.expect_record_activity_heartbeat()
|
|
538
|
-
.returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
|
|
539
|
-
.times(0);
|
|
540
|
-
let hm = ActivityHeartbeatManager::new(Arc::new(mock_client));
|
|
541
|
-
hm.shutdown().await;
|
|
542
|
-
match hm.record(
|
|
543
|
-
ActivityHeartbeat {
|
|
544
|
-
task_token: vec![1, 2, 3],
|
|
545
|
-
details: vec![Payload {
|
|
546
|
-
// payload doesn't matter in this case, as it shouldn't get sent anyways.
|
|
547
|
-
..Default::default()
|
|
548
|
-
}],
|
|
549
|
-
},
|
|
550
|
-
Duration::from_millis(1000),
|
|
551
|
-
) {
|
|
552
|
-
Ok(_) => {
|
|
553
|
-
unreachable!("heartbeat should not be recorded after the shutdown");
|
|
554
|
-
}
|
|
555
|
-
Err(e) => {
|
|
556
|
-
matches!(e, ActivityHeartbeatError::ShuttingDown);
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
|
|
561
520
|
fn record_heartbeat(
|
|
562
521
|
hm: &ActivityHeartbeatManager,
|
|
563
522
|
task_token: Vec<u8>,
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
use crate::abstractions::MeteredSemaphore;
|
|
2
|
+
use crate::worker::activities::PermittedTqResp;
|
|
3
|
+
use crate::{pollers::BoxedActPoller, MetricsContext};
|
|
4
|
+
use futures::{stream, Stream};
|
|
5
|
+
use governor::clock::DefaultClock;
|
|
6
|
+
use governor::middleware::NoOpMiddleware;
|
|
7
|
+
use governor::state::{InMemoryState, NotKeyed};
|
|
8
|
+
use governor::RateLimiter;
|
|
9
|
+
use std::sync::Arc;
|
|
10
|
+
use temporal_sdk_core_protos::temporal::api::workflowservice::v1::PollActivityTaskQueueResponse;
|
|
11
|
+
use tokio::select;
|
|
12
|
+
use tokio_util::sync::CancellationToken;
|
|
13
|
+
|
|
14
|
+
struct StreamState {
|
|
15
|
+
poller: BoxedActPoller,
|
|
16
|
+
semaphore: Arc<MeteredSemaphore>,
|
|
17
|
+
rate_limiter: Option<RateLimiter<NotKeyed, InMemoryState, DefaultClock, NoOpMiddleware>>,
|
|
18
|
+
metrics: MetricsContext,
|
|
19
|
+
shutdown_token: CancellationToken,
|
|
20
|
+
poller_was_shutdown: bool,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
pub(crate) fn new_activity_task_poller(
|
|
24
|
+
poller: BoxedActPoller,
|
|
25
|
+
semaphore: Arc<MeteredSemaphore>,
|
|
26
|
+
rate_limiter: Option<RateLimiter<NotKeyed, InMemoryState, DefaultClock, NoOpMiddleware>>,
|
|
27
|
+
metrics: MetricsContext,
|
|
28
|
+
shutdown_token: CancellationToken,
|
|
29
|
+
) -> impl Stream<Item = Result<PermittedTqResp, tonic::Status>> {
|
|
30
|
+
let state = StreamState {
|
|
31
|
+
poller,
|
|
32
|
+
semaphore,
|
|
33
|
+
rate_limiter,
|
|
34
|
+
metrics,
|
|
35
|
+
shutdown_token,
|
|
36
|
+
poller_was_shutdown: false,
|
|
37
|
+
};
|
|
38
|
+
stream::unfold(state, |mut state| async move {
|
|
39
|
+
loop {
|
|
40
|
+
let poll = async {
|
|
41
|
+
let permit = state
|
|
42
|
+
.semaphore
|
|
43
|
+
.acquire_owned()
|
|
44
|
+
.await
|
|
45
|
+
.expect("outstanding activity semaphore not closed");
|
|
46
|
+
if !state.poller_was_shutdown {
|
|
47
|
+
if let Some(ref rl) = state.rate_limiter {
|
|
48
|
+
rl.until_ready().await;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
loop {
|
|
52
|
+
return match state.poller.poll().await {
|
|
53
|
+
Some(Ok(resp)) => {
|
|
54
|
+
if resp == PollActivityTaskQueueResponse::default() {
|
|
55
|
+
// We get the default proto in the event that the long poll times out.
|
|
56
|
+
debug!("Poll activity task timeout");
|
|
57
|
+
state.metrics.act_poll_timeout();
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
Some(Ok(PermittedTqResp { permit, resp }))
|
|
61
|
+
}
|
|
62
|
+
Some(Err(e)) => {
|
|
63
|
+
warn!(error=?e, "Error while polling for activity tasks");
|
|
64
|
+
Some(Err(e))
|
|
65
|
+
}
|
|
66
|
+
// If poller returns None, it's dead, thus we also return None to terminate this
|
|
67
|
+
// stream.
|
|
68
|
+
None => None,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
if state.poller_was_shutdown {
|
|
73
|
+
return poll.await.map(|res| (res, state));
|
|
74
|
+
}
|
|
75
|
+
select! {
|
|
76
|
+
biased;
|
|
77
|
+
|
|
78
|
+
_ = state.shutdown_token.cancelled() => {
|
|
79
|
+
state.poller.notify_shutdown();
|
|
80
|
+
state.poller_was_shutdown = true;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
res = poll => {
|
|
84
|
+
return res.map(|res| (res, state));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
}
|