@temporalio/core-bridge 1.1.0 → 1.4.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 +765 -128
- package/Cargo.toml +2 -2
- package/common.js +7 -3
- package/index.d.ts +118 -5
- package/index.js +2 -6
- package/package.json +2 -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/scripts/build.js +4 -3
- package/sdk-core/.buildkite/docker/Dockerfile +2 -1
- package/sdk-core/.buildkite/pipeline.yml +2 -0
- package/sdk-core/.cargo/config.toml +1 -1
- package/sdk-core/ARCHITECTURE.md +2 -2
- package/sdk-core/README.md +12 -0
- package/sdk-core/bridge-ffi/Cargo.toml +2 -2
- package/sdk-core/bridge-ffi/src/lib.rs +2 -2
- package/sdk-core/client/Cargo.toml +7 -5
- package/sdk-core/client/src/lib.rs +354 -226
- package/sdk-core/client/src/metrics.rs +13 -11
- package/sdk-core/client/src/raw.rs +352 -107
- package/sdk-core/client/src/retry.rs +188 -147
- package/sdk-core/client/src/workflow_handle/mod.rs +1 -1
- package/sdk-core/core/Cargo.toml +28 -15
- package/sdk-core/core/src/core_tests/activity_tasks.rs +98 -33
- package/sdk-core/core/src/core_tests/child_workflows.rs +125 -3
- package/sdk-core/core/src/core_tests/local_activities.rs +6 -6
- package/sdk-core/core/src/core_tests/workers.rs +3 -2
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +70 -2
- package/sdk-core/core/src/ephemeral_server/mod.rs +515 -0
- package/sdk-core/core/src/lib.rs +62 -28
- package/sdk-core/core/src/pollers/mod.rs +2 -0
- package/sdk-core/core/src/pollers/poll_buffer.rs +4 -4
- package/sdk-core/core/src/replay/mod.rs +3 -3
- package/sdk-core/core/src/retry_logic.rs +10 -9
- package/sdk-core/core/src/telemetry/metrics.rs +48 -39
- package/sdk-core/core/src/telemetry/mod.rs +46 -12
- package/sdk-core/core/src/telemetry/prometheus_server.rs +17 -13
- package/sdk-core/core/src/test_help/mod.rs +18 -8
- package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +10 -10
- package/sdk-core/core/src/worker/activities/local_activities.rs +13 -13
- package/sdk-core/core/src/worker/activities.rs +6 -12
- package/sdk-core/core/src/worker/client/mocks.rs +1 -0
- package/sdk-core/core/src/worker/client.rs +193 -64
- package/sdk-core/core/src/worker/mod.rs +14 -19
- package/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -0
- package/sdk-core/core/src/worker/workflow/history_update.rs +5 -5
- package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +133 -85
- package/sdk-core/core/src/worker/workflow/machines/mod.rs +3 -2
- package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +160 -105
- package/sdk-core/core/src/worker/workflow/managed_run.rs +2 -1
- package/sdk-core/core/src/worker/workflow/mod.rs +62 -58
- package/sdk-core/core/src/worker/workflow/run_cache.rs +5 -3
- package/sdk-core/core/src/worker/workflow/workflow_stream.rs +7 -5
- package/sdk-core/core-api/Cargo.toml +3 -3
- package/sdk-core/core-api/src/errors.rs +3 -11
- package/sdk-core/core-api/src/worker.rs +7 -0
- package/sdk-core/protos/api_upstream/.buildkite/Dockerfile +1 -1
- package/sdk-core/protos/api_upstream/.github/CODEOWNERS +1 -1
- package/sdk-core/protos/api_upstream/.github/PULL_REQUEST_TEMPLATE.md +2 -6
- package/sdk-core/protos/api_upstream/.github/workflows/trigger-api-go-update.yml +29 -0
- package/sdk-core/protos/api_upstream/Makefile +2 -2
- package/sdk-core/protos/api_upstream/buf.yaml +1 -0
- package/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +86 -0
- package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +26 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +46 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +7 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +14 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +51 -0
- package/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +18 -0
- package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +57 -1
- package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +1 -3
- package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +4 -2
- package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +11 -0
- package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +23 -0
- package/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +46 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +1 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +172 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +30 -0
- package/sdk-core/protos/grpc/health/v1/health.proto +63 -0
- package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +18 -15
- package/sdk-core/protos/testsrv_upstream/Makefile +80 -0
- package/sdk-core/protos/testsrv_upstream/api-linter.yaml +38 -0
- package/sdk-core/protos/testsrv_upstream/buf.yaml +13 -0
- package/sdk-core/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +141 -0
- package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +63 -0
- package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +90 -0
- package/sdk-core/sdk/Cargo.toml +2 -2
- package/sdk-core/sdk/src/lib.rs +2 -2
- package/sdk-core/sdk/src/workflow_context/options.rs +36 -8
- package/sdk-core/sdk/src/workflow_context.rs +30 -6
- package/sdk-core/sdk/src/workflow_future.rs +4 -4
- package/sdk-core/sdk-core-protos/Cargo.toml +5 -5
- package/sdk-core/sdk-core-protos/build.rs +9 -1
- package/sdk-core/sdk-core-protos/src/history_builder.rs +6 -1
- package/sdk-core/sdk-core-protos/src/lib.rs +93 -32
- package/sdk-core/test-utils/Cargo.toml +3 -3
- package/sdk-core/test-utils/src/canned_histories.rs +58 -0
- package/sdk-core/test-utils/src/lib.rs +35 -12
- package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +128 -0
- package/sdk-core/tests/integ_tests/heartbeat_tests.rs +55 -5
- package/sdk-core/tests/integ_tests/polling_tests.rs +2 -1
- package/sdk-core/tests/integ_tests/queries_tests.rs +5 -5
- package/sdk-core/tests/integ_tests/visibility_tests.rs +93 -0
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +93 -10
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -1
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +14 -14
- package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +2 -6
- package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +12 -12
- package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +12 -1
- package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +3 -3
- package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +8 -2
- package/sdk-core/tests/integ_tests/workflow_tests.rs +19 -4
- package/sdk-core/tests/load_tests.rs +2 -1
- package/sdk-core/tests/main.rs +17 -0
- package/sdk-core/tests/runner.rs +93 -0
- package/src/conversions.rs +157 -94
- package/src/helpers.rs +190 -0
- package/src/lib.rs +10 -912
- package/src/runtime.rs +436 -0
- package/src/testing.rs +67 -0
- package/src/worker.rs +465 -0
package/src/lib.rs
CHANGED
|
@@ -1,919 +1,14 @@
|
|
|
1
1
|
mod conversions;
|
|
2
2
|
mod errors;
|
|
3
|
+
mod helpers;
|
|
4
|
+
mod runtime;
|
|
5
|
+
mod testing;
|
|
6
|
+
mod worker;
|
|
3
7
|
|
|
4
|
-
use crate::
|
|
5
|
-
use
|
|
6
|
-
use futures::stream::StreamExt;
|
|
8
|
+
use crate::runtime::*;
|
|
9
|
+
use crate::worker::*;
|
|
7
10
|
use neon::prelude::*;
|
|
8
|
-
use
|
|
9
|
-
use opentelemetry::trace::{FutureExt, SpanContext, TraceContextExt};
|
|
10
|
-
use parking_lot::RwLock;
|
|
11
|
-
use prost::Message;
|
|
12
|
-
use std::collections::HashMap;
|
|
13
|
-
use std::ops::Deref;
|
|
14
|
-
use std::{
|
|
15
|
-
cell::RefCell,
|
|
16
|
-
fmt::Display,
|
|
17
|
-
future::Future,
|
|
18
|
-
sync::Arc,
|
|
19
|
-
time::{Duration, SystemTime, UNIX_EPOCH},
|
|
20
|
-
};
|
|
21
|
-
use temporal_client::{
|
|
22
|
-
AnyClient, ClientInitError, ConfiguredClient, WorkflowServiceClientWithMetrics,
|
|
23
|
-
};
|
|
24
|
-
use temporal_sdk_core::{
|
|
25
|
-
api::{
|
|
26
|
-
errors::{CompleteActivityError, CompleteWfError, PollActivityError, PollWfError},
|
|
27
|
-
Worker as CoreWorkerTrait,
|
|
28
|
-
},
|
|
29
|
-
fetch_global_buffered_logs, init_replay_worker, init_worker,
|
|
30
|
-
protos::{
|
|
31
|
-
coresdk::{
|
|
32
|
-
workflow_completion::WorkflowActivationCompletion, ActivityHeartbeat,
|
|
33
|
-
ActivityTaskCompletion,
|
|
34
|
-
},
|
|
35
|
-
temporal::api::history::v1::History,
|
|
36
|
-
},
|
|
37
|
-
telemetry_init, ClientOptions, RetryClient, Worker as CoreWorker, WorkerConfig,
|
|
38
|
-
};
|
|
39
|
-
use tokio::{
|
|
40
|
-
runtime::Runtime,
|
|
41
|
-
sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
|
|
42
|
-
};
|
|
43
|
-
use tokio_stream::wrappers::UnboundedReceiverStream;
|
|
44
|
-
|
|
45
|
-
type RawClient = RetryClient<ConfiguredClient<WorkflowServiceClientWithMetrics>>;
|
|
46
|
-
|
|
47
|
-
/// A request from JS to bridge to core
|
|
48
|
-
enum RuntimeRequest {
|
|
49
|
-
/// A request to shutdown the runtime, breaks from the thread loop.
|
|
50
|
-
Shutdown {
|
|
51
|
-
/// Used to send the result back into JS
|
|
52
|
-
callback: Root<JsFunction>,
|
|
53
|
-
},
|
|
54
|
-
/// A request to create a client in a runtime
|
|
55
|
-
CreateClient {
|
|
56
|
-
runtime: Arc<RuntimeHandle>,
|
|
57
|
-
options: ClientOptions,
|
|
58
|
-
headers: Option<HashMap<String, String>>,
|
|
59
|
-
/// Used to send the result back into JS
|
|
60
|
-
callback: Root<JsFunction>,
|
|
61
|
-
},
|
|
62
|
-
/// A request to update a client's HTTP request headers
|
|
63
|
-
UpdateClientHeaders {
|
|
64
|
-
client: Arc<RawClient>,
|
|
65
|
-
headers: HashMap<String, String>,
|
|
66
|
-
/// Used to send the result back into JS
|
|
67
|
-
callback: Root<JsFunction>,
|
|
68
|
-
},
|
|
69
|
-
/// A request to create a new Worker using a connected client
|
|
70
|
-
InitWorker {
|
|
71
|
-
/// Worker configuration e.g. limits and task queue
|
|
72
|
-
config: WorkerConfig,
|
|
73
|
-
/// A client created with a [CreateClient] request
|
|
74
|
-
client: Arc<RawClient>,
|
|
75
|
-
/// Used to send the result back into JS
|
|
76
|
-
callback: Root<JsFunction>,
|
|
77
|
-
},
|
|
78
|
-
/// A request to register a replay worker
|
|
79
|
-
InitReplayWorker {
|
|
80
|
-
/// Worker configuration. Must have unique task queue name.
|
|
81
|
-
config: WorkerConfig,
|
|
82
|
-
/// The history this worker should replay
|
|
83
|
-
history: History,
|
|
84
|
-
/// Used to send the result back into JS
|
|
85
|
-
callback: Root<JsFunction>,
|
|
86
|
-
},
|
|
87
|
-
/// A request to drain logs from core so they can be emitted in node
|
|
88
|
-
PollLogs {
|
|
89
|
-
/// Logs are sent to this function
|
|
90
|
-
callback: Root<JsFunction>,
|
|
91
|
-
},
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
#[derive(Debug)]
|
|
95
|
-
enum WorkerRequest {
|
|
96
|
-
/// A request to shutdown a worker, the worker instance will remain active to
|
|
97
|
-
/// allow draining of pending tasks
|
|
98
|
-
InitiateShutdown {
|
|
99
|
-
/// Used to send the result back into JS
|
|
100
|
-
callback: Root<JsFunction>,
|
|
101
|
-
},
|
|
102
|
-
/// A request to poll for workflow activations
|
|
103
|
-
PollWorkflowActivation {
|
|
104
|
-
otel_span: SpanContext,
|
|
105
|
-
/// Used to send the result back into JS
|
|
106
|
-
callback: Root<JsFunction>,
|
|
107
|
-
},
|
|
108
|
-
/// A request to complete a single workflow activation
|
|
109
|
-
CompleteWorkflowActivation {
|
|
110
|
-
completion: WorkflowActivationCompletion,
|
|
111
|
-
otel_span: SpanContext,
|
|
112
|
-
/// Used to send the result back into JS
|
|
113
|
-
callback: Root<JsFunction>,
|
|
114
|
-
},
|
|
115
|
-
/// A request to poll for activity tasks
|
|
116
|
-
PollActivityTask {
|
|
117
|
-
otel_span: SpanContext,
|
|
118
|
-
/// Used to report completion or error back into JS
|
|
119
|
-
callback: Root<JsFunction>,
|
|
120
|
-
},
|
|
121
|
-
/// A request to complete a single activity task
|
|
122
|
-
CompleteActivityTask {
|
|
123
|
-
completion: ActivityTaskCompletion,
|
|
124
|
-
otel_span: SpanContext,
|
|
125
|
-
/// Used to send the result back into JS
|
|
126
|
-
callback: Root<JsFunction>,
|
|
127
|
-
},
|
|
128
|
-
/// A request to send a heartbeat from a running activity
|
|
129
|
-
RecordActivityHeartbeat { heartbeat: ActivityHeartbeat },
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
struct RuntimeHandle {
|
|
133
|
-
sender: UnboundedSender<RuntimeRequest>,
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/// Box it so we can use the runtime from JS
|
|
137
|
-
type BoxedRuntime = JsBox<Arc<RuntimeHandle>>;
|
|
138
|
-
impl Finalize for RuntimeHandle {}
|
|
139
|
-
|
|
140
|
-
#[derive(Clone)]
|
|
141
|
-
struct Client {
|
|
142
|
-
runtime: Arc<RuntimeHandle>,
|
|
143
|
-
core_client: Arc<RawClient>,
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
type BoxedClient = JsBox<RefCell<Option<Client>>>;
|
|
147
|
-
impl Finalize for Client {}
|
|
148
|
-
|
|
149
|
-
/// Worker struct, hold a reference for the channel sender responsible for sending requests from
|
|
150
|
-
/// JS to a bridge thread which forwards them to core
|
|
151
|
-
struct WorkerHandle {
|
|
152
|
-
sender: UnboundedSender<WorkerRequest>,
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/// Box it so we can use Worker from JS
|
|
156
|
-
type BoxedWorker = JsBox<RefCell<Option<WorkerHandle>>>;
|
|
157
|
-
impl Finalize for WorkerHandle {}
|
|
158
|
-
|
|
159
|
-
/// Inits a multi-threaded tokio runtime used to interact with sdk-core APIs
|
|
160
|
-
fn tokio_runtime() -> Runtime {
|
|
161
|
-
tokio::runtime::Builder::new_multi_thread()
|
|
162
|
-
.enable_all()
|
|
163
|
-
.thread_name("core")
|
|
164
|
-
.build()
|
|
165
|
-
.expect("Tokio runtime must construct properly")
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/// Send a result to JS via callback using a [Channel]
|
|
169
|
-
fn send_result<F, T>(channel: Arc<Channel>, callback: Root<JsFunction>, res_fn: F)
|
|
170
|
-
where
|
|
171
|
-
F: for<'a> FnOnce(&mut TaskContext<'a>) -> NeonResult<Handle<'a, T>> + Send + 'static,
|
|
172
|
-
T: Value,
|
|
173
|
-
{
|
|
174
|
-
channel.send(move |mut cx| {
|
|
175
|
-
let callback = callback.into_inner(&mut cx);
|
|
176
|
-
let this = cx.undefined();
|
|
177
|
-
let error = cx.undefined();
|
|
178
|
-
let result = res_fn(&mut cx)?;
|
|
179
|
-
let args: Vec<Handle<JsValue>> = vec![error.upcast(), result.upcast()];
|
|
180
|
-
callback.call(&mut cx, this, args)?;
|
|
181
|
-
Ok(())
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/// Send an error to JS via callback using a [Channel]
|
|
186
|
-
fn send_error<E, F>(channel: Arc<Channel>, callback: Root<JsFunction>, error_ctor: F)
|
|
187
|
-
where
|
|
188
|
-
E: Object,
|
|
189
|
-
F: for<'a> FnOnce(&mut TaskContext<'a>) -> JsResult<'a, E> + Send + 'static,
|
|
190
|
-
{
|
|
191
|
-
channel.send(move |mut cx| {
|
|
192
|
-
let callback = callback.into_inner(&mut cx);
|
|
193
|
-
callback_with_error(&mut cx, callback, error_ctor)
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/// Call `callback` with given error
|
|
198
|
-
fn callback_with_error<'a, C, E, F>(
|
|
199
|
-
cx: &mut C,
|
|
200
|
-
callback: Handle<JsFunction>,
|
|
201
|
-
error_ctor: F,
|
|
202
|
-
) -> NeonResult<()>
|
|
203
|
-
where
|
|
204
|
-
C: Context<'a>,
|
|
205
|
-
E: Object,
|
|
206
|
-
F: FnOnce(&mut C) -> JsResult<'a, E> + Send + 'static,
|
|
207
|
-
{
|
|
208
|
-
let this = cx.undefined();
|
|
209
|
-
let error = error_ctor(cx)?;
|
|
210
|
-
let result = cx.undefined();
|
|
211
|
-
let args: Vec<Handle<JsValue>> = vec![error.upcast(), result.upcast()];
|
|
212
|
-
callback.call(cx, this, args)?;
|
|
213
|
-
Ok(())
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/// Call `callback` with an UnexpectedError created from `err`
|
|
217
|
-
fn callback_with_unexpected_error<'a, C, E>(
|
|
218
|
-
cx: &mut C,
|
|
219
|
-
callback: Handle<JsFunction>,
|
|
220
|
-
err: E,
|
|
221
|
-
) -> NeonResult<()>
|
|
222
|
-
where
|
|
223
|
-
C: Context<'a>,
|
|
224
|
-
E: std::fmt::Display,
|
|
225
|
-
{
|
|
226
|
-
let err_str = format!("{}", err);
|
|
227
|
-
callback_with_error(cx, callback, move |cx| {
|
|
228
|
-
UNEXPECTED_ERROR.from_string(cx, err_str)
|
|
229
|
-
})
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/// When Future completes, call given JS callback using a neon::Channel with either error or
|
|
233
|
-
/// undefined
|
|
234
|
-
async fn void_future_to_js<E, F, ER, EF>(
|
|
235
|
-
channel: Arc<Channel>,
|
|
236
|
-
callback: Root<JsFunction>,
|
|
237
|
-
f: F,
|
|
238
|
-
error_function: EF,
|
|
239
|
-
) where
|
|
240
|
-
E: Display + Send + 'static,
|
|
241
|
-
F: Future<Output = Result<(), E>> + Send,
|
|
242
|
-
ER: Object,
|
|
243
|
-
EF: for<'a> FnOnce(&mut TaskContext<'a>, E) -> JsResult<'a, ER> + Send + 'static,
|
|
244
|
-
{
|
|
245
|
-
match f.await {
|
|
246
|
-
Ok(()) => {
|
|
247
|
-
send_result(channel, callback, |cx| Ok(cx.undefined()));
|
|
248
|
-
}
|
|
249
|
-
Err(err) => {
|
|
250
|
-
send_error(channel, callback, |cx| error_function(cx, err));
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/// Builds a tokio runtime and starts polling on [RuntimeRequest]s via an internal channel.
|
|
256
|
-
/// Bridges requests from JS to core and sends responses back to JS using a neon::Channel.
|
|
257
|
-
/// Blocks current thread until a [Shutdown] request is received in channel.
|
|
258
|
-
fn start_bridge_loop(channel: Arc<Channel>, receiver: &mut UnboundedReceiver<RuntimeRequest>) {
|
|
259
|
-
tokio_runtime().block_on(async {
|
|
260
|
-
loop {
|
|
261
|
-
let request_option = receiver.recv().await;
|
|
262
|
-
let request = match request_option {
|
|
263
|
-
None => break,
|
|
264
|
-
Some(request) => request,
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
let channel = channel.clone();
|
|
268
|
-
|
|
269
|
-
match request {
|
|
270
|
-
RuntimeRequest::Shutdown { callback } => {
|
|
271
|
-
send_result(channel, callback, |cx| Ok(cx.undefined()));
|
|
272
|
-
break;
|
|
273
|
-
}
|
|
274
|
-
RuntimeRequest::CreateClient {
|
|
275
|
-
runtime,
|
|
276
|
-
options,
|
|
277
|
-
headers,
|
|
278
|
-
callback,
|
|
279
|
-
} => {
|
|
280
|
-
// `metrics_meter` (second arg) can be None here since we don't use the
|
|
281
|
-
// returned client directly at the moment, when we repurpose the client to be
|
|
282
|
-
// used by a Worker, `init_worker` will attach the correct metrics meter for
|
|
283
|
-
// us.
|
|
284
|
-
tokio::spawn(async move {
|
|
285
|
-
match options
|
|
286
|
-
.connect_no_namespace(None, headers.map(|h| Arc::new(RwLock::new(h))))
|
|
287
|
-
.await
|
|
288
|
-
{
|
|
289
|
-
Err(err) => {
|
|
290
|
-
send_error(channel.clone(), callback, |cx| match err {
|
|
291
|
-
ClientInitError::SystemInfoCallError(e) => TRANSPORT_ERROR
|
|
292
|
-
.from_string(
|
|
293
|
-
cx,
|
|
294
|
-
format!("Failed to call GetSystemInfo: {}", e),
|
|
295
|
-
),
|
|
296
|
-
ClientInitError::TonicTransportError(e) => {
|
|
297
|
-
TRANSPORT_ERROR.from_error(cx, e)
|
|
298
|
-
}
|
|
299
|
-
ClientInitError::InvalidUri(e) => {
|
|
300
|
-
Ok(JsError::type_error(cx, format!("{}", e))?.upcast())
|
|
301
|
-
}
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
Ok(client) => {
|
|
305
|
-
send_result(channel.clone(), callback, |cx| {
|
|
306
|
-
Ok(cx.boxed(RefCell::new(Some(Client {
|
|
307
|
-
runtime,
|
|
308
|
-
core_client: Arc::new(client),
|
|
309
|
-
}))))
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
RuntimeRequest::UpdateClientHeaders {
|
|
316
|
-
client,
|
|
317
|
-
headers,
|
|
318
|
-
callback,
|
|
319
|
-
} => {
|
|
320
|
-
client.get_client().set_headers(headers);
|
|
321
|
-
send_result(channel.clone(), callback, |cx| Ok(cx.undefined()));
|
|
322
|
-
}
|
|
323
|
-
RuntimeRequest::PollLogs { callback } => {
|
|
324
|
-
let logs = fetch_global_buffered_logs();
|
|
325
|
-
send_result(channel.clone(), callback, |cx| {
|
|
326
|
-
let logarr = cx.empty_array();
|
|
327
|
-
for (i, cl) in logs.into_iter().enumerate() {
|
|
328
|
-
// Not much to do here except for panic when there's an
|
|
329
|
-
// error here.
|
|
330
|
-
let logobj = cx.empty_object();
|
|
331
|
-
let level = cx.string(cl.level.to_string());
|
|
332
|
-
logobj.set(cx, "level", level).unwrap();
|
|
333
|
-
let ts = system_time_to_js(cx, cl.timestamp).unwrap();
|
|
334
|
-
logobj.set(cx, "timestamp", ts).unwrap();
|
|
335
|
-
let msg = cx.string(cl.message);
|
|
336
|
-
logobj.set(cx, "message", msg).unwrap();
|
|
337
|
-
logarr.set(cx, i as u32, logobj).unwrap();
|
|
338
|
-
}
|
|
339
|
-
Ok(logarr)
|
|
340
|
-
});
|
|
341
|
-
}
|
|
342
|
-
RuntimeRequest::InitWorker {
|
|
343
|
-
config,
|
|
344
|
-
client,
|
|
345
|
-
callback,
|
|
346
|
-
} => {
|
|
347
|
-
let client = (*client).clone();
|
|
348
|
-
let worker =
|
|
349
|
-
init_worker(config, AnyClient::LowLevel(Box::new(client.into_inner())));
|
|
350
|
-
let (tx, rx) = unbounded_channel();
|
|
351
|
-
tokio::spawn(start_worker_loop(worker, rx, channel.clone()));
|
|
352
|
-
send_result(channel.clone(), callback, |cx| {
|
|
353
|
-
Ok(cx.boxed(RefCell::new(Some(WorkerHandle { sender: tx }))))
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
|
-
RuntimeRequest::InitReplayWorker {
|
|
357
|
-
config,
|
|
358
|
-
history,
|
|
359
|
-
callback,
|
|
360
|
-
} => {
|
|
361
|
-
match init_replay_worker(config, &history) {
|
|
362
|
-
Ok(worker) => {
|
|
363
|
-
let (tx, rx) = unbounded_channel();
|
|
364
|
-
tokio::spawn(start_worker_loop(worker, rx, channel.clone()));
|
|
365
|
-
send_result(channel.clone(), callback, |cx| {
|
|
366
|
-
Ok(cx.boxed(RefCell::new(Some(WorkerHandle { sender: tx }))))
|
|
367
|
-
})
|
|
368
|
-
}
|
|
369
|
-
Err(err) => send_error(channel.clone(), callback, move |cx| {
|
|
370
|
-
UNEXPECTED_ERROR.from_error(cx, err.deref())
|
|
371
|
-
}),
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
})
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
/// Polls on [WorkerRequest]s via given channel.
|
|
380
|
-
/// Bridges requests from JS to core and sends responses back to JS using a neon::Channel.
|
|
381
|
-
/// Returns when the given channel is dropped.
|
|
382
|
-
async fn start_worker_loop(
|
|
383
|
-
worker: CoreWorker,
|
|
384
|
-
rx: UnboundedReceiver<WorkerRequest>,
|
|
385
|
-
channel: Arc<Channel>,
|
|
386
|
-
) {
|
|
387
|
-
UnboundedReceiverStream::new(rx)
|
|
388
|
-
.for_each_concurrent(None, |request| {
|
|
389
|
-
let worker = &worker;
|
|
390
|
-
let channel = channel.clone();
|
|
391
|
-
async move {
|
|
392
|
-
match request {
|
|
393
|
-
WorkerRequest::InitiateShutdown { callback } => {
|
|
394
|
-
worker.initiate_shutdown();
|
|
395
|
-
send_result(channel, callback, |cx| Ok(cx.undefined()));
|
|
396
|
-
}
|
|
397
|
-
WorkerRequest::PollWorkflowActivation {
|
|
398
|
-
otel_span,
|
|
399
|
-
callback,
|
|
400
|
-
} => {
|
|
401
|
-
handle_poll_workflow_activation_request(
|
|
402
|
-
&worker, otel_span, channel, callback,
|
|
403
|
-
)
|
|
404
|
-
.await
|
|
405
|
-
}
|
|
406
|
-
WorkerRequest::PollActivityTask {
|
|
407
|
-
otel_span,
|
|
408
|
-
callback,
|
|
409
|
-
} => {
|
|
410
|
-
handle_poll_activity_task_request(&worker, otel_span, channel, callback)
|
|
411
|
-
.await
|
|
412
|
-
}
|
|
413
|
-
WorkerRequest::CompleteWorkflowActivation {
|
|
414
|
-
completion,
|
|
415
|
-
otel_span,
|
|
416
|
-
callback,
|
|
417
|
-
} => {
|
|
418
|
-
let otel_ctx =
|
|
419
|
-
opentelemetry::Context::new().with_remote_span_context(otel_span);
|
|
420
|
-
void_future_to_js(
|
|
421
|
-
channel,
|
|
422
|
-
callback,
|
|
423
|
-
async move {
|
|
424
|
-
worker
|
|
425
|
-
.complete_workflow_activation(completion)
|
|
426
|
-
.with_context(otel_ctx)
|
|
427
|
-
.await
|
|
428
|
-
},
|
|
429
|
-
|cx, err| match err {
|
|
430
|
-
CompleteWfError::TonicError(_) => {
|
|
431
|
-
TRANSPORT_ERROR.from_error(cx, err)
|
|
432
|
-
}
|
|
433
|
-
CompleteWfError::MalformedWorkflowCompletion { reason, .. } => {
|
|
434
|
-
Ok(JsError::type_error(cx, reason)?.upcast())
|
|
435
|
-
}
|
|
436
|
-
},
|
|
437
|
-
)
|
|
438
|
-
.await;
|
|
439
|
-
}
|
|
440
|
-
WorkerRequest::CompleteActivityTask {
|
|
441
|
-
completion,
|
|
442
|
-
otel_span,
|
|
443
|
-
callback,
|
|
444
|
-
} => {
|
|
445
|
-
let otel_ctx =
|
|
446
|
-
opentelemetry::Context::new().with_remote_span_context(otel_span);
|
|
447
|
-
void_future_to_js(
|
|
448
|
-
channel,
|
|
449
|
-
callback,
|
|
450
|
-
async move {
|
|
451
|
-
worker
|
|
452
|
-
.complete_activity_task(completion)
|
|
453
|
-
.with_context(otel_ctx)
|
|
454
|
-
.await
|
|
455
|
-
},
|
|
456
|
-
|cx, err| match err {
|
|
457
|
-
CompleteActivityError::MalformedActivityCompletion {
|
|
458
|
-
reason,
|
|
459
|
-
..
|
|
460
|
-
} => Ok(JsError::type_error(cx, reason)?.upcast()),
|
|
461
|
-
CompleteActivityError::TonicError(_) => {
|
|
462
|
-
TRANSPORT_ERROR.from_error(cx, err)
|
|
463
|
-
}
|
|
464
|
-
},
|
|
465
|
-
)
|
|
466
|
-
.await;
|
|
467
|
-
}
|
|
468
|
-
WorkerRequest::RecordActivityHeartbeat { heartbeat } => {
|
|
469
|
-
worker.record_activity_heartbeat(heartbeat)
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
})
|
|
474
|
-
.await;
|
|
475
|
-
worker.finalize_shutdown().await;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
/// Called within the poll loop thread, calls core and triggers JS callback with result
|
|
479
|
-
async fn handle_poll_workflow_activation_request(
|
|
480
|
-
worker: &CoreWorker,
|
|
481
|
-
span_context: SpanContext,
|
|
482
|
-
channel: Arc<Channel>,
|
|
483
|
-
callback: Root<JsFunction>,
|
|
484
|
-
) {
|
|
485
|
-
let otel_ctx = opentelemetry::Context::new().with_remote_span_context(span_context);
|
|
486
|
-
match worker
|
|
487
|
-
.poll_workflow_activation()
|
|
488
|
-
.with_context(otel_ctx)
|
|
489
|
-
.await
|
|
490
|
-
{
|
|
491
|
-
Ok(task) => {
|
|
492
|
-
send_result(channel, callback, move |cx| {
|
|
493
|
-
let len = task.encoded_len();
|
|
494
|
-
let mut result = JsArrayBuffer::new(cx, len)?;
|
|
495
|
-
let mut slice = result.as_mut_slice(cx);
|
|
496
|
-
if task.encode(&mut slice).is_err() {
|
|
497
|
-
panic!("Failed to encode task")
|
|
498
|
-
};
|
|
499
|
-
Ok(result)
|
|
500
|
-
});
|
|
501
|
-
}
|
|
502
|
-
Err(err) => {
|
|
503
|
-
send_error(channel, callback, move |cx| match err {
|
|
504
|
-
PollWfError::ShutDown => SHUTDOWN_ERROR.from_error(cx, err),
|
|
505
|
-
PollWfError::TonicError(_)
|
|
506
|
-
| PollWfError::AutocompleteError(CompleteWfError::TonicError(_)) => {
|
|
507
|
-
TRANSPORT_ERROR.from_error(cx, err)
|
|
508
|
-
}
|
|
509
|
-
PollWfError::AutocompleteError(CompleteWfError::MalformedWorkflowCompletion {
|
|
510
|
-
reason,
|
|
511
|
-
..
|
|
512
|
-
}) => Ok(JsError::type_error(cx, reason)?.upcast()),
|
|
513
|
-
});
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
/// Called within the poll loop thread, calls core and triggers JS callback with result
|
|
519
|
-
async fn handle_poll_activity_task_request(
|
|
520
|
-
worker: &CoreWorker,
|
|
521
|
-
span_context: SpanContext,
|
|
522
|
-
channel: Arc<Channel>,
|
|
523
|
-
callback: Root<JsFunction>,
|
|
524
|
-
) {
|
|
525
|
-
let otel_ctx = opentelemetry::Context::new().with_remote_span_context(span_context);
|
|
526
|
-
match worker.poll_activity_task().with_context(otel_ctx).await {
|
|
527
|
-
Ok(task) => {
|
|
528
|
-
send_result(channel, callback, move |cx| {
|
|
529
|
-
let len = task.encoded_len();
|
|
530
|
-
let mut result = JsArrayBuffer::new(cx, len)?;
|
|
531
|
-
let mut slice = result.as_mut_slice(cx);
|
|
532
|
-
if task.encode(&mut slice).is_err() {
|
|
533
|
-
panic!("Failed to encode task")
|
|
534
|
-
};
|
|
535
|
-
Ok(result)
|
|
536
|
-
});
|
|
537
|
-
}
|
|
538
|
-
Err(err) => {
|
|
539
|
-
send_error(channel, callback, move |cx| match err {
|
|
540
|
-
PollActivityError::ShutDown => SHUTDOWN_ERROR.from_error(cx, err),
|
|
541
|
-
PollActivityError::TonicError(_) => TRANSPORT_ERROR.from_error(cx, err),
|
|
542
|
-
});
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
// Below are functions exported to JS
|
|
548
|
-
|
|
549
|
-
/// Initialize Core global telemetry.
|
|
550
|
-
/// This should typically be called once on process startup.
|
|
551
|
-
fn init_telemetry(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
552
|
-
let telemetry_options = cx.argument::<JsObject>(0)?.as_telemetry_options(&mut cx)?;
|
|
553
|
-
telemetry_init(&telemetry_options).map_err(|err| {
|
|
554
|
-
cx.throw_type_error::<String, ()>(format!("{}", err))
|
|
555
|
-
.unwrap_err()
|
|
556
|
-
})?;
|
|
557
|
-
Ok(cx.undefined())
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
/// Create the tokio runtime required to run Core.
|
|
561
|
-
/// Immediately spawns a poller thread that will block on [RuntimeRequest]s
|
|
562
|
-
fn runtime_new(mut cx: FunctionContext) -> JsResult<BoxedRuntime> {
|
|
563
|
-
let channel = Arc::new(cx.channel());
|
|
564
|
-
let (sender, mut receiver) = unbounded_channel::<RuntimeRequest>();
|
|
565
|
-
|
|
566
|
-
std::thread::spawn(move || start_bridge_loop(channel, &mut receiver));
|
|
567
|
-
|
|
568
|
-
Ok(cx.boxed(Arc::new(RuntimeHandle { sender })))
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
/// Create a connected gRPC client which can be used to initialize workers.
|
|
572
|
-
/// Client will be returned in the supplied `callback`.
|
|
573
|
-
fn client_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
574
|
-
let runtime = cx.argument::<BoxedRuntime>(0)?;
|
|
575
|
-
let opts = cx.argument::<JsObject>(1)?;
|
|
576
|
-
let callback = cx.argument::<JsFunction>(2)?;
|
|
577
|
-
|
|
578
|
-
let client_options = opts.as_client_options(&mut cx)?;
|
|
579
|
-
let headers = match js_optional_getter!(&mut cx, &opts, "metadata", JsObject) {
|
|
580
|
-
None => None,
|
|
581
|
-
Some(h) => Some(
|
|
582
|
-
h.as_hash_map_of_string_to_string(&mut cx)
|
|
583
|
-
.map_err(|reason| {
|
|
584
|
-
cx.throw_type_error::<_, HashMap<String, String>>(format!(
|
|
585
|
-
"Invalid metadata: {}",
|
|
586
|
-
reason
|
|
587
|
-
))
|
|
588
|
-
.unwrap_err()
|
|
589
|
-
})?,
|
|
590
|
-
),
|
|
591
|
-
};
|
|
592
|
-
|
|
593
|
-
let request = RuntimeRequest::CreateClient {
|
|
594
|
-
runtime: (**runtime).clone(),
|
|
595
|
-
options: client_options,
|
|
596
|
-
headers,
|
|
597
|
-
callback: callback.root(&mut cx),
|
|
598
|
-
};
|
|
599
|
-
if let Err(err) = runtime.sender.send(request) {
|
|
600
|
-
callback_with_unexpected_error(&mut cx, callback, err)?;
|
|
601
|
-
};
|
|
602
|
-
|
|
603
|
-
Ok(cx.undefined())
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
/// Update a Client's HTTP request headers
|
|
607
|
-
fn client_update_headers(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
608
|
-
let client = cx.argument::<BoxedClient>(0)?;
|
|
609
|
-
let headers = cx
|
|
610
|
-
.argument::<JsObject>(1)?
|
|
611
|
-
.as_hash_map_of_string_to_string(&mut cx)?;
|
|
612
|
-
let callback = cx.argument::<JsFunction>(2)?;
|
|
613
|
-
|
|
614
|
-
match &*client.borrow() {
|
|
615
|
-
None => {
|
|
616
|
-
callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Client")?;
|
|
617
|
-
}
|
|
618
|
-
Some(client) => {
|
|
619
|
-
let request = RuntimeRequest::UpdateClientHeaders {
|
|
620
|
-
client: client.core_client.clone(),
|
|
621
|
-
headers,
|
|
622
|
-
callback: callback.root(&mut cx),
|
|
623
|
-
};
|
|
624
|
-
if let Err(err) = client.runtime.sender.send(request) {
|
|
625
|
-
callback_with_unexpected_error(&mut cx, callback, err)?;
|
|
626
|
-
};
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
Ok(cx.undefined())
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
/// Create a new worker asynchronously.
|
|
634
|
-
/// Worker uses the provided connection and returned to JS using supplied `callback`.
|
|
635
|
-
fn worker_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
636
|
-
let client = cx.argument::<BoxedClient>(0)?;
|
|
637
|
-
let worker_options = cx.argument::<JsObject>(1)?;
|
|
638
|
-
let callback = cx.argument::<JsFunction>(2)?;
|
|
639
|
-
|
|
640
|
-
let config = worker_options.as_worker_config(&mut cx)?;
|
|
641
|
-
match &*client.borrow() {
|
|
642
|
-
None => {
|
|
643
|
-
callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Client")?;
|
|
644
|
-
}
|
|
645
|
-
Some(client) => {
|
|
646
|
-
let request = RuntimeRequest::InitWorker {
|
|
647
|
-
client: client.core_client.clone(),
|
|
648
|
-
config,
|
|
649
|
-
callback: callback.root(&mut cx),
|
|
650
|
-
};
|
|
651
|
-
if let Err(err) = client.runtime.sender.send(request) {
|
|
652
|
-
callback_with_unexpected_error(&mut cx, callback, err)?;
|
|
653
|
-
};
|
|
654
|
-
}
|
|
655
|
-
};
|
|
656
|
-
|
|
657
|
-
Ok(cx.undefined())
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
/// Create a new replay worker asynchronously.
|
|
661
|
-
/// Worker is returned to JS using supplied callback.
|
|
662
|
-
fn replay_worker_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
663
|
-
let runtime = cx.argument::<BoxedRuntime>(0)?;
|
|
664
|
-
let worker_options = cx.argument::<JsObject>(1)?;
|
|
665
|
-
let history_binary = cx.argument::<JsArrayBuffer>(2)?;
|
|
666
|
-
let callback = cx.argument::<JsFunction>(3)?;
|
|
667
|
-
|
|
668
|
-
let config = worker_options.as_worker_config(&mut cx)?;
|
|
669
|
-
let data = history_binary.as_slice(&mut cx);
|
|
670
|
-
match History::decode_length_delimited(data) {
|
|
671
|
-
Ok(history) => {
|
|
672
|
-
let request = RuntimeRequest::InitReplayWorker {
|
|
673
|
-
config,
|
|
674
|
-
history,
|
|
675
|
-
callback: callback.root(&mut cx),
|
|
676
|
-
};
|
|
677
|
-
if let Err(err) = runtime.sender.send(request) {
|
|
678
|
-
callback_with_unexpected_error(&mut cx, callback, err)?;
|
|
679
|
-
};
|
|
680
|
-
}
|
|
681
|
-
Err(_) => callback_with_error(&mut cx, callback, |cx| {
|
|
682
|
-
JsError::type_error(cx, "Cannot decode History from buffer")
|
|
683
|
-
})?,
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
Ok(cx.undefined())
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
/// Shutdown the Core instance and break out of the thread loop
|
|
690
|
-
fn runtime_shutdown(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
691
|
-
let runtime = cx.argument::<BoxedRuntime>(0)?;
|
|
692
|
-
let callback = cx.argument::<JsFunction>(1)?;
|
|
693
|
-
let request = RuntimeRequest::Shutdown {
|
|
694
|
-
callback: callback.root(&mut cx),
|
|
695
|
-
};
|
|
696
|
-
if let Err(err) = runtime.sender.send(request) {
|
|
697
|
-
callback_with_unexpected_error(&mut cx, callback, err)?;
|
|
698
|
-
};
|
|
699
|
-
Ok(cx.undefined())
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
/// Request to drain forwarded logs from core
|
|
703
|
-
fn poll_logs(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
704
|
-
let runtime = cx.argument::<BoxedRuntime>(0)?;
|
|
705
|
-
let callback = cx.argument::<JsFunction>(1)?;
|
|
706
|
-
let request = RuntimeRequest::PollLogs {
|
|
707
|
-
callback: callback.root(&mut cx),
|
|
708
|
-
};
|
|
709
|
-
if let Err(err) = runtime.sender.send(request) {
|
|
710
|
-
callback_with_unexpected_error(&mut cx, callback, err)?;
|
|
711
|
-
}
|
|
712
|
-
Ok(cx.undefined())
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
/// Initiate a single workflow activation poll request.
|
|
716
|
-
/// There should be only one concurrent poll request for this type.
|
|
717
|
-
fn worker_poll_workflow_activation(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
718
|
-
let worker = cx.argument::<BoxedWorker>(0)?;
|
|
719
|
-
let otel_span = cx.argument::<JsObject>(1)?;
|
|
720
|
-
let callback = cx.argument::<JsFunction>(2)?;
|
|
721
|
-
match &*worker.borrow() {
|
|
722
|
-
None => {
|
|
723
|
-
callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Worker")?;
|
|
724
|
-
}
|
|
725
|
-
Some(worker) => {
|
|
726
|
-
let request = WorkerRequest::PollWorkflowActivation {
|
|
727
|
-
otel_span: otel_span.as_otel_span_context(&mut cx)?,
|
|
728
|
-
callback: callback.root(&mut cx),
|
|
729
|
-
};
|
|
730
|
-
if let Err(err) = worker.sender.send(request) {
|
|
731
|
-
callback_with_unexpected_error(&mut cx, callback, err)?;
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
Ok(cx.undefined())
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
/// Initiate a single activity task poll request.
|
|
739
|
-
/// There should be only one concurrent poll request for this type.
|
|
740
|
-
fn worker_poll_activity_task(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
741
|
-
let worker = cx.argument::<BoxedWorker>(0)?;
|
|
742
|
-
let otel_span = cx.argument::<JsObject>(1)?;
|
|
743
|
-
let callback = cx.argument::<JsFunction>(2)?;
|
|
744
|
-
match &*worker.borrow() {
|
|
745
|
-
None => {
|
|
746
|
-
callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Worker")?;
|
|
747
|
-
}
|
|
748
|
-
Some(worker) => {
|
|
749
|
-
let request = WorkerRequest::PollActivityTask {
|
|
750
|
-
otel_span: otel_span.as_otel_span_context(&mut cx)?,
|
|
751
|
-
callback: callback.root(&mut cx),
|
|
752
|
-
};
|
|
753
|
-
if let Err(err) = worker.sender.send(request) {
|
|
754
|
-
callback_with_unexpected_error(&mut cx, callback, err)?;
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
Ok(cx.undefined())
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
/// Submit a workflow activation completion to core.
|
|
762
|
-
fn worker_complete_workflow_activation(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
763
|
-
let worker = cx.argument::<BoxedWorker>(0)?;
|
|
764
|
-
let otel_span = cx.argument::<JsObject>(1)?;
|
|
765
|
-
let completion = cx.argument::<JsArrayBuffer>(2)?;
|
|
766
|
-
let callback = cx.argument::<JsFunction>(3)?;
|
|
767
|
-
match &*worker.borrow() {
|
|
768
|
-
None => {
|
|
769
|
-
callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Worker")?;
|
|
770
|
-
}
|
|
771
|
-
Some(worker) => {
|
|
772
|
-
match WorkflowActivationCompletion::decode_length_delimited(
|
|
773
|
-
completion.as_slice(&mut cx),
|
|
774
|
-
) {
|
|
775
|
-
Ok(completion) => {
|
|
776
|
-
let request = WorkerRequest::CompleteWorkflowActivation {
|
|
777
|
-
completion,
|
|
778
|
-
otel_span: otel_span.as_otel_span_context(&mut cx)?,
|
|
779
|
-
callback: callback.root(&mut cx),
|
|
780
|
-
};
|
|
781
|
-
if let Err(err) = worker.sender.send(request) {
|
|
782
|
-
callback_with_unexpected_error(&mut cx, callback, err)?;
|
|
783
|
-
};
|
|
784
|
-
}
|
|
785
|
-
Err(_) => callback_with_error(&mut cx, callback, |cx| {
|
|
786
|
-
JsError::type_error(cx, "Cannot decode Completion from buffer")
|
|
787
|
-
})?,
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
};
|
|
791
|
-
Ok(cx.undefined())
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
/// Submit an activity task completion to core.
|
|
795
|
-
fn worker_complete_activity_task(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
796
|
-
let worker = cx.argument::<BoxedWorker>(0)?;
|
|
797
|
-
let otel_span = cx.argument::<JsObject>(1)?;
|
|
798
|
-
let result = cx.argument::<JsArrayBuffer>(2)?;
|
|
799
|
-
let callback = cx.argument::<JsFunction>(3)?;
|
|
800
|
-
match &*worker.borrow() {
|
|
801
|
-
None => {
|
|
802
|
-
callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Worker")?;
|
|
803
|
-
}
|
|
804
|
-
Some(worker) => {
|
|
805
|
-
match ActivityTaskCompletion::decode_length_delimited(result.as_slice(&mut cx)) {
|
|
806
|
-
Ok(completion) => {
|
|
807
|
-
let request = WorkerRequest::CompleteActivityTask {
|
|
808
|
-
completion,
|
|
809
|
-
otel_span: otel_span.as_otel_span_context(&mut cx)?,
|
|
810
|
-
callback: callback.root(&mut cx),
|
|
811
|
-
};
|
|
812
|
-
if let Err(err) = worker.sender.send(request) {
|
|
813
|
-
callback_with_unexpected_error(&mut cx, callback, err)?;
|
|
814
|
-
};
|
|
815
|
-
}
|
|
816
|
-
Err(_) => callback_with_error(&mut cx, callback, |cx| {
|
|
817
|
-
JsError::type_error(cx, "Cannot decode Completion from buffer")
|
|
818
|
-
})?,
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
};
|
|
822
|
-
Ok(cx.undefined())
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
/// Submit an activity heartbeat to core.
|
|
826
|
-
fn worker_record_activity_heartbeat(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
827
|
-
let worker = cx.argument::<BoxedWorker>(0)?;
|
|
828
|
-
let heartbeat = cx.argument::<JsArrayBuffer>(1)?;
|
|
829
|
-
match &*worker.borrow() {
|
|
830
|
-
None => UNEXPECTED_ERROR
|
|
831
|
-
.from_string(&mut cx, "Tried to use closed Worker")
|
|
832
|
-
.and_then(|err| cx.throw(err))?,
|
|
833
|
-
Some(worker) => {
|
|
834
|
-
match ActivityHeartbeat::decode_length_delimited(heartbeat.as_slice(&mut cx)) {
|
|
835
|
-
Ok(heartbeat) => {
|
|
836
|
-
let request = WorkerRequest::RecordActivityHeartbeat { heartbeat };
|
|
837
|
-
if let Err(err) = worker.sender.send(request) {
|
|
838
|
-
UNEXPECTED_ERROR
|
|
839
|
-
.from_error(&mut cx, err)
|
|
840
|
-
.and_then(|err| cx.throw(err))?;
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
Err(_) => cx.throw_type_error("Cannot decode ActivityHeartbeat from buffer")?,
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
};
|
|
847
|
-
Ok(cx.undefined())
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
/// Request shutdown of the worker.
|
|
851
|
-
/// Once complete Core will stop polling on new tasks and activations on worker's task queue.
|
|
852
|
-
/// Caller should drain any pending tasks and activations and call worker_finalize_shutdown before breaking from
|
|
853
|
-
/// the loop to ensure graceful shutdown.
|
|
854
|
-
fn worker_initiate_shutdown(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
855
|
-
let worker = cx.argument::<BoxedWorker>(0)?;
|
|
856
|
-
let callback = cx.argument::<JsFunction>(1)?;
|
|
857
|
-
match &*worker.borrow() {
|
|
858
|
-
None => {
|
|
859
|
-
callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Worker")?;
|
|
860
|
-
}
|
|
861
|
-
Some(worker) => {
|
|
862
|
-
if let Err(err) = worker.sender.send(WorkerRequest::InitiateShutdown {
|
|
863
|
-
callback: callback.root(&mut cx),
|
|
864
|
-
}) {
|
|
865
|
-
UNEXPECTED_ERROR
|
|
866
|
-
.from_error(&mut cx, err)
|
|
867
|
-
.and_then(|err| cx.throw(err))?;
|
|
868
|
-
};
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
Ok(cx.undefined())
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
fn worker_finalize_shutdown(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
875
|
-
let worker = cx.argument::<BoxedWorker>(0)?;
|
|
876
|
-
if worker.replace(None).is_none() {
|
|
877
|
-
ILLEGAL_STATE_ERROR
|
|
878
|
-
.from_string(&mut cx, "Worker already closed")
|
|
879
|
-
.and_then(|err| cx.throw(err))?;
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
Ok(cx.undefined())
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
/// Drop a reference to a Client, once all references are dropped, the Client will be closed.
|
|
886
|
-
fn client_close(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
887
|
-
let client = cx.argument::<BoxedClient>(0)?;
|
|
888
|
-
if client.replace(None).is_none() {
|
|
889
|
-
ILLEGAL_STATE_ERROR
|
|
890
|
-
.from_string(&mut cx, "Client already closed")
|
|
891
|
-
.and_then(|err| cx.throw(err))?;
|
|
892
|
-
};
|
|
893
|
-
Ok(cx.undefined())
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
/// Convert Rust SystemTime into a JS array with 2 numbers (seconds, nanos)
|
|
897
|
-
fn system_time_to_js<'a, C>(cx: &mut C, time: SystemTime) -> NeonResult<Handle<'a, JsArray>>
|
|
898
|
-
where
|
|
899
|
-
C: Context<'a>,
|
|
900
|
-
{
|
|
901
|
-
let nanos = time
|
|
902
|
-
.duration_since(UNIX_EPOCH)
|
|
903
|
-
.unwrap_or(Duration::ZERO)
|
|
904
|
-
.as_nanos();
|
|
905
|
-
let only_nanos = cx.number((nanos % 1_000_000_000) as f64);
|
|
906
|
-
let ts_seconds = cx.number((nanos / 1_000_000_000) as f64);
|
|
907
|
-
let ts = cx.empty_array();
|
|
908
|
-
ts.set(cx, 0, ts_seconds).unwrap();
|
|
909
|
-
ts.set(cx, 1, only_nanos).unwrap();
|
|
910
|
-
Ok(ts)
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
/// Helper to get the current time in nanosecond resolution.
|
|
914
|
-
fn get_time_of_day(mut cx: FunctionContext) -> JsResult<JsArray> {
|
|
915
|
-
system_time_to_js(&mut cx, SystemTime::now())
|
|
916
|
-
}
|
|
11
|
+
use testing::*;
|
|
917
12
|
|
|
918
13
|
#[neon::main]
|
|
919
14
|
fn main(mut cx: ModuleContext) -> NeonResult<()> {
|
|
@@ -944,5 +39,8 @@ fn main(mut cx: ModuleContext) -> NeonResult<()> {
|
|
|
944
39
|
"workerRecordActivityHeartbeat",
|
|
945
40
|
worker_record_activity_heartbeat,
|
|
946
41
|
)?;
|
|
42
|
+
cx.export_function("startEphemeralServer", start_ephemeral_server)?;
|
|
43
|
+
cx.export_function("shutdownEphemeralServer", shutdown_ephemeral_server)?;
|
|
44
|
+
cx.export_function("getEphemeralServerTarget", get_ephemeral_server_target)?;
|
|
947
45
|
Ok(())
|
|
948
46
|
}
|