@temporalio/core-bridge 1.11.8 → 1.12.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 +219 -193
- package/Cargo.toml +27 -8
- package/README.md +5 -0
- package/index.js +72 -12
- package/lib/errors.d.ts +25 -0
- package/lib/errors.js +76 -1
- package/lib/errors.js.map +1 -1
- package/lib/index.d.ts +11 -478
- package/lib/index.js +28 -5
- package/lib/index.js.map +1 -1
- package/lib/native.d.ts +330 -0
- package/lib/{worker-tuner.js → native.js} +1 -1
- package/lib/native.js.map +1 -0
- package/package.json +7 -3
- 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/.cargo/config.toml +8 -2
- package/sdk-core/.cargo/multi-worker-manual-test +15 -0
- package/sdk-core/.github/workflows/per-pr.yml +40 -11
- package/sdk-core/AGENTS.md +73 -0
- package/sdk-core/ARCHITECTURE.md +71 -23
- package/sdk-core/Cargo.toml +1 -1
- package/sdk-core/README.md +4 -4
- package/sdk-core/arch_docs/workflow_task_chunking.md +51 -0
- package/sdk-core/client/Cargo.toml +1 -1
- package/sdk-core/client/src/lib.rs +49 -13
- package/sdk-core/client/src/metrics.rs +15 -16
- package/sdk-core/client/src/proxy.rs +2 -2
- package/sdk-core/client/src/raw.rs +54 -8
- package/sdk-core/client/src/retry.rs +109 -13
- package/sdk-core/client/src/worker_registry/mod.rs +4 -4
- package/sdk-core/client/src/workflow_handle/mod.rs +1 -1
- package/sdk-core/core/Cargo.toml +28 -8
- package/sdk-core/core/src/abstractions.rs +62 -10
- package/sdk-core/core/src/core_tests/activity_tasks.rs +180 -8
- package/sdk-core/core/src/core_tests/mod.rs +4 -4
- package/sdk-core/core/src/core_tests/queries.rs +18 -4
- package/sdk-core/core/src/core_tests/workers.rs +3 -3
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +191 -25
- package/sdk-core/core/src/ephemeral_server/mod.rs +10 -3
- package/sdk-core/core/src/internal_flags.rs +14 -14
- package/sdk-core/core/src/lib.rs +5 -2
- package/sdk-core/core/src/pollers/mod.rs +1 -1
- package/sdk-core/core/src/pollers/poll_buffer.rs +495 -164
- package/sdk-core/core/src/protosext/mod.rs +3 -3
- package/sdk-core/core/src/replay/mod.rs +3 -3
- package/sdk-core/core/src/telemetry/metrics.rs +13 -4
- package/sdk-core/core/src/telemetry/mod.rs +72 -70
- package/sdk-core/core/src/telemetry/otel.rs +51 -54
- package/sdk-core/core/src/test_help/mod.rs +9 -3
- package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +31 -11
- package/sdk-core/core/src/worker/activities/local_activities.rs +35 -28
- package/sdk-core/core/src/worker/activities.rs +58 -30
- package/sdk-core/core/src/worker/client/mocks.rs +3 -3
- package/sdk-core/core/src/worker/client.rs +155 -53
- package/sdk-core/core/src/worker/mod.rs +103 -95
- package/sdk-core/core/src/worker/nexus.rs +100 -73
- package/sdk-core/core/src/worker/tuner/resource_based.rs +14 -6
- package/sdk-core/core/src/worker/tuner.rs +4 -13
- package/sdk-core/core/src/worker/workflow/history_update.rs +47 -51
- package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +1 -4
- package/sdk-core/core/src/worker/workflow/machines/nexus_operation_state_machine.rs +127 -32
- package/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +1 -2
- package/sdk-core/core/src/worker/workflow/machines/update_state_machine.rs +1 -1
- package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +55 -42
- package/sdk-core/core/src/worker/workflow/managed_run.rs +45 -35
- package/sdk-core/core/src/worker/workflow/mod.rs +200 -97
- package/sdk-core/core/src/worker/workflow/wft_poller.rs +175 -4
- package/sdk-core/core/src/worker/workflow/workflow_stream.rs +38 -36
- package/sdk-core/core-api/Cargo.toml +8 -0
- package/sdk-core/core-api/src/envconfig.rs +1544 -0
- package/sdk-core/core-api/src/lib.rs +2 -0
- package/sdk-core/core-api/src/telemetry/metrics.rs +8 -0
- package/sdk-core/core-api/src/telemetry.rs +36 -3
- package/sdk-core/core-api/src/worker.rs +301 -75
- package/sdk-core/docker/docker-compose-telem.yaml +1 -0
- package/sdk-core/etc/prometheus.yaml +6 -2
- package/sdk-core/histories/long_local_activity_with_update-0_history.bin +0 -0
- package/sdk-core/histories/long_local_activity_with_update-1_history.bin +0 -0
- package/sdk-core/histories/long_local_activity_with_update-2_history.bin +0 -0
- package/sdk-core/histories/long_local_activity_with_update-3_history.bin +0 -0
- package/sdk-core/sdk/src/activity_context.rs +5 -0
- package/sdk-core/sdk/src/interceptors.rs +73 -3
- package/sdk-core/sdk/src/lib.rs +15 -16
- package/sdk-core/sdk/src/workflow_context/options.rs +10 -0
- package/sdk-core/sdk/src/workflow_context.rs +48 -29
- package/sdk-core/sdk/src/workflow_future.rs +5 -6
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/.github/workflows/push-to-buf.yml +20 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/CODEOWNERS +6 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/README.md +17 -6
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/VERSION +1 -1
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/buf.lock +7 -2
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/buf.yaml +2 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/cloudservice/v1/request_response.proto +78 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/cloudservice/v1/service.proto +29 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/identity/v1/message.proto +74 -32
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/namespace/v1/message.proto +45 -15
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/nexus/v1/message.proto +7 -1
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/operation/v1/message.proto +3 -3
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/region/v1/message.proto +3 -3
- package/sdk-core/sdk-core-protos/protos/api_upstream/LICENSE +1 -1
- package/sdk-core/sdk-core-protos/protos/api_upstream/buf.yaml +2 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv2.json +1103 -88
- package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv3.yaml +1233 -151
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/activity/v1/message.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/batch/v1/message.proto +19 -24
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/command/v1/message.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/common/v1/message.proto +12 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/deployment/v1/message.proto +45 -45
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/command_type.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/common.proto +15 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/deployment.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/event_type.proto +4 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/namespace.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/nexus.proto +0 -20
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/query.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/reset.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/schedule.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/update.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/workflow.proto +4 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/errordetails/v1/message.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/export/v1/message.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/failure/v1/message.proto +2 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/filter/v1/message.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/history/v1/message.proto +75 -49
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/namespace/v1/message.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/nexus/v1/message.proto +0 -20
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/protocol/v1/message.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/query/v1/message.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/replication/v1/message.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/rules/v1/message.proto +90 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/schedule/v1/message.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/enhanced_stack_trace.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/user_metadata.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/workflow_metadata.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +17 -38
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/update/v1/message.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/version/v1/message.proto +0 -22
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflow/v1/message.proto +151 -44
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +172 -65
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +69 -28
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/activity_task/activity_task.proto +18 -0
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/common/common.proto +5 -0
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/nexus/nexus.proto +16 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +21 -15
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +3 -0
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +3 -0
- package/sdk-core/sdk-core-protos/src/lib.rs +60 -16
- package/sdk-core/test-utils/src/lib.rs +157 -39
- package/sdk-core/tests/cloud_tests.rs +86 -0
- package/sdk-core/tests/fuzzy_workflow.rs +23 -26
- package/sdk-core/tests/global_metric_tests.rs +116 -0
- package/sdk-core/tests/heavy_tests.rs +127 -7
- package/sdk-core/tests/integ_tests/client_tests.rs +2 -8
- package/sdk-core/tests/integ_tests/metrics_tests.rs +100 -106
- package/sdk-core/tests/integ_tests/polling_tests.rs +94 -8
- package/sdk-core/tests/integ_tests/update_tests.rs +75 -6
- package/sdk-core/tests/integ_tests/worker_tests.rs +54 -5
- package/sdk-core/tests/integ_tests/worker_versioning_tests.rs +240 -0
- package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +41 -3
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +168 -8
- package/sdk-core/tests/integ_tests/workflow_tests/nexus.rs +285 -15
- package/sdk-core/tests/integ_tests/workflow_tests/priority.rs +12 -4
- package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +3 -2
- package/sdk-core/tests/integ_tests/workflow_tests.rs +124 -74
- package/sdk-core/tests/main.rs +3 -51
- package/sdk-core/tests/manual_tests.rs +430 -0
- package/sdk-core/tests/runner.rs +28 -2
- package/src/client.rs +565 -0
- package/src/helpers/abort_controller.rs +204 -0
- package/src/helpers/callbacks.rs +299 -0
- package/src/helpers/errors.rs +302 -0
- package/src/helpers/future.rs +44 -0
- package/src/helpers/handles.rs +191 -0
- package/src/helpers/inspect.rs +18 -0
- package/src/helpers/json_string.rs +58 -0
- package/src/helpers/mod.rs +20 -0
- package/src/helpers/properties.rs +71 -0
- package/src/helpers/try_from_js.rs +213 -0
- package/src/helpers/try_into_js.rs +129 -0
- package/src/lib.rs +28 -40
- package/src/logs.rs +111 -0
- package/src/metrics.rs +325 -0
- package/src/runtime.rs +409 -498
- package/src/testing.rs +315 -57
- package/src/worker.rs +907 -378
- package/ts/errors.ts +57 -0
- package/ts/index.ts +10 -596
- package/ts/native.ts +496 -0
- package/lib/worker-tuner.d.ts +0 -167
- package/lib/worker-tuner.js.map +0 -1
- package/src/conversions/slot_supplier_bridge.rs +0 -291
- package/src/conversions.rs +0 -618
- package/src/errors.rs +0 -38
- package/src/helpers.rs +0 -297
- package/ts/worker-tuner.ts +0 -193
package/src/worker.rs
CHANGED
|
@@ -1,454 +1,983 @@
|
|
|
1
|
-
use
|
|
2
|
-
|
|
3
|
-
use
|
|
1
|
+
use std::sync::Arc;
|
|
2
|
+
|
|
3
|
+
use anyhow::Context as AnyhowContext;
|
|
4
|
+
use neon::prelude::*;
|
|
4
5
|
use prost::Message;
|
|
5
|
-
use
|
|
6
|
-
use
|
|
6
|
+
use tokio::sync::mpsc::{Sender, channel};
|
|
7
|
+
use tokio_stream::wrappers::ReceiverStream;
|
|
8
|
+
|
|
7
9
|
use temporal_sdk_core::{
|
|
10
|
+
CoreRuntime,
|
|
8
11
|
api::{
|
|
9
|
-
errors::{CompleteActivityError, CompleteWfError, PollError},
|
|
10
12
|
Worker as CoreWorkerTrait,
|
|
13
|
+
errors::{CompleteActivityError, CompleteWfError, PollError},
|
|
11
14
|
},
|
|
15
|
+
init_replay_worker, init_worker,
|
|
12
16
|
protos::{
|
|
13
17
|
coresdk::{
|
|
14
|
-
|
|
15
|
-
|
|
18
|
+
ActivityHeartbeat, ActivityTaskCompletion,
|
|
19
|
+
workflow_completion::WorkflowActivationCompletion,
|
|
16
20
|
},
|
|
17
21
|
temporal::api::history::v1::History,
|
|
18
22
|
},
|
|
19
|
-
|
|
23
|
+
replay::{HistoryForReplay, ReplayWorkerInput},
|
|
20
24
|
};
|
|
21
|
-
use tokio::sync::mpsc::{unbounded_channel, UnboundedSender};
|
|
22
|
-
use tokio_stream::wrappers::UnboundedReceiverStream;
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
use bridge_macros::js_function;
|
|
27
|
+
|
|
28
|
+
use crate::{
|
|
29
|
+
client::Client,
|
|
30
|
+
enter_sync,
|
|
31
|
+
helpers::{handles::MutableFinalize, *},
|
|
32
|
+
runtime::{Runtime, RuntimeExt},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
pub fn init(cx: &mut ModuleContext) -> NeonResult<()> {
|
|
36
|
+
cx.export_function("newWorker", worker_new)?;
|
|
37
|
+
cx.export_function("workerValidate", worker_validate)?;
|
|
38
|
+
|
|
39
|
+
cx.export_function(
|
|
40
|
+
"workerPollWorkflowActivation",
|
|
41
|
+
worker_poll_workflow_activation,
|
|
42
|
+
)?;
|
|
43
|
+
cx.export_function(
|
|
44
|
+
"workerCompleteWorkflowActivation",
|
|
45
|
+
worker_complete_workflow_activation,
|
|
46
|
+
)?;
|
|
47
|
+
|
|
48
|
+
cx.export_function("workerPollActivityTask", worker_poll_activity_task)?;
|
|
49
|
+
cx.export_function("workerCompleteActivityTask", worker_complete_activity_task)?;
|
|
50
|
+
cx.export_function(
|
|
51
|
+
"workerRecordActivityHeartbeat",
|
|
52
|
+
worker_record_activity_heartbeat,
|
|
53
|
+
)?;
|
|
54
|
+
|
|
55
|
+
cx.export_function("workerInitiateShutdown", worker_initiate_shutdown)?;
|
|
56
|
+
cx.export_function("workerFinalizeShutdown", worker_finalize_shutdown)?;
|
|
57
|
+
|
|
58
|
+
// Replay worker functions
|
|
59
|
+
cx.export_function("newReplayWorker", replay_worker_new)?;
|
|
60
|
+
cx.export_function("pushHistory", push_history)?;
|
|
61
|
+
cx.export_function("closeHistoryStream", close_history_stream)?;
|
|
62
|
+
|
|
63
|
+
Ok(())
|
|
28
64
|
}
|
|
29
65
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
/// A request to complete a single activity task
|
|
59
|
-
CompleteActivityTask {
|
|
60
|
-
completion: ActivityTaskCompletion,
|
|
61
|
-
/// Used to send the result back into JS
|
|
62
|
-
callback: Root<JsFunction>,
|
|
63
|
-
},
|
|
64
|
-
/// A request to send a heartbeat from a running activity
|
|
65
|
-
RecordActivityHeartbeat { heartbeat: ActivityHeartbeat },
|
|
66
|
+
pub struct Worker {
|
|
67
|
+
core_runtime: Arc<CoreRuntime>,
|
|
68
|
+
|
|
69
|
+
// Arc so that we can send reference into async closures
|
|
70
|
+
core_worker: Arc<temporal_sdk_core::Worker>,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/// Create a new worker.
|
|
74
|
+
#[js_function]
|
|
75
|
+
pub fn worker_new(
|
|
76
|
+
client: OpaqueInboundHandle<Client>,
|
|
77
|
+
worker_options: config::BridgeWorkerOptions,
|
|
78
|
+
) -> BridgeResult<OpaqueOutboundHandle<Worker>> {
|
|
79
|
+
let config = worker_options
|
|
80
|
+
.into_core_config()
|
|
81
|
+
.context("Failed to convert WorkerOptions to CoreWorkerConfig")?;
|
|
82
|
+
|
|
83
|
+
let client_ref = client.borrow()?;
|
|
84
|
+
let client = client_ref.core_client.clone();
|
|
85
|
+
let runtime = client_ref.core_runtime.clone();
|
|
86
|
+
|
|
87
|
+
enter_sync!(runtime);
|
|
88
|
+
let worker = init_worker(&runtime, config, client).context("Failed to initialize worker")?;
|
|
89
|
+
|
|
90
|
+
Ok(OpaqueOutboundHandle::new(Worker {
|
|
91
|
+
core_runtime: runtime,
|
|
92
|
+
core_worker: Arc::new(worker),
|
|
93
|
+
}))
|
|
66
94
|
}
|
|
67
95
|
|
|
68
|
-
///
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
worker
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
96
|
+
/// Validate a worker.
|
|
97
|
+
#[js_function]
|
|
98
|
+
pub fn worker_validate(worker: OpaqueInboundHandle<Worker>) -> BridgeResult<BridgeFuture<()>> {
|
|
99
|
+
let worker_ref = worker.borrow()?;
|
|
100
|
+
let worker = worker_ref.core_worker.clone();
|
|
101
|
+
let runtime = worker_ref.core_runtime.clone();
|
|
102
|
+
|
|
103
|
+
runtime.future_to_promise(async move {
|
|
104
|
+
worker
|
|
105
|
+
.validate()
|
|
106
|
+
.await
|
|
107
|
+
.map_err(|err| BridgeError::TransportError(err.to_string()))
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/// Initiate a single workflow activation poll request.
|
|
112
|
+
/// There should be only one concurrent poll request for this type.
|
|
113
|
+
#[js_function]
|
|
114
|
+
pub fn worker_poll_workflow_activation(
|
|
115
|
+
worker: OpaqueInboundHandle<Worker>,
|
|
116
|
+
) -> BridgeResult<BridgeFuture<Vec<u8>>> {
|
|
117
|
+
let worker_ref = worker.borrow()?;
|
|
118
|
+
let worker = worker_ref.core_worker.clone();
|
|
119
|
+
let runtime = worker_ref.core_runtime.clone();
|
|
120
|
+
|
|
121
|
+
runtime.future_to_promise(async move {
|
|
122
|
+
let result = worker.poll_workflow_activation().await;
|
|
123
|
+
|
|
124
|
+
match result {
|
|
125
|
+
Ok(task) => Ok(task.encode_to_vec()),
|
|
126
|
+
Err(err) => match err {
|
|
127
|
+
PollError::ShutDown => Err(BridgeError::WorkerShutdown)?,
|
|
128
|
+
PollError::TonicError(status) => {
|
|
129
|
+
Err(BridgeError::TransportError(status.message().to_string()))?
|
|
130
|
+
}
|
|
131
|
+
},
|
|
83
132
|
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
WorkerRequest::CompleteWorkflowActivation {
|
|
118
|
-
completion,
|
|
119
|
-
callback,
|
|
120
|
-
} => {
|
|
121
|
-
void_future_to_js(
|
|
122
|
-
channel,
|
|
123
|
-
callback,
|
|
124
|
-
async move { worker.complete_workflow_activation(completion).await },
|
|
125
|
-
|cx, err| -> JsResult<JsObject> {
|
|
126
|
-
match err {
|
|
127
|
-
CompleteWfError::MalformedWorkflowCompletion {
|
|
128
|
-
reason, ..
|
|
129
|
-
} => Ok(JsError::type_error(cx, reason)?.upcast()),
|
|
130
|
-
}
|
|
131
|
-
},
|
|
132
|
-
)
|
|
133
|
-
.await;
|
|
134
|
-
}
|
|
135
|
-
WorkerRequest::CompleteActivityTask {
|
|
136
|
-
completion,
|
|
137
|
-
callback,
|
|
138
|
-
} => {
|
|
139
|
-
void_future_to_js(
|
|
140
|
-
channel,
|
|
141
|
-
callback,
|
|
142
|
-
async move { worker.complete_activity_task(completion).await },
|
|
143
|
-
|cx, err| -> JsResult<JsObject> {
|
|
144
|
-
match err {
|
|
145
|
-
CompleteActivityError::MalformedActivityCompletion {
|
|
146
|
-
reason,
|
|
147
|
-
..
|
|
148
|
-
} => Ok(JsError::type_error(cx, reason)?.upcast()),
|
|
149
|
-
}
|
|
150
|
-
},
|
|
151
|
-
)
|
|
152
|
-
.await;
|
|
153
|
-
}
|
|
154
|
-
WorkerRequest::RecordActivityHeartbeat { heartbeat } => {
|
|
155
|
-
worker.record_activity_heartbeat(heartbeat)
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/// Submit a workflow activation completion to core.
|
|
137
|
+
#[js_function]
|
|
138
|
+
pub fn worker_complete_workflow_activation(
|
|
139
|
+
worker: OpaqueInboundHandle<Worker>,
|
|
140
|
+
completion: Vec<u8>,
|
|
141
|
+
) -> BridgeResult<BridgeFuture<()>> {
|
|
142
|
+
let workflow_completion = WorkflowActivationCompletion::decode_length_delimited(
|
|
143
|
+
completion.as_slice(),
|
|
144
|
+
)
|
|
145
|
+
.map_err(|err| BridgeError::TypeError {
|
|
146
|
+
field: None,
|
|
147
|
+
message: format!("Cannot decode Completion from buffer: {err:?}"),
|
|
148
|
+
})?;
|
|
149
|
+
|
|
150
|
+
let worker_ref = worker.borrow()?;
|
|
151
|
+
let worker = worker_ref.core_worker.clone();
|
|
152
|
+
let runtime = worker_ref.core_runtime.clone();
|
|
153
|
+
|
|
154
|
+
runtime.future_to_promise(async move {
|
|
155
|
+
worker
|
|
156
|
+
.complete_workflow_activation(workflow_completion)
|
|
157
|
+
.await
|
|
158
|
+
.map_err(|err| match err {
|
|
159
|
+
CompleteWfError::MalformedWorkflowCompletion { reason, run_id } => {
|
|
160
|
+
BridgeError::TypeError {
|
|
161
|
+
field: None,
|
|
162
|
+
message: format!(
|
|
163
|
+
"Malformed Workflow Completion: {reason:?} for RunID={run_id}"
|
|
164
|
+
),
|
|
156
165
|
}
|
|
157
166
|
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
.await;
|
|
161
|
-
worker.finalize_shutdown().await;
|
|
167
|
+
})
|
|
168
|
+
})
|
|
162
169
|
}
|
|
163
170
|
|
|
164
|
-
///
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
) {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
PollError::TonicError(_) => make_named_error_from_error(cx, TRANSPORT_ERROR, err),
|
|
186
|
-
});
|
|
171
|
+
/// Initiate a single activity task poll request.
|
|
172
|
+
/// There should be only one concurrent poll request for this type.
|
|
173
|
+
#[js_function]
|
|
174
|
+
pub fn worker_poll_activity_task(
|
|
175
|
+
worker: OpaqueInboundHandle<Worker>,
|
|
176
|
+
) -> BridgeResult<BridgeFuture<Vec<u8>>> {
|
|
177
|
+
let worker_ref = worker.borrow()?;
|
|
178
|
+
let worker = worker_ref.core_worker.clone();
|
|
179
|
+
let runtime = worker_ref.core_runtime.clone();
|
|
180
|
+
|
|
181
|
+
runtime.future_to_promise(async move {
|
|
182
|
+
let result = worker.poll_activity_task().await;
|
|
183
|
+
|
|
184
|
+
match result {
|
|
185
|
+
Ok(task) => Ok(task.encode_to_vec()),
|
|
186
|
+
Err(err) => match err {
|
|
187
|
+
PollError::ShutDown => Err(BridgeError::WorkerShutdown)?,
|
|
188
|
+
PollError::TonicError(status) => {
|
|
189
|
+
Err(BridgeError::TransportError(status.message().to_string()))?
|
|
190
|
+
}
|
|
191
|
+
},
|
|
187
192
|
}
|
|
188
|
-
}
|
|
193
|
+
})
|
|
189
194
|
}
|
|
190
195
|
|
|
191
|
-
///
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
196
|
+
/// Submit an activity task completion to core.
|
|
197
|
+
#[js_function]
|
|
198
|
+
pub fn worker_complete_activity_task(
|
|
199
|
+
worker: OpaqueInboundHandle<Worker>,
|
|
200
|
+
completion: Vec<u8>,
|
|
201
|
+
) -> BridgeResult<BridgeFuture<()>> {
|
|
202
|
+
let activity_completion =
|
|
203
|
+
ActivityTaskCompletion::decode_length_delimited(completion.as_slice()).map_err(|err| {
|
|
204
|
+
BridgeError::TypeError {
|
|
205
|
+
field: None,
|
|
206
|
+
message: format!("Cannot decode Completion from buffer: {err:?}"),
|
|
207
|
+
}
|
|
208
|
+
})?;
|
|
209
|
+
|
|
210
|
+
let worker_ref = worker.borrow()?;
|
|
211
|
+
let worker = worker_ref.core_worker.clone();
|
|
212
|
+
let runtime = worker_ref.core_runtime.clone();
|
|
213
|
+
|
|
214
|
+
runtime.future_to_promise(async move {
|
|
215
|
+
worker
|
|
216
|
+
.complete_activity_task(activity_completion)
|
|
217
|
+
.await
|
|
218
|
+
.map_err(|err| match err {
|
|
219
|
+
CompleteActivityError::MalformedActivityCompletion {
|
|
220
|
+
reason,
|
|
221
|
+
completion: _,
|
|
222
|
+
} => BridgeError::TypeError {
|
|
223
|
+
field: None,
|
|
224
|
+
message: format!("Malformed Activity Completion: {reason:?}"),
|
|
225
|
+
},
|
|
226
|
+
})
|
|
227
|
+
})
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/// Submit an activity heartbeat to core.
|
|
231
|
+
#[js_function]
|
|
232
|
+
pub fn worker_record_activity_heartbeat(
|
|
233
|
+
worker: OpaqueInboundHandle<Worker>,
|
|
234
|
+
heartbeat: Vec<u8>,
|
|
235
|
+
) -> BridgeResult<()> {
|
|
236
|
+
let activity_heartbeat = ActivityHeartbeat::decode_length_delimited(heartbeat.as_slice())
|
|
237
|
+
.map_err(|err| BridgeError::TypeError {
|
|
238
|
+
field: None,
|
|
239
|
+
message: format!("Cannot decode Heartbeat from buffer: {err:?}"),
|
|
240
|
+
})?;
|
|
241
|
+
|
|
242
|
+
let worker_ref = worker.borrow()?;
|
|
243
|
+
worker_ref
|
|
244
|
+
.core_worker
|
|
245
|
+
.record_activity_heartbeat(activity_heartbeat);
|
|
246
|
+
|
|
247
|
+
Ok(())
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/// Request shutdown of the worker.
|
|
251
|
+
/// Once complete Core will stop polling on new tasks and activations on worker's task queue.
|
|
252
|
+
/// Caller should drain any pending tasks and activations and call worker_finalize_shutdown before breaking from
|
|
253
|
+
/// the loop to ensure graceful shutdown.
|
|
254
|
+
#[js_function]
|
|
255
|
+
pub fn worker_initiate_shutdown(worker: OpaqueInboundHandle<Worker>) -> BridgeResult<()> {
|
|
256
|
+
let worker_ref = worker.borrow()?;
|
|
257
|
+
worker_ref.core_worker.initiate_shutdown();
|
|
258
|
+
Ok(())
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
#[js_function]
|
|
262
|
+
pub fn worker_finalize_shutdown(
|
|
263
|
+
worker: OpaqueInboundHandle<Worker>,
|
|
264
|
+
) -> BridgeResult<BridgeFuture<()>> {
|
|
265
|
+
let worker_ref = worker.take()?;
|
|
266
|
+
|
|
267
|
+
// We make supplementary copies of this arc in `worker_poll_*_task`, `worker_complete_*_task`, and
|
|
268
|
+
// similar functions. Lang is responsible to ensure that there is no outstanding calls to these
|
|
269
|
+
// functions by the time `finalize_shutdown` is called, in which case unwrapping the arc here is ok.
|
|
270
|
+
let worker = Arc::try_unwrap(worker_ref.core_worker).map_err(|arc| {
|
|
271
|
+
BridgeError::IllegalStateStillInUse {
|
|
272
|
+
what: "Worker",
|
|
273
|
+
details: Some(format!(
|
|
274
|
+
"Expected 1 reference, but got {}",
|
|
275
|
+
Arc::strong_count(&arc)
|
|
276
|
+
)),
|
|
214
277
|
}
|
|
278
|
+
})?;
|
|
279
|
+
|
|
280
|
+
worker_ref.core_runtime.future_to_promise(async move {
|
|
281
|
+
worker.finalize_shutdown().await;
|
|
282
|
+
Ok(())
|
|
283
|
+
})
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
impl MutableFinalize for Worker {
|
|
287
|
+
fn finalize_mut(self) {
|
|
288
|
+
self.core_runtime.spawn_and_forget(async move {
|
|
289
|
+
// We make supplementary copies of this arc in `worker_poll_*_task`, `worker_complete_*_task`, and
|
|
290
|
+
// similar functions. Lang is responsible to ensure that there is no outstanding calls to these
|
|
291
|
+
// functions by the time `finalize_shutdown` is called, in which case unwrapping the arc here is ok.
|
|
292
|
+
let worker = Arc::try_unwrap(self.core_worker).map_err(|arc| {
|
|
293
|
+
BridgeError::IllegalStateStillInUse {
|
|
294
|
+
what: "Worker",
|
|
295
|
+
details: Some(format!(
|
|
296
|
+
"Expected 1 reference, but got {}",
|
|
297
|
+
Arc::strong_count(&arc)
|
|
298
|
+
)),
|
|
299
|
+
}
|
|
300
|
+
})?;
|
|
301
|
+
|
|
302
|
+
worker.finalize_shutdown().await;
|
|
303
|
+
Ok(())
|
|
304
|
+
});
|
|
215
305
|
}
|
|
216
306
|
}
|
|
217
307
|
|
|
218
|
-
|
|
308
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
219
309
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
let worker_options = cx.argument::<JsObject>(1)?;
|
|
225
|
-
let callback = cx.argument::<JsFunction>(2)?;
|
|
310
|
+
pub struct HistoryForReplayTunnelHandle {
|
|
311
|
+
core_runtime: Arc<CoreRuntime>,
|
|
312
|
+
sender: Sender<HistoryForReplay>,
|
|
313
|
+
}
|
|
226
314
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
if let Err(err) = client.runtime.sender.send(request) {
|
|
239
|
-
callback_with_unexpected_error(&mut cx, callback, err)?;
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
};
|
|
315
|
+
impl HistoryForReplayTunnelHandle {
|
|
316
|
+
fn new(runtime: &Arc<CoreRuntime>) -> (Self, ReceiverStream<HistoryForReplay>) {
|
|
317
|
+
let (sender, rx) = channel(1);
|
|
318
|
+
(
|
|
319
|
+
Self {
|
|
320
|
+
core_runtime: Arc::clone(runtime),
|
|
321
|
+
sender,
|
|
322
|
+
},
|
|
323
|
+
ReceiverStream::new(rx),
|
|
324
|
+
)
|
|
325
|
+
}
|
|
243
326
|
|
|
244
|
-
|
|
327
|
+
pub(crate) fn get_chan(&self) -> Sender<HistoryForReplay> {
|
|
328
|
+
self.sender.clone()
|
|
329
|
+
}
|
|
245
330
|
}
|
|
246
331
|
|
|
332
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
333
|
+
|
|
247
334
|
/// Create a new replay worker asynchronously.
|
|
248
|
-
|
|
249
|
-
pub fn replay_worker_new(
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
335
|
+
#[js_function]
|
|
336
|
+
pub fn replay_worker_new(
|
|
337
|
+
runtime: OpaqueInboundHandle<Runtime>,
|
|
338
|
+
config: config::BridgeWorkerOptions,
|
|
339
|
+
) -> BridgeResult<(
|
|
340
|
+
OpaqueOutboundHandle<Worker>,
|
|
341
|
+
OpaqueOutboundHandle<HistoryForReplayTunnelHandle>,
|
|
342
|
+
)> {
|
|
343
|
+
let config = config
|
|
344
|
+
.into_core_config()
|
|
345
|
+
.context("Failed to convert WorkerOptions to CoreWorkerConfig")?;
|
|
346
|
+
|
|
347
|
+
let runtime = runtime.borrow()?.core_runtime.clone();
|
|
348
|
+
enter_sync!(runtime);
|
|
349
|
+
|
|
350
|
+
let (tunnel, stream) = HistoryForReplayTunnelHandle::new(&runtime);
|
|
351
|
+
|
|
352
|
+
let worker = init_replay_worker(ReplayWorkerInput::new(config, Box::pin(stream)))
|
|
353
|
+
.context("Failed to initialize replay worker")?;
|
|
354
|
+
|
|
355
|
+
let worker_handle = Worker {
|
|
356
|
+
core_runtime: runtime,
|
|
357
|
+
core_worker: Arc::new(worker),
|
|
262
358
|
};
|
|
263
359
|
|
|
264
|
-
Ok(
|
|
360
|
+
Ok((
|
|
361
|
+
OpaqueOutboundHandle::new(worker_handle),
|
|
362
|
+
OpaqueOutboundHandle::new(tunnel),
|
|
363
|
+
))
|
|
265
364
|
}
|
|
266
365
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
.runtime
|
|
279
|
-
.sender
|
|
280
|
-
.send(RuntimeRequest::PushReplayHistory {
|
|
281
|
-
tx: chan,
|
|
282
|
-
pushme: HistoryForReplay::new(hist, workflow_id),
|
|
283
|
-
callback: callback.root(&mut cx),
|
|
284
|
-
})
|
|
285
|
-
}) {
|
|
286
|
-
callback_with_unexpected_error(&mut cx, callback, e)?;
|
|
366
|
+
#[js_function]
|
|
367
|
+
pub fn push_history(
|
|
368
|
+
pusher: OpaqueInboundHandle<HistoryForReplayTunnelHandle>,
|
|
369
|
+
workflow_id: String,
|
|
370
|
+
history_binary: Vec<u8>,
|
|
371
|
+
) -> BridgeResult<BridgeFuture<()>> {
|
|
372
|
+
let history: History =
|
|
373
|
+
History::decode_length_delimited(history_binary.as_slice()).map_err(|err| {
|
|
374
|
+
BridgeError::TypeError {
|
|
375
|
+
field: None,
|
|
376
|
+
message: format!("Cannot decode History from buffer: {err:?}"),
|
|
287
377
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
378
|
+
})?;
|
|
379
|
+
let history = HistoryForReplay::new(history, workflow_id);
|
|
380
|
+
|
|
381
|
+
let pusher_ref = pusher.borrow()?;
|
|
382
|
+
let chan = pusher_ref.get_chan();
|
|
383
|
+
|
|
384
|
+
pusher_ref.core_runtime.future_to_promise(async move {
|
|
385
|
+
chan.send(history)
|
|
386
|
+
.await
|
|
387
|
+
.context("Error pushing history to replay worker")?;
|
|
388
|
+
Ok(())
|
|
389
|
+
})
|
|
292
390
|
}
|
|
293
391
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
pusher
|
|
297
|
-
|
|
392
|
+
#[js_function]
|
|
393
|
+
pub fn close_history_stream(
|
|
394
|
+
pusher: OpaqueInboundHandle<HistoryForReplayTunnelHandle>,
|
|
395
|
+
) -> BridgeResult<()> {
|
|
396
|
+
// Just drop the pusher's channel; there's actually no "close" method on the channel.
|
|
397
|
+
let _pusher_ref = pusher.take()?;
|
|
398
|
+
Ok(())
|
|
298
399
|
}
|
|
299
400
|
|
|
300
|
-
///
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
401
|
+
/// Let drop handle the cleanup.
|
|
402
|
+
impl MutableFinalize for HistoryForReplayTunnelHandle {}
|
|
403
|
+
|
|
404
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
405
|
+
|
|
406
|
+
mod config {
|
|
407
|
+
use std::{sync::Arc, time::Duration};
|
|
408
|
+
|
|
409
|
+
use temporal_sdk_core::{
|
|
410
|
+
ResourceBasedSlotsOptions, ResourceBasedSlotsOptionsBuilder, ResourceSlotOptions,
|
|
411
|
+
SlotSupplierOptions as CoreSlotSupplierOptions, TunerHolder, TunerHolderOptionsBuilder,
|
|
412
|
+
api::worker::{
|
|
413
|
+
ActivitySlotKind, LocalActivitySlotKind, PollerBehavior as CorePollerBehavior,
|
|
414
|
+
SlotKind, WorkerConfig, WorkerConfigBuilder, WorkerConfigBuilderError,
|
|
415
|
+
WorkerDeploymentOptions as CoreWorkerDeploymentOptions,
|
|
416
|
+
WorkerDeploymentVersion as CoreWorkerDeploymentVersion, WorkflowSlotKind,
|
|
417
|
+
},
|
|
418
|
+
protos::temporal::api::enums::v1::VersioningBehavior as CoreVersioningBehavior,
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
use super::custom_slot_supplier::CustomSlotSupplierOptions;
|
|
422
|
+
use crate::helpers::TryIntoJs;
|
|
423
|
+
use bridge_macros::TryFromJs;
|
|
424
|
+
use neon::context::Context;
|
|
425
|
+
use neon::object::Object;
|
|
426
|
+
use neon::prelude::JsResult;
|
|
427
|
+
use neon::types::JsObject;
|
|
428
|
+
use temporal_sdk_core::api::worker::WorkerVersioningStrategy;
|
|
429
|
+
|
|
430
|
+
#[derive(TryFromJs)]
|
|
431
|
+
pub struct BridgeWorkerOptions {
|
|
432
|
+
identity: String,
|
|
433
|
+
build_id: String,
|
|
434
|
+
use_versioning: bool,
|
|
435
|
+
worker_deployment_options: Option<WorkerDeploymentOptions>,
|
|
436
|
+
task_queue: String,
|
|
437
|
+
namespace: String,
|
|
438
|
+
tuner: WorkerTuner,
|
|
439
|
+
non_sticky_to_sticky_poll_ratio: f32,
|
|
440
|
+
workflow_task_poller_behavior: PollerBehavior,
|
|
441
|
+
activity_task_poller_behavior: PollerBehavior,
|
|
442
|
+
enable_non_local_activities: bool,
|
|
443
|
+
sticky_queue_schedule_to_start_timeout: Duration,
|
|
444
|
+
max_cached_workflows: usize,
|
|
445
|
+
max_heartbeat_throttle_interval: Duration,
|
|
446
|
+
default_heartbeat_throttle_interval: Duration,
|
|
447
|
+
max_activities_per_second: Option<f64>,
|
|
448
|
+
max_task_queue_activities_per_second: Option<f64>,
|
|
449
|
+
shutdown_grace_time: Option<Duration>,
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
#[derive(TryFromJs)]
|
|
453
|
+
pub enum PollerBehavior {
|
|
454
|
+
SimpleMaximum {
|
|
455
|
+
maximum: usize,
|
|
456
|
+
},
|
|
457
|
+
Autoscaling {
|
|
458
|
+
minimum: usize,
|
|
459
|
+
maximum: usize,
|
|
460
|
+
initial: usize,
|
|
461
|
+
},
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
#[derive(TryFromJs)]
|
|
465
|
+
pub struct WorkerDeploymentOptions {
|
|
466
|
+
version: WorkerDeploymentVersion,
|
|
467
|
+
use_worker_versioning: bool,
|
|
468
|
+
default_versioning_behavior: VersioningBehavior,
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
#[derive(TryFromJs)]
|
|
472
|
+
pub struct WorkerDeploymentVersion {
|
|
473
|
+
build_id: String,
|
|
474
|
+
deployment_name: String,
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
#[derive(TryFromJs)]
|
|
478
|
+
pub enum VersioningBehavior {
|
|
479
|
+
Pinned,
|
|
480
|
+
AutoUpgrade,
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
impl BridgeWorkerOptions {
|
|
484
|
+
pub(crate) fn into_core_config(self) -> Result<WorkerConfig, WorkerConfigBuilderError> {
|
|
485
|
+
// Set all other options
|
|
486
|
+
let mut builder = WorkerConfigBuilder::default();
|
|
487
|
+
builder
|
|
488
|
+
.client_identity_override(Some(self.identity))
|
|
489
|
+
.versioning_strategy({
|
|
490
|
+
if let Some(dopts) = self.worker_deployment_options {
|
|
491
|
+
WorkerVersioningStrategy::WorkerDeploymentBased(dopts.into())
|
|
492
|
+
} else if self.use_versioning {
|
|
493
|
+
WorkerVersioningStrategy::LegacyBuildIdBased {
|
|
494
|
+
build_id: self.build_id,
|
|
495
|
+
}
|
|
496
|
+
} else {
|
|
497
|
+
WorkerVersioningStrategy::None {
|
|
498
|
+
build_id: self.build_id,
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
})
|
|
502
|
+
.task_queue(self.task_queue)
|
|
503
|
+
.namespace(self.namespace)
|
|
504
|
+
.tuner(self.tuner.into_core_config()?)
|
|
505
|
+
.nonsticky_to_sticky_poll_ratio(self.non_sticky_to_sticky_poll_ratio)
|
|
506
|
+
.workflow_task_poller_behavior(self.workflow_task_poller_behavior)
|
|
507
|
+
.activity_task_poller_behavior(self.activity_task_poller_behavior)
|
|
508
|
+
.no_remote_activities(!self.enable_non_local_activities)
|
|
509
|
+
.sticky_queue_schedule_to_start_timeout(self.sticky_queue_schedule_to_start_timeout)
|
|
510
|
+
.max_cached_workflows(self.max_cached_workflows)
|
|
511
|
+
.max_heartbeat_throttle_interval(self.max_heartbeat_throttle_interval)
|
|
512
|
+
.default_heartbeat_throttle_interval(self.default_heartbeat_throttle_interval)
|
|
513
|
+
.max_task_queue_activities_per_second(self.max_task_queue_activities_per_second)
|
|
514
|
+
.max_worker_activities_per_second(self.max_activities_per_second)
|
|
515
|
+
.graceful_shutdown_period(self.shutdown_grace_time)
|
|
516
|
+
.build()
|
|
308
517
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
impl From<PollerBehavior> for CorePollerBehavior {
|
|
521
|
+
fn from(val: PollerBehavior) -> Self {
|
|
522
|
+
match val {
|
|
523
|
+
PollerBehavior::SimpleMaximum { maximum } => Self::SimpleMaximum(maximum),
|
|
524
|
+
PollerBehavior::Autoscaling {
|
|
525
|
+
minimum,
|
|
526
|
+
maximum,
|
|
527
|
+
initial,
|
|
528
|
+
} => Self::Autoscaling {
|
|
529
|
+
minimum,
|
|
530
|
+
maximum,
|
|
531
|
+
initial,
|
|
532
|
+
},
|
|
315
533
|
}
|
|
316
534
|
}
|
|
317
535
|
}
|
|
318
|
-
Ok(cx.undefined())
|
|
319
|
-
}
|
|
320
536
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Worker")?;
|
|
537
|
+
impl From<WorkerDeploymentOptions> for CoreWorkerDeploymentOptions {
|
|
538
|
+
fn from(val: WorkerDeploymentOptions) -> Self {
|
|
539
|
+
Self {
|
|
540
|
+
version: val.version.into(),
|
|
541
|
+
use_worker_versioning: val.use_worker_versioning,
|
|
542
|
+
default_versioning_behavior: Some(val.default_versioning_behavior.into()),
|
|
543
|
+
}
|
|
329
544
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
impl From<WorkerDeploymentVersion> for CoreWorkerDeploymentVersion {
|
|
548
|
+
fn from(val: WorkerDeploymentVersion) -> Self {
|
|
549
|
+
Self {
|
|
550
|
+
build_id: val.build_id,
|
|
551
|
+
deployment_name: val.deployment_name,
|
|
336
552
|
}
|
|
337
553
|
}
|
|
338
554
|
}
|
|
339
|
-
Ok(cx.undefined())
|
|
340
|
-
}
|
|
341
555
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
None => {
|
|
349
|
-
callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Worker")?;
|
|
556
|
+
impl From<CoreWorkerDeploymentVersion> for WorkerDeploymentVersion {
|
|
557
|
+
fn from(val: CoreWorkerDeploymentVersion) -> Self {
|
|
558
|
+
Self {
|
|
559
|
+
build_id: val.build_id,
|
|
560
|
+
deployment_name: val.deployment_name,
|
|
561
|
+
}
|
|
350
562
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
impl TryIntoJs for WorkerDeploymentVersion {
|
|
566
|
+
type Output = JsObject;
|
|
567
|
+
|
|
568
|
+
fn try_into_js<'cx>(self, cx: &mut impl Context<'cx>) -> JsResult<'cx, Self::Output> {
|
|
569
|
+
let obj = cx.empty_object();
|
|
570
|
+
let bid = self.build_id.try_into_js(cx)?;
|
|
571
|
+
obj.set(cx, "buildId", bid)?;
|
|
572
|
+
let dn = self.deployment_name.try_into_js(cx)?;
|
|
573
|
+
obj.set(cx, "deploymentName", dn)?;
|
|
574
|
+
Ok(obj)
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
impl From<VersioningBehavior> for CoreVersioningBehavior {
|
|
579
|
+
fn from(val: VersioningBehavior) -> Self {
|
|
580
|
+
match val {
|
|
581
|
+
VersioningBehavior::Pinned => Self::Pinned,
|
|
582
|
+
VersioningBehavior::AutoUpgrade => Self::AutoUpgrade,
|
|
365
583
|
}
|
|
366
584
|
}
|
|
367
|
-
}
|
|
368
|
-
Ok(cx.undefined())
|
|
369
|
-
}
|
|
585
|
+
}
|
|
370
586
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
587
|
+
#[derive(TryFromJs)]
|
|
588
|
+
#[allow(clippy::struct_field_names)]
|
|
589
|
+
pub(super) struct WorkerTuner {
|
|
590
|
+
workflow_task_slot_supplier: SlotSupplier<WorkflowSlotKind>,
|
|
591
|
+
activity_task_slot_supplier: SlotSupplier<ActivitySlotKind>,
|
|
592
|
+
local_activity_task_slot_supplier: SlotSupplier<LocalActivitySlotKind>,
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
impl WorkerTuner {
|
|
596
|
+
fn into_core_config(self) -> Result<Arc<TunerHolder>, String> {
|
|
597
|
+
let mut tuner_holder = TunerHolderOptionsBuilder::default();
|
|
598
|
+
let mut rbo = None;
|
|
599
|
+
|
|
600
|
+
tuner_holder.workflow_slot_options(
|
|
601
|
+
self.workflow_task_slot_supplier
|
|
602
|
+
.into_slot_supplier(&mut rbo),
|
|
603
|
+
);
|
|
604
|
+
tuner_holder.activity_slot_options(
|
|
605
|
+
self.activity_task_slot_supplier
|
|
606
|
+
.into_slot_supplier(&mut rbo),
|
|
607
|
+
);
|
|
608
|
+
tuner_holder.local_activity_slot_options(
|
|
609
|
+
self.local_activity_task_slot_supplier
|
|
610
|
+
.into_slot_supplier(&mut rbo),
|
|
611
|
+
);
|
|
612
|
+
if let Some(rbo) = rbo {
|
|
613
|
+
tuner_holder.resource_based_options(rbo);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
tuner_holder
|
|
617
|
+
.build_tuner_holder()
|
|
618
|
+
.map(Arc::new)
|
|
619
|
+
.map_err(|e| format!("Invalid tuner options: {e:?}"))
|
|
379
620
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
#[derive(TryFromJs)]
|
|
624
|
+
pub(super) enum SlotSupplier<SK: SlotKind + Send + Sync + 'static> {
|
|
625
|
+
FixedSize(FixedSizeSlotSupplierOptions),
|
|
626
|
+
ResourceBased(ResourceBasedSlotSupplierOptions),
|
|
627
|
+
Custom(CustomSlotSupplierOptions<SK>),
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
#[derive(TryFromJs)]
|
|
631
|
+
pub(super) struct FixedSizeSlotSupplierOptions {
|
|
632
|
+
num_slots: usize,
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
#[derive(TryFromJs)]
|
|
636
|
+
pub(super) struct ResourceBasedSlotSupplierOptions {
|
|
637
|
+
minimum_slots: usize,
|
|
638
|
+
maximum_slots: usize,
|
|
639
|
+
ramp_throttle: Duration,
|
|
640
|
+
tuner_options: ResourceBasedTunerOptions,
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
#[derive(TryFromJs)]
|
|
644
|
+
pub(super) struct ResourceBasedTunerOptions {
|
|
645
|
+
target_memory_usage: f64,
|
|
646
|
+
target_cpu_usage: f64,
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
impl<SK: SlotKind + Send + Sync + 'static> SlotSupplier<SK> {
|
|
650
|
+
fn into_slot_supplier(
|
|
651
|
+
self,
|
|
652
|
+
rbo: &mut Option<ResourceBasedSlotsOptions>,
|
|
653
|
+
) -> CoreSlotSupplierOptions<SK> {
|
|
654
|
+
match self {
|
|
655
|
+
Self::FixedSize(opts) => CoreSlotSupplierOptions::FixedSize {
|
|
656
|
+
slots: opts.num_slots,
|
|
657
|
+
},
|
|
658
|
+
Self::ResourceBased(opts) => {
|
|
659
|
+
*rbo = Some(
|
|
660
|
+
ResourceBasedSlotsOptionsBuilder::default()
|
|
661
|
+
.target_cpu_usage(opts.tuner_options.target_cpu_usage)
|
|
662
|
+
.target_mem_usage(opts.tuner_options.target_memory_usage)
|
|
663
|
+
.build()
|
|
664
|
+
.expect("Building ResourceBasedSlotsOptions can't fail"),
|
|
665
|
+
);
|
|
666
|
+
CoreSlotSupplierOptions::ResourceBased(ResourceSlotOptions::new(
|
|
667
|
+
opts.minimum_slots,
|
|
668
|
+
opts.maximum_slots,
|
|
669
|
+
opts.ramp_throttle,
|
|
670
|
+
))
|
|
390
671
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
672
|
+
Self::Custom(opts) => CoreSlotSupplierOptions::Custom(Arc::new(
|
|
673
|
+
super::custom_slot_supplier::SlotSupplierBridge::new(opts),
|
|
674
|
+
)),
|
|
394
675
|
}
|
|
395
676
|
}
|
|
396
|
-
}
|
|
397
|
-
Ok(cx.undefined())
|
|
677
|
+
}
|
|
398
678
|
}
|
|
399
679
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
680
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
681
|
+
|
|
682
|
+
mod custom_slot_supplier {
|
|
683
|
+
use std::{marker::PhantomData, sync::Arc};
|
|
684
|
+
|
|
685
|
+
use neon::{context::Context, handle::Handle, prelude::*};
|
|
686
|
+
|
|
687
|
+
use temporal_sdk_core::{
|
|
688
|
+
SlotSupplierOptions as CoreSlotSupplierOptions,
|
|
689
|
+
api::worker::{
|
|
690
|
+
SlotInfo as CoreSlotInfo, SlotInfoTrait as _, SlotKind,
|
|
691
|
+
SlotKindType as CoreSlotKindType, SlotMarkUsedContext as CoreSlotMarkUsedContext,
|
|
692
|
+
SlotReleaseContext as CoreSlotReleaseContext,
|
|
693
|
+
SlotReservationContext as CoreSlotReservationContext, SlotSupplier as CoreSlotSupplier,
|
|
694
|
+
SlotSupplierPermit as CoreSlotSupplierPermit,
|
|
695
|
+
},
|
|
696
|
+
};
|
|
697
|
+
|
|
698
|
+
use bridge_macros::{TryFromJs, TryIntoJs};
|
|
699
|
+
use tracing::warn;
|
|
700
|
+
|
|
701
|
+
use crate::helpers::*;
|
|
702
|
+
use crate::worker::config::WorkerDeploymentVersion;
|
|
703
|
+
// Custom Slot Supplier ////////////////////////////////////////////////////////////////////////////
|
|
704
|
+
|
|
705
|
+
pub(super) struct SlotSupplierBridge<SK: SlotKind + Send + Sync + 'static> {
|
|
706
|
+
options: CustomSlotSupplierOptions<SK>,
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
impl<SK: SlotKind + Send + Sync + 'static> SlotSupplierBridge<SK> {
|
|
710
|
+
pub(crate) const fn new(options: CustomSlotSupplierOptions<SK>) -> Self {
|
|
711
|
+
Self { options }
|
|
408
712
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
#[async_trait::async_trait]
|
|
716
|
+
impl<SK: SlotKind + Send + Sync + 'static> CoreSlotSupplier for SlotSupplierBridge<SK> {
|
|
717
|
+
type SlotKind = SK;
|
|
718
|
+
|
|
719
|
+
async fn reserve_slot(
|
|
720
|
+
&self,
|
|
721
|
+
ctx: &dyn CoreSlotReservationContext,
|
|
722
|
+
) -> CoreSlotSupplierPermit {
|
|
723
|
+
loop {
|
|
724
|
+
let reserve_ctx = SlotReserveContext {
|
|
725
|
+
slot_type: SK::kind().into(),
|
|
726
|
+
task_queue: ctx.task_queue().to_string(),
|
|
727
|
+
worker_identity: ctx.worker_identity().to_string(),
|
|
728
|
+
worker_deployment_version: ctx
|
|
729
|
+
.worker_deployment_version()
|
|
730
|
+
.clone()
|
|
731
|
+
.map(Into::into),
|
|
732
|
+
is_sticky: ctx.is_sticky(),
|
|
733
|
+
};
|
|
734
|
+
|
|
735
|
+
let abort_controller = AbortController::new("Request Cancelled".to_string());
|
|
736
|
+
|
|
737
|
+
let permit_result = self
|
|
738
|
+
.options
|
|
739
|
+
.reserve_slot
|
|
740
|
+
.call((reserve_ctx, abort_controller.get_signal()))
|
|
741
|
+
.await;
|
|
742
|
+
|
|
743
|
+
// The call has already returned; it's no longer pertinent to trigger abort on drop
|
|
744
|
+
abort_controller.disarm();
|
|
745
|
+
|
|
746
|
+
match permit_result {
|
|
747
|
+
Ok(permit) => {
|
|
748
|
+
return CoreSlotSupplierPermit::with_user_data(BridgePermitData {
|
|
749
|
+
permit: Arc::new(permit),
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
Err(err) => {
|
|
753
|
+
warn!("Error reserving slot: {err:?}");
|
|
754
|
+
tokio::time::sleep(std::time::Duration::from_millis(1000)).await;
|
|
755
|
+
continue;
|
|
756
|
+
}
|
|
415
757
|
}
|
|
416
758
|
}
|
|
417
|
-
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
fn try_reserve_slot(
|
|
762
|
+
&self,
|
|
763
|
+
ctx: &dyn CoreSlotReservationContext,
|
|
764
|
+
) -> Option<CoreSlotSupplierPermit> {
|
|
765
|
+
let tokio_runtime = tokio::runtime::Handle::current();
|
|
766
|
+
let _entered = tokio_runtime.enter();
|
|
767
|
+
|
|
768
|
+
let reserve_ctx = SlotReserveContext {
|
|
769
|
+
slot_type: SK::kind().into(),
|
|
770
|
+
task_queue: ctx.task_queue().to_string(),
|
|
771
|
+
worker_identity: ctx.worker_identity().to_string(),
|
|
772
|
+
worker_deployment_version: ctx.worker_deployment_version().clone().map(Into::into),
|
|
773
|
+
is_sticky: ctx.is_sticky(),
|
|
774
|
+
};
|
|
775
|
+
|
|
776
|
+
// Try to reserve slot synchronously
|
|
777
|
+
let result = self.options.try_reserve_slot.call_and_block((reserve_ctx,));
|
|
778
|
+
|
|
779
|
+
match result {
|
|
780
|
+
Ok(res) => res.map(|permit| {
|
|
781
|
+
CoreSlotSupplierPermit::with_user_data(BridgePermitData {
|
|
782
|
+
permit: Arc::new(permit),
|
|
783
|
+
})
|
|
784
|
+
}),
|
|
785
|
+
Err(err) => {
|
|
786
|
+
warn!("Error reserving {} slot: {:?}", SK::kind(), err);
|
|
787
|
+
None
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
fn mark_slot_used(&self, ctx: &dyn CoreSlotMarkUsedContext<SlotKind = Self::SlotKind>) {
|
|
793
|
+
let tokio_runtime = tokio::runtime::Handle::current();
|
|
794
|
+
let _entered = tokio_runtime.enter();
|
|
795
|
+
|
|
796
|
+
let permit_data = ctx
|
|
797
|
+
.permit()
|
|
798
|
+
.user_data::<BridgePermitData>()
|
|
799
|
+
.expect("Expected BridgePermitData in mark_slot_used");
|
|
800
|
+
|
|
801
|
+
let slot_info = SlotInfo::from(&ctx.info().downcast());
|
|
802
|
+
|
|
803
|
+
// Fire and forget call to mark_slot_used
|
|
804
|
+
let _ = self
|
|
805
|
+
.options
|
|
806
|
+
.mark_slot_used
|
|
807
|
+
.call_on_js_thread((SlotMarkUsedContext::<SK> {
|
|
808
|
+
slot_info,
|
|
809
|
+
permit: permit_data.permit.clone(),
|
|
810
|
+
_marker: PhantomData,
|
|
811
|
+
},));
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
fn release_slot(&self, ctx: &dyn CoreSlotReleaseContext<SlotKind = Self::SlotKind>) {
|
|
815
|
+
let tokio_runtime = tokio::runtime::Handle::current();
|
|
816
|
+
let _entered = tokio_runtime.enter();
|
|
817
|
+
|
|
818
|
+
let permit_data = ctx
|
|
819
|
+
.permit()
|
|
820
|
+
.user_data::<BridgePermitData>()
|
|
821
|
+
.expect("Expected BridgePermitData in release_slot");
|
|
822
|
+
|
|
823
|
+
let slot_info = ctx.info().map(|info| SlotInfo::from(&info.downcast()));
|
|
824
|
+
|
|
825
|
+
// Fire and forget call to release_slot
|
|
826
|
+
let _ = self
|
|
827
|
+
.options
|
|
828
|
+
.release_slot
|
|
829
|
+
.call_on_js_thread((SlotReleaseContext::<SK> {
|
|
830
|
+
slot_info,
|
|
831
|
+
permit: permit_data.permit.clone(),
|
|
832
|
+
_marker: PhantomData,
|
|
833
|
+
},));
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
#[derive(TryFromJs)]
|
|
838
|
+
pub(super) struct CustomSlotSupplierOptions<SK: SlotKind + Send + Sync + 'static> {
|
|
839
|
+
reserve_slot: JsAsyncCallback<(SlotReserveContext, AbortSignal), SlotPermitOpaqueData>,
|
|
840
|
+
try_reserve_slot: JsCallback<(SlotReserveContext,), Option<SlotPermitOpaqueData>>,
|
|
841
|
+
mark_slot_used: JsCallback<(SlotMarkUsedContext<SK>,), ()>,
|
|
842
|
+
release_slot: JsCallback<(SlotReleaseContext<SK>,), ()>,
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
impl<SK: SlotKind + Send + Sync + 'static> TryInto<CoreSlotSupplierOptions<SK>>
|
|
846
|
+
for CustomSlotSupplierOptions<SK>
|
|
847
|
+
{
|
|
848
|
+
type Error = BridgeError;
|
|
849
|
+
|
|
850
|
+
fn try_into(self) -> Result<CoreSlotSupplierOptions<SK>, Self::Error> {
|
|
851
|
+
Ok(CoreSlotSupplierOptions::Custom(Arc::new(
|
|
852
|
+
SlotSupplierBridge { options: self },
|
|
853
|
+
)))
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
#[derive(TryIntoJs)]
|
|
858
|
+
enum SlotInfo {
|
|
859
|
+
Workflow {
|
|
860
|
+
workflow_type: String,
|
|
861
|
+
is_sticky: bool,
|
|
418
862
|
},
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
}
|
|
863
|
+
Activity {
|
|
864
|
+
activity_type: String,
|
|
865
|
+
},
|
|
866
|
+
LocalActivity {
|
|
867
|
+
activity_type: String,
|
|
868
|
+
},
|
|
869
|
+
Nexus {
|
|
870
|
+
service: String,
|
|
871
|
+
operation: String,
|
|
872
|
+
},
|
|
873
|
+
}
|
|
422
874
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
875
|
+
impl<'a> From<&'a CoreSlotInfo<'a>> for SlotInfo {
|
|
876
|
+
fn from(info: &'a CoreSlotInfo<'a>) -> Self {
|
|
877
|
+
match info {
|
|
878
|
+
CoreSlotInfo::Workflow(info) => Self::Workflow {
|
|
879
|
+
workflow_type: info.workflow_type.to_string(),
|
|
880
|
+
is_sticky: info.is_sticky,
|
|
881
|
+
},
|
|
882
|
+
CoreSlotInfo::Activity(info) => Self::Activity {
|
|
883
|
+
activity_type: info.activity_type.to_string(),
|
|
884
|
+
},
|
|
885
|
+
CoreSlotInfo::LocalActivity(info) => Self::LocalActivity {
|
|
886
|
+
activity_type: info.activity_type.to_string(),
|
|
887
|
+
},
|
|
888
|
+
CoreSlotInfo::Nexus(info) => Self::Nexus {
|
|
889
|
+
service: info.service.to_string(),
|
|
890
|
+
operation: info.operation.to_string(),
|
|
891
|
+
},
|
|
892
|
+
}
|
|
433
893
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
#[derive(TryIntoJs)]
|
|
897
|
+
struct SlotReserveContext {
|
|
898
|
+
slot_type: SlotKindType,
|
|
899
|
+
task_queue: String,
|
|
900
|
+
worker_identity: String,
|
|
901
|
+
worker_deployment_version: Option<WorkerDeploymentVersion>,
|
|
902
|
+
is_sticky: bool,
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
#[derive(TryIntoJs)]
|
|
906
|
+
struct SlotMarkUsedContext<SK: SlotKind> {
|
|
907
|
+
slot_info: SlotInfo,
|
|
908
|
+
permit: Arc<SlotPermitOpaqueData>,
|
|
909
|
+
_marker: PhantomData<SK>,
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
#[derive(TryIntoJs)]
|
|
913
|
+
struct SlotReleaseContext<SK: SlotKind> {
|
|
914
|
+
slot_info: Option<SlotInfo>,
|
|
915
|
+
permit: Arc<SlotPermitOpaqueData>,
|
|
916
|
+
_marker: PhantomData<SK>,
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
enum SlotKindType {
|
|
920
|
+
Workflow,
|
|
921
|
+
Activity,
|
|
922
|
+
LocalActivity,
|
|
923
|
+
Nexus,
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
impl TryIntoJs for SlotKindType {
|
|
927
|
+
type Output = JsString;
|
|
928
|
+
fn try_into_js<'cx>(self, cx: &mut impl Context<'cx>) -> JsResult<'cx, JsString> {
|
|
929
|
+
let s = match self {
|
|
930
|
+
Self::Workflow => "workflow",
|
|
931
|
+
Self::Activity => "activity",
|
|
932
|
+
Self::LocalActivity => "local-activity",
|
|
933
|
+
Self::Nexus => "nexus",
|
|
440
934
|
};
|
|
935
|
+
Ok(cx.string(s))
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
impl From<CoreSlotKindType> for SlotKindType {
|
|
940
|
+
fn from(val: CoreSlotKindType) -> Self {
|
|
941
|
+
match val {
|
|
942
|
+
CoreSlotKindType::Workflow => Self::Workflow,
|
|
943
|
+
CoreSlotKindType::Activity => Self::Activity,
|
|
944
|
+
CoreSlotKindType::LocalActivity => Self::LocalActivity,
|
|
945
|
+
CoreSlotKindType::Nexus => Self::Nexus,
|
|
946
|
+
}
|
|
441
947
|
}
|
|
442
948
|
}
|
|
443
|
-
Ok(cx.undefined())
|
|
444
|
-
}
|
|
445
949
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
make_named_error_from_string(&mut cx, ILLEGAL_STATE_ERROR, "Worker already closed")
|
|
450
|
-
.and_then(|err| cx.throw(err))?;
|
|
950
|
+
/// `BridgePermitData` holds the data associated with a slot permit.
|
|
951
|
+
struct BridgePermitData {
|
|
952
|
+
permit: Arc<SlotPermitOpaqueData>,
|
|
451
953
|
}
|
|
452
954
|
|
|
453
|
-
|
|
955
|
+
/// An opaque handle to a root'd JS object.
|
|
956
|
+
///
|
|
957
|
+
/// Note that even though the public API allows `permit` to be any JS value, including
|
|
958
|
+
/// `undefined` or `null`, we may in fact only root JS _objects_ (including arrays and
|
|
959
|
+
/// functions, but not primitives). For that reason, we wrap the user's JS value in a
|
|
960
|
+
/// `JSObject`, and root that object instead.
|
|
961
|
+
struct SlotPermitOpaqueData(Root<JsObject>);
|
|
962
|
+
|
|
963
|
+
static PERMIT_DATA_FIELD: &str = "permit_data";
|
|
964
|
+
|
|
965
|
+
impl TryFromJs for SlotPermitOpaqueData {
|
|
966
|
+
fn try_from_js<'cx, 'b>(
|
|
967
|
+
cx: &mut impl Context<'cx>,
|
|
968
|
+
js_value: Handle<'b, JsValue>,
|
|
969
|
+
) -> BridgeResult<Self> {
|
|
970
|
+
let obj = cx.empty_object();
|
|
971
|
+
obj.set(cx, PERMIT_DATA_FIELD, js_value)?;
|
|
972
|
+
Ok(Self(obj.root(cx)))
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
impl TryIntoJs for Arc<SlotPermitOpaqueData> {
|
|
977
|
+
type Output = JsValue;
|
|
978
|
+
fn try_into_js<'a>(self, cx: &mut impl Context<'a>) -> JsResult<'a, JsValue> {
|
|
979
|
+
let obj = self.as_ref().0.to_inner(cx);
|
|
980
|
+
obj.get_value(cx, PERMIT_DATA_FIELD)
|
|
981
|
+
}
|
|
982
|
+
}
|
|
454
983
|
}
|