@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,15 +1,33 @@
|
|
|
1
1
|
use super::*;
|
|
2
2
|
use futures::Future;
|
|
3
3
|
|
|
4
|
+
pub(crate) static DEFAULT_TEST_CAPABILITIES: &Capabilities = &Capabilities {
|
|
5
|
+
signal_and_query_header: true,
|
|
6
|
+
internal_error_differentiation: true,
|
|
7
|
+
activity_failure_include_heartbeat: true,
|
|
8
|
+
supports_schedules: true,
|
|
9
|
+
encoded_failure_attributes: true,
|
|
10
|
+
build_id_based_versioning: true,
|
|
11
|
+
upsert_memo: true,
|
|
12
|
+
eager_workflow_start: true,
|
|
13
|
+
sdk_metadata: true,
|
|
14
|
+
};
|
|
15
|
+
|
|
4
16
|
#[cfg(test)]
|
|
5
17
|
/// Create a mock client primed with basic necessary expectations
|
|
6
18
|
pub(crate) fn mock_workflow_client() -> MockWorkerClient {
|
|
7
|
-
MockWorkerClient::new()
|
|
19
|
+
let mut r = MockWorkerClient::new();
|
|
20
|
+
r.expect_capabilities()
|
|
21
|
+
.returning(|| Some(DEFAULT_TEST_CAPABILITIES));
|
|
22
|
+
r
|
|
8
23
|
}
|
|
9
24
|
|
|
10
25
|
/// Create a mock manual client primed with basic necessary expectations
|
|
11
26
|
pub(crate) fn mock_manual_workflow_client() -> MockManualWorkerClient {
|
|
12
|
-
MockManualWorkerClient::new()
|
|
27
|
+
let mut r = MockManualWorkerClient::new();
|
|
28
|
+
r.expect_capabilities()
|
|
29
|
+
.returning(|| Some(DEFAULT_TEST_CAPABILITIES));
|
|
30
|
+
r
|
|
13
31
|
}
|
|
14
32
|
|
|
15
33
|
// Need a version of the mock that can return futures so we can return potentially pending
|
|
@@ -83,5 +101,7 @@ mockall::mock! {
|
|
|
83
101
|
query_result: QueryResult,
|
|
84
102
|
) -> impl Future<Output = Result<RespondQueryTaskCompletedResponse>> + Send + 'b
|
|
85
103
|
where 'a: 'b, Self: 'b;
|
|
104
|
+
|
|
105
|
+
fn capabilities(&self) -> Option<&'static get_system_info_response::Capabilities>;
|
|
86
106
|
}
|
|
87
107
|
}
|
|
@@ -7,12 +7,13 @@ use temporal_sdk_core_protos::{
|
|
|
7
7
|
coresdk::workflow_commands::QueryResult,
|
|
8
8
|
temporal::api::{
|
|
9
9
|
command::v1::Command,
|
|
10
|
-
common::v1::{Payloads, WorkflowExecution},
|
|
10
|
+
common::v1::{MeteringMetadata, Payloads, WorkflowExecution},
|
|
11
11
|
enums::v1::{TaskQueueKind, WorkflowTaskFailedCause},
|
|
12
12
|
failure::v1::Failure,
|
|
13
13
|
query::v1::WorkflowQueryResult,
|
|
14
|
+
sdk::v1::WorkflowTaskCompletedMetadata,
|
|
14
15
|
taskqueue::v1::{StickyExecutionAttributes, TaskQueue, TaskQueueMetadata, VersionId},
|
|
15
|
-
workflowservice::v1
|
|
16
|
+
workflowservice::v1::{get_system_info_response::Capabilities, *},
|
|
16
17
|
},
|
|
17
18
|
TaskToken,
|
|
18
19
|
};
|
|
@@ -109,6 +110,9 @@ pub(crate) trait WorkerClient: Sync + Send {
|
|
|
109
110
|
task_token: TaskToken,
|
|
110
111
|
query_result: QueryResult,
|
|
111
112
|
) -> Result<RespondQueryTaskCompletedResponse>;
|
|
113
|
+
|
|
114
|
+
#[allow(clippy::needless_lifetimes)] // Clippy is wrong here
|
|
115
|
+
fn capabilities<'a>(&'a self) -> Option<&'a get_system_info_response::Capabilities>;
|
|
112
116
|
}
|
|
113
117
|
|
|
114
118
|
#[async_trait::async_trait]
|
|
@@ -189,6 +193,7 @@ impl WorkerClient for WorkerClientBag {
|
|
|
189
193
|
worker_versioning_id: Some(VersionId {
|
|
190
194
|
worker_build_id: self.versioning_build_id(),
|
|
191
195
|
}),
|
|
196
|
+
messages: vec![],
|
|
192
197
|
binary_checksum: self.worker_build_id.clone(),
|
|
193
198
|
query_results: request
|
|
194
199
|
.query_responses
|
|
@@ -206,6 +211,8 @@ impl WorkerClient for WorkerClientBag {
|
|
|
206
211
|
})
|
|
207
212
|
.collect(),
|
|
208
213
|
namespace: self.namespace.clone(),
|
|
214
|
+
sdk_metadata: Some(request.sdk_metadata),
|
|
215
|
+
metering_metadata: Some(request.metering_metadata),
|
|
209
216
|
};
|
|
210
217
|
Ok(self
|
|
211
218
|
.client
|
|
@@ -302,6 +309,7 @@ impl WorkerClient for WorkerClientBag {
|
|
|
302
309
|
identity: self.identity.clone(),
|
|
303
310
|
binary_checksum: self.worker_build_id.clone(),
|
|
304
311
|
namespace: self.namespace.clone(),
|
|
312
|
+
messages: vec![],
|
|
305
313
|
};
|
|
306
314
|
Ok(self
|
|
307
315
|
.client
|
|
@@ -352,6 +360,10 @@ impl WorkerClient for WorkerClientBag {
|
|
|
352
360
|
.await?
|
|
353
361
|
.into_inner())
|
|
354
362
|
}
|
|
363
|
+
|
|
364
|
+
fn capabilities(&self) -> Option<&Capabilities> {
|
|
365
|
+
self.client.get_client().inner().capabilities()
|
|
366
|
+
}
|
|
355
367
|
}
|
|
356
368
|
|
|
357
369
|
/// A version of [RespondWorkflowTaskCompletedRequest] that will finish being filled out by the
|
|
@@ -370,4 +382,8 @@ pub(crate) struct WorkflowTaskCompletion {
|
|
|
370
382
|
pub return_new_workflow_task: bool,
|
|
371
383
|
/// Force a new WFT to be created after this completion
|
|
372
384
|
pub force_create_new_workflow_task: bool,
|
|
385
|
+
/// SDK-specific metadata to send
|
|
386
|
+
pub sdk_metadata: WorkflowTaskCompletedMetadata,
|
|
387
|
+
/// Metering info
|
|
388
|
+
pub metering_metadata: MeteringMetadata,
|
|
373
389
|
}
|
|
@@ -3,6 +3,8 @@ pub(crate) mod client;
|
|
|
3
3
|
mod workflow;
|
|
4
4
|
|
|
5
5
|
pub use temporal_sdk_core_api::worker::{WorkerConfig, WorkerConfigBuilder};
|
|
6
|
+
#[cfg(feature = "save_wf_inputs")]
|
|
7
|
+
pub use workflow::replay_wf_state_inputs;
|
|
6
8
|
|
|
7
9
|
pub(crate) use activities::{
|
|
8
10
|
ExecutingLAId, LocalActRequest, LocalActivityExecutionResult, LocalActivityResolution,
|
|
@@ -19,20 +21,30 @@ use crate::{
|
|
|
19
21
|
WorkflowTaskPoller,
|
|
20
22
|
},
|
|
21
23
|
protosext::{validate_activity_completion, ValidPollWFTQResponse},
|
|
22
|
-
telemetry::
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
telemetry::{
|
|
25
|
+
metrics::{
|
|
26
|
+
activity_poller, local_activity_worker_type, workflow_poller, workflow_sticky_poller,
|
|
27
|
+
MetricsContext,
|
|
28
|
+
},
|
|
29
|
+
TelemetryInstance,
|
|
25
30
|
},
|
|
26
31
|
worker::{
|
|
27
32
|
activities::{DispatchOrTimeoutLA, LACompleteAction, LocalActivityManager},
|
|
28
33
|
client::WorkerClient,
|
|
29
|
-
workflow::{LocalResolution, WorkflowBasics, Workflows},
|
|
34
|
+
workflow::{LAReqSink, LocalResolution, WorkflowBasics, Workflows},
|
|
30
35
|
},
|
|
31
36
|
ActivityHeartbeat, CompleteActivityError, PollActivityError, PollWfError, WorkerTrait,
|
|
32
37
|
};
|
|
33
38
|
use activities::{LocalInFlightActInfo, WorkerActivityTasks};
|
|
34
39
|
use futures::Stream;
|
|
35
|
-
use std::{
|
|
40
|
+
use std::{
|
|
41
|
+
convert::TryInto,
|
|
42
|
+
future,
|
|
43
|
+
sync::{
|
|
44
|
+
atomic::{AtomicBool, Ordering},
|
|
45
|
+
Arc,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
36
48
|
use temporal_sdk_core_protos::{
|
|
37
49
|
coresdk::{
|
|
38
50
|
activity_result::activity_execution_result,
|
|
@@ -44,10 +56,11 @@ use temporal_sdk_core_protos::{
|
|
|
44
56
|
temporal::api::{
|
|
45
57
|
enums::v1::TaskQueueKind,
|
|
46
58
|
taskqueue::v1::{StickyExecutionAttributes, TaskQueue},
|
|
47
|
-
workflowservice::v1::PollActivityTaskQueueResponse,
|
|
59
|
+
workflowservice::v1::{get_system_info_response, PollActivityTaskQueueResponse},
|
|
48
60
|
},
|
|
49
61
|
TaskToken,
|
|
50
62
|
};
|
|
63
|
+
use tokio::sync::mpsc::unbounded_channel;
|
|
51
64
|
use tokio_util::sync::CancellationToken;
|
|
52
65
|
|
|
53
66
|
/// A worker polls on a certain task queue
|
|
@@ -65,7 +78,11 @@ pub struct Worker {
|
|
|
65
78
|
shutdown_token: CancellationToken,
|
|
66
79
|
/// Will be called at the end of each activation completion
|
|
67
80
|
#[allow(clippy::type_complexity)] // Sorry clippy, there's no simple way to re-use here.
|
|
68
|
-
post_activate_hook: Option<Box<dyn Fn(&Self,
|
|
81
|
+
post_activate_hook: Option<Box<dyn Fn(&Self, PostActivateHookData) + Send + Sync>>,
|
|
82
|
+
/// Set when non-local activities are complete and should stop being polled
|
|
83
|
+
non_local_activities_complete: Arc<AtomicBool>,
|
|
84
|
+
/// Set when local activities are complete and should stop being polled
|
|
85
|
+
local_activities_complete: Arc<AtomicBool>,
|
|
69
86
|
}
|
|
70
87
|
|
|
71
88
|
#[async_trait::async_trait]
|
|
@@ -132,7 +149,13 @@ impl WorkerTrait for Worker {
|
|
|
132
149
|
self.shutdown_token.cancel();
|
|
133
150
|
// First, we want to stop polling of both activity and workflow tasks
|
|
134
151
|
if let Some(atm) = self.at_task_mgr.as_ref() {
|
|
135
|
-
atm.
|
|
152
|
+
atm.initiate_shutdown();
|
|
153
|
+
}
|
|
154
|
+
// Let the manager know that shutdown has been initiated to try to unblock the local
|
|
155
|
+
// activity poll in case this worker is an activity-only worker.
|
|
156
|
+
self.local_act_mgr.shutdown_initiated();
|
|
157
|
+
if !self.workflows.ever_polled() {
|
|
158
|
+
self.local_act_mgr.workflows_have_shutdown();
|
|
136
159
|
}
|
|
137
160
|
info!(
|
|
138
161
|
task_queue=%self.config.task_queue,
|
|
@@ -156,11 +179,17 @@ impl Worker {
|
|
|
156
179
|
config: WorkerConfig,
|
|
157
180
|
sticky_queue_name: Option<String>,
|
|
158
181
|
client: Arc<dyn WorkerClient>,
|
|
159
|
-
|
|
182
|
+
telem_instance: Option<&TelemetryInstance>,
|
|
160
183
|
) -> Self {
|
|
161
184
|
info!(task_queue=%config.task_queue,
|
|
162
185
|
namespace=%config.namespace,
|
|
163
186
|
"Initializing worker");
|
|
187
|
+
let metrics = if let Some(ti) = telem_instance {
|
|
188
|
+
MetricsContext::top_level(config.namespace.clone(), ti)
|
|
189
|
+
.with_task_q(config.task_queue.clone())
|
|
190
|
+
} else {
|
|
191
|
+
MetricsContext::no_op()
|
|
192
|
+
};
|
|
164
193
|
metrics.worker_registered();
|
|
165
194
|
|
|
166
195
|
let shutdown_token = CancellationToken::new();
|
|
@@ -223,31 +252,34 @@ impl Worker {
|
|
|
223
252
|
wft_stream,
|
|
224
253
|
act_poll_buffer,
|
|
225
254
|
metrics,
|
|
255
|
+
telem_instance,
|
|
226
256
|
shutdown_token,
|
|
227
257
|
)
|
|
228
258
|
}
|
|
229
259
|
|
|
230
260
|
#[cfg(test)]
|
|
231
261
|
pub(crate) fn new_test(config: WorkerConfig, client: impl WorkerClient + 'static) -> Self {
|
|
232
|
-
Self::new(config, None, Arc::new(client),
|
|
262
|
+
Self::new(config, None, Arc::new(client), None)
|
|
233
263
|
}
|
|
234
264
|
|
|
265
|
+
#[allow(clippy::too_many_arguments)] // Not much worth combining here
|
|
235
266
|
pub(crate) fn new_with_pollers(
|
|
236
|
-
config: WorkerConfig,
|
|
267
|
+
mut config: WorkerConfig,
|
|
237
268
|
sticky_queue_name: Option<String>,
|
|
238
269
|
client: Arc<dyn WorkerClient>,
|
|
239
270
|
wft_stream: impl Stream<Item = Result<ValidPollWFTQResponse, tonic::Status>> + Send + 'static,
|
|
240
271
|
act_poller: Option<BoxedActPoller>,
|
|
241
272
|
metrics: MetricsContext,
|
|
273
|
+
telem_instance: Option<&TelemetryInstance>,
|
|
242
274
|
shutdown_token: CancellationToken,
|
|
243
275
|
) -> Self {
|
|
276
|
+
let (hb_tx, hb_rx) = unbounded_channel();
|
|
244
277
|
let local_act_mgr = Arc::new(LocalActivityManager::new(
|
|
245
278
|
config.max_outstanding_local_activities,
|
|
246
279
|
config.namespace.clone(),
|
|
280
|
+
hb_tx,
|
|
247
281
|
metrics.with_new_attrs([local_activity_worker_type()]),
|
|
248
282
|
));
|
|
249
|
-
let lam_clone = local_act_mgr.clone();
|
|
250
|
-
let local_act_req_sink = move |requests| lam_clone.enqueue(requests);
|
|
251
283
|
let at_task_mgr = act_poller.map(|ap| {
|
|
252
284
|
WorkerActivityTasks::new(
|
|
253
285
|
config.max_outstanding_activities,
|
|
@@ -257,20 +289,23 @@ impl Worker {
|
|
|
257
289
|
metrics.clone(),
|
|
258
290
|
config.max_heartbeat_throttle_interval,
|
|
259
291
|
config.default_heartbeat_throttle_interval,
|
|
292
|
+
config.graceful_shutdown_period,
|
|
260
293
|
)
|
|
261
294
|
});
|
|
295
|
+
let poll_on_non_local_activities = at_task_mgr.is_some();
|
|
296
|
+
if !poll_on_non_local_activities {
|
|
297
|
+
info!("Activity polling is disabled for this worker");
|
|
298
|
+
};
|
|
299
|
+
let la_sink = LAReqSink::new(local_act_mgr.clone(), config.wf_state_inputs.clone());
|
|
262
300
|
Self {
|
|
263
301
|
wf_client: client.clone(),
|
|
264
302
|
workflows: Workflows::new(
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
max_outstanding_wfts: config.max_outstanding_workflow_tasks,
|
|
268
|
-
shutdown_token: shutdown_token.child_token(),
|
|
303
|
+
build_wf_basics(
|
|
304
|
+
&mut config,
|
|
269
305
|
metrics,
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
},
|
|
306
|
+
shutdown_token.child_token(),
|
|
307
|
+
client.capabilities().cloned().unwrap_or_default(),
|
|
308
|
+
),
|
|
274
309
|
sticky_queue_name.map(|sq| StickyExecutionAttributes {
|
|
275
310
|
worker_task_queue: Some(TaskQueue {
|
|
276
311
|
name: sq,
|
|
@@ -285,16 +320,22 @@ impl Worker {
|
|
|
285
320
|
}),
|
|
286
321
|
client,
|
|
287
322
|
wft_stream,
|
|
288
|
-
|
|
323
|
+
la_sink,
|
|
324
|
+
local_act_mgr.clone(),
|
|
325
|
+
hb_rx,
|
|
289
326
|
at_task_mgr
|
|
290
327
|
.as_ref()
|
|
291
328
|
.map(|mgr| mgr.get_handle_for_workflows()),
|
|
329
|
+
telem_instance,
|
|
292
330
|
),
|
|
293
331
|
at_task_mgr,
|
|
294
332
|
local_act_mgr,
|
|
295
333
|
config,
|
|
296
334
|
shutdown_token,
|
|
297
335
|
post_activate_hook: None,
|
|
336
|
+
// Complete if there configured not to poll on non-local activities.
|
|
337
|
+
non_local_activities_complete: Arc::new(AtomicBool::new(!poll_on_non_local_activities)),
|
|
338
|
+
local_activities_complete: Default::default(),
|
|
298
339
|
}
|
|
299
340
|
}
|
|
300
341
|
|
|
@@ -302,9 +343,11 @@ impl Worker {
|
|
|
302
343
|
/// completed
|
|
303
344
|
async fn shutdown(&self) {
|
|
304
345
|
self.initiate_shutdown();
|
|
305
|
-
//
|
|
306
|
-
//
|
|
307
|
-
self.local_act_mgr
|
|
346
|
+
// We need to wait for all local activities to finish so no more workflow task heartbeats
|
|
347
|
+
// will be generated
|
|
348
|
+
self.local_act_mgr
|
|
349
|
+
.wait_all_outstanding_tasks_finished()
|
|
350
|
+
.await;
|
|
308
351
|
// Wait for workflows to finish
|
|
309
352
|
self.workflows
|
|
310
353
|
.shutdown()
|
|
@@ -312,12 +355,13 @@ impl Worker {
|
|
|
312
355
|
.expect("Workflow processing terminates cleanly");
|
|
313
356
|
// Wait for activities to finish
|
|
314
357
|
if let Some(acts) = self.at_task_mgr.as_ref() {
|
|
315
|
-
acts.
|
|
358
|
+
acts.shutdown().await;
|
|
316
359
|
}
|
|
317
360
|
}
|
|
318
361
|
|
|
319
362
|
/// Finish shutting down by consuming the background pollers and freeing all resources
|
|
320
363
|
async fn finalize_shutdown(self) {
|
|
364
|
+
self.shutdown().await;
|
|
321
365
|
if let Some(b) = self.at_task_mgr {
|
|
322
366
|
b.shutdown().await;
|
|
323
367
|
}
|
|
@@ -346,13 +390,9 @@ impl Worker {
|
|
|
346
390
|
.unwrap_or_default()
|
|
347
391
|
}
|
|
348
392
|
|
|
349
|
-
#[
|
|
393
|
+
#[allow(unused)]
|
|
350
394
|
pub(crate) async fn available_wft_permits(&self) -> usize {
|
|
351
|
-
self.workflows
|
|
352
|
-
.get_state_info()
|
|
353
|
-
.await
|
|
354
|
-
.expect("You can only check for available permits before shutdown")
|
|
355
|
-
.available_wft_permits
|
|
395
|
+
self.workflows.available_wft_permits()
|
|
356
396
|
}
|
|
357
397
|
|
|
358
398
|
/// Get new activity tasks (may be local or nonlocal). Local activities are returned first
|
|
@@ -361,35 +401,63 @@ impl Worker {
|
|
|
361
401
|
/// Returns `Ok(None)` in the event of a poll timeout or if the polling loop should otherwise
|
|
362
402
|
/// be restarted
|
|
363
403
|
async fn activity_poll(&self) -> Result<Option<ActivityTask>, PollActivityError> {
|
|
404
|
+
let local_activities_complete = self.local_activities_complete.load(Ordering::Relaxed);
|
|
405
|
+
let non_local_activities_complete =
|
|
406
|
+
self.non_local_activities_complete.load(Ordering::Relaxed);
|
|
407
|
+
if local_activities_complete && non_local_activities_complete {
|
|
408
|
+
return Err(PollActivityError::ShutDown);
|
|
409
|
+
}
|
|
364
410
|
let act_mgr_poll = async {
|
|
411
|
+
if non_local_activities_complete {
|
|
412
|
+
future::pending::<()>().await;
|
|
413
|
+
unreachable!()
|
|
414
|
+
}
|
|
365
415
|
if let Some(ref act_mgr) = self.at_task_mgr {
|
|
366
|
-
act_mgr.poll().await
|
|
416
|
+
let res = act_mgr.poll().await;
|
|
417
|
+
if let Err(err) = res.as_ref() {
|
|
418
|
+
if matches!(err, PollActivityError::ShutDown) {
|
|
419
|
+
self.non_local_activities_complete
|
|
420
|
+
.store(true, Ordering::Relaxed);
|
|
421
|
+
return Ok(None);
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
res.map(Some)
|
|
367
425
|
} else {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
426
|
+
// We expect the local activity branch below to produce shutdown when appropriate if
|
|
427
|
+
// there are no activity pollers.
|
|
428
|
+
future::pending::<()>().await;
|
|
429
|
+
unreachable!()
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
let local_activities_poll = async {
|
|
433
|
+
if local_activities_complete {
|
|
434
|
+
future::pending::<()>().await;
|
|
435
|
+
unreachable!()
|
|
436
|
+
}
|
|
437
|
+
match self.local_act_mgr.next_pending().await {
|
|
438
|
+
Some(DispatchOrTimeoutLA::Dispatch(r)) => Ok(Some(r)),
|
|
439
|
+
Some(DispatchOrTimeoutLA::Timeout {
|
|
440
|
+
run_id,
|
|
441
|
+
resolution,
|
|
442
|
+
task,
|
|
443
|
+
}) => {
|
|
444
|
+
self.notify_local_result(&run_id, LocalResolution::LocalActivity(resolution));
|
|
445
|
+
Ok(task)
|
|
446
|
+
}
|
|
447
|
+
None => {
|
|
448
|
+
if self.shutdown_token.is_cancelled() {
|
|
449
|
+
self.local_activities_complete
|
|
450
|
+
.store(true, Ordering::Relaxed);
|
|
451
|
+
}
|
|
452
|
+
Ok(None)
|
|
453
|
+
}
|
|
371
454
|
}
|
|
372
455
|
};
|
|
373
456
|
|
|
374
457
|
tokio::select! {
|
|
375
458
|
biased;
|
|
376
459
|
|
|
377
|
-
r =
|
|
378
|
-
match r {
|
|
379
|
-
Some(DispatchOrTimeoutLA::Dispatch(r)) => Ok(Some(r)),
|
|
380
|
-
Some(DispatchOrTimeoutLA::Timeout { run_id, resolution, task }) => {
|
|
381
|
-
self.notify_local_result(
|
|
382
|
-
&run_id, LocalResolution::LocalActivity(resolution));
|
|
383
|
-
Ok(task)
|
|
384
|
-
},
|
|
385
|
-
None => {
|
|
386
|
-
if self.shutdown_token.is_cancelled() {
|
|
387
|
-
return Err(PollActivityError::ShutDown);
|
|
388
|
-
}
|
|
389
|
-
Ok(None)
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
},
|
|
460
|
+
r = local_activities_poll => r,
|
|
393
461
|
r = act_mgr_poll => r,
|
|
394
462
|
}
|
|
395
463
|
}
|
|
@@ -448,7 +516,16 @@ impl Worker {
|
|
|
448
516
|
|
|
449
517
|
#[instrument(skip(self), fields(run_id, workflow_id, task_queue=%self.config.task_queue))]
|
|
450
518
|
pub(crate) async fn next_workflow_activation(&self) -> Result<WorkflowActivation, PollWfError> {
|
|
451
|
-
self.workflows.next_workflow_activation().await
|
|
519
|
+
let r = self.workflows.next_workflow_activation().await;
|
|
520
|
+
// In the event workflows are shutdown, begin shutdown of everything else, since that's
|
|
521
|
+
// about to happen anyway. Tell the local activity manager that, so that it can know to
|
|
522
|
+
// cancel any remaining outstanding LAs and shutdown.
|
|
523
|
+
if matches!(r, Err(PollWfError::ShutDown)) {
|
|
524
|
+
// This is covering the situation where WFT pollers dying is the reason for shutdown
|
|
525
|
+
self.initiate_shutdown();
|
|
526
|
+
self.local_act_mgr.workflows_have_shutdown();
|
|
527
|
+
}
|
|
528
|
+
r
|
|
452
529
|
}
|
|
453
530
|
|
|
454
531
|
#[instrument(skip(self, completion),
|
|
@@ -458,11 +535,14 @@ impl Worker {
|
|
|
458
535
|
&self,
|
|
459
536
|
completion: WorkflowActivationCompletion,
|
|
460
537
|
) -> Result<(), CompleteWfError> {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
538
|
+
self.workflows
|
|
539
|
+
.activation_completed(
|
|
540
|
+
completion,
|
|
541
|
+
self.post_activate_hook
|
|
542
|
+
.as_ref()
|
|
543
|
+
.map(|h| |data: PostActivateHookData| h(self, data)),
|
|
544
|
+
)
|
|
545
|
+
.await?;
|
|
466
546
|
Ok(())
|
|
467
547
|
}
|
|
468
548
|
|
|
@@ -479,7 +559,7 @@ impl Worker {
|
|
|
479
559
|
/// Sets a function to be called at the end of each activation completion
|
|
480
560
|
pub(crate) fn set_post_activate_hook(
|
|
481
561
|
&mut self,
|
|
482
|
-
callback: impl Fn(&Self,
|
|
562
|
+
callback: impl Fn(&Self, PostActivateHookData) + Send + Sync + 'static,
|
|
483
563
|
) {
|
|
484
564
|
self.post_activate_hook = Some(Box::new(callback))
|
|
485
565
|
}
|
|
@@ -498,7 +578,7 @@ impl Worker {
|
|
|
498
578
|
runtime: info.dispatch_time.elapsed(),
|
|
499
579
|
attempt: info.attempt,
|
|
500
580
|
backoff,
|
|
501
|
-
original_schedule_time:
|
|
581
|
+
original_schedule_time: info.la_info.schedule_cmd.original_schedule_time,
|
|
502
582
|
}),
|
|
503
583
|
)
|
|
504
584
|
}
|
|
@@ -508,14 +588,45 @@ impl Worker {
|
|
|
508
588
|
}
|
|
509
589
|
}
|
|
510
590
|
|
|
591
|
+
pub struct PostActivateHookData<'a> {
|
|
592
|
+
pub run_id: &'a str,
|
|
593
|
+
pub most_recent_event: usize,
|
|
594
|
+
pub replaying: bool,
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
fn build_wf_basics(
|
|
598
|
+
config: &mut WorkerConfig,
|
|
599
|
+
metrics: MetricsContext,
|
|
600
|
+
shutdown_token: CancellationToken,
|
|
601
|
+
server_capabilities: get_system_info_response::Capabilities,
|
|
602
|
+
) -> WorkflowBasics {
|
|
603
|
+
WorkflowBasics {
|
|
604
|
+
max_cached_workflows: config.max_cached_workflows,
|
|
605
|
+
max_outstanding_wfts: config.max_outstanding_workflow_tasks,
|
|
606
|
+
shutdown_token,
|
|
607
|
+
metrics,
|
|
608
|
+
namespace: config.namespace.clone(),
|
|
609
|
+
task_queue: config.task_queue.clone(),
|
|
610
|
+
ignore_evicts_on_shutdown: config.ignore_evicts_on_shutdown,
|
|
611
|
+
fetching_concurrency: config.fetching_concurrency,
|
|
612
|
+
server_capabilities,
|
|
613
|
+
#[cfg(feature = "save_wf_inputs")]
|
|
614
|
+
wf_state_inputs: config.wf_state_inputs.take(),
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
511
618
|
#[cfg(test)]
|
|
512
619
|
mod tests {
|
|
513
620
|
use super::*;
|
|
514
|
-
use crate::{
|
|
621
|
+
use crate::{
|
|
622
|
+
advance_fut, test_help::test_worker_cfg, worker::client::mocks::mock_workflow_client,
|
|
623
|
+
};
|
|
624
|
+
use futures::FutureExt;
|
|
625
|
+
|
|
515
626
|
use temporal_sdk_core_protos::temporal::api::workflowservice::v1::PollActivityTaskQueueResponse;
|
|
516
627
|
|
|
517
628
|
#[tokio::test]
|
|
518
|
-
async fn
|
|
629
|
+
async fn activity_timeouts_maintain_permit() {
|
|
519
630
|
let mut mock_client = mock_workflow_client();
|
|
520
631
|
mock_client
|
|
521
632
|
.expect_poll_activity_task()
|
|
@@ -526,8 +637,16 @@ mod tests {
|
|
|
526
637
|
.build()
|
|
527
638
|
.unwrap();
|
|
528
639
|
let worker = Worker::new_test(cfg, mock_client);
|
|
529
|
-
|
|
530
|
-
|
|
640
|
+
let fut = worker.poll_activity_task();
|
|
641
|
+
advance_fut!(fut);
|
|
642
|
+
assert_eq!(
|
|
643
|
+
worker
|
|
644
|
+
.at_task_mgr
|
|
645
|
+
.as_ref()
|
|
646
|
+
.unwrap()
|
|
647
|
+
.remaining_activity_capacity(),
|
|
648
|
+
4
|
|
649
|
+
);
|
|
531
650
|
}
|
|
532
651
|
|
|
533
652
|
#[tokio::test]
|
|
@@ -25,11 +25,9 @@ impl WorkflowBridge {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
#[async_trait::async_trait]
|
|
29
28
|
impl WorkflowFetcher for WorkflowBridge {
|
|
30
|
-
|
|
29
|
+
fn fetch_workflow_iteration_output(&mut self) -> Vec<WFCommand> {
|
|
31
30
|
let in_cmds = self.incoming_commands.try_recv();
|
|
32
|
-
|
|
33
31
|
let in_cmds = in_cmds.unwrap_or_else(|_| vec![WFCommand::NoCommandsFromLang]);
|
|
34
32
|
debug!(in_cmds = %in_cmds.display(), "wf bridge iteration fetch");
|
|
35
33
|
in_cmds
|
|
@@ -78,16 +78,14 @@ impl DrivenWorkflow {
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
#[async_trait::async_trait]
|
|
82
81
|
impl WorkflowFetcher for DrivenWorkflow {
|
|
83
|
-
|
|
84
|
-
self.fetcher.fetch_workflow_iteration_output()
|
|
82
|
+
fn fetch_workflow_iteration_output(&mut self) -> Vec<WFCommand> {
|
|
83
|
+
self.fetcher.fetch_workflow_iteration_output()
|
|
85
84
|
}
|
|
86
85
|
}
|
|
87
86
|
|
|
88
87
|
/// Implementors of this trait represent a way to fetch output from executing/iterating some
|
|
89
88
|
/// workflow code (or a mocked workflow).
|
|
90
|
-
#[async_trait::async_trait]
|
|
91
89
|
pub trait WorkflowFetcher: Send {
|
|
92
90
|
/// Obtain any output from the workflow's recent execution(s). Because the lang sdk is
|
|
93
91
|
/// responsible for calling workflow code as a result of receiving tasks from
|
|
@@ -97,5 +95,5 @@ pub trait WorkflowFetcher: Send {
|
|
|
97
95
|
///
|
|
98
96
|
/// In the case of the real [WorkflowBridge] implementation, commands are simply pulled from
|
|
99
97
|
/// a buffer that the language side sinks into when it calls [crate::Core::complete_task]
|
|
100
|
-
|
|
98
|
+
fn fetch_workflow_iteration_output(&mut self) -> Vec<WFCommand>;
|
|
101
99
|
}
|