@temporalio/core-bridge 1.11.2 → 1.11.4
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 +396 -489
- package/Cargo.toml +3 -2
- package/lib/errors.d.ts +2 -0
- package/lib/errors.js +7 -3
- package/lib/errors.js.map +1 -1
- package/lib/index.d.ts +8 -2
- package/lib/index.js.map +1 -1
- package/lib/worker-tuner.d.ts +111 -1
- package/package.json +3 -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/.github/workflows/per-pr.yml +3 -3
- package/sdk-core/Cargo.toml +0 -1
- package/sdk-core/client/Cargo.toml +1 -2
- package/sdk-core/client/src/lib.rs +21 -13
- package/sdk-core/client/src/metrics.rs +1 -1
- package/sdk-core/client/src/raw.rs +46 -1
- package/sdk-core/core/Cargo.toml +7 -7
- package/sdk-core/core/benches/workflow_replay.rs +1 -1
- package/sdk-core/core/src/abstractions/take_cell.rs +1 -1
- package/sdk-core/core/src/abstractions.rs +98 -10
- package/sdk-core/core/src/core_tests/activity_tasks.rs +8 -2
- package/sdk-core/core/src/core_tests/local_activities.rs +1 -1
- package/sdk-core/core/src/core_tests/mod.rs +3 -3
- package/sdk-core/core/src/core_tests/updates.rs +104 -9
- package/sdk-core/core/src/core_tests/workers.rs +72 -3
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +6 -7
- package/sdk-core/core/src/debug_client.rs +78 -0
- package/sdk-core/core/src/ephemeral_server/mod.rs +15 -5
- package/sdk-core/core/src/lib.rs +30 -4
- package/sdk-core/core/src/pollers/mod.rs +1 -1
- package/sdk-core/core/src/pollers/poll_buffer.rs +7 -7
- package/sdk-core/core/src/replay/mod.rs +4 -4
- package/sdk-core/core/src/telemetry/log_export.rs +2 -2
- package/sdk-core/core/src/telemetry/metrics.rs +69 -1
- package/sdk-core/core/src/telemetry/otel.rs +2 -2
- package/sdk-core/core/src/test_help/mod.rs +3 -3
- package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +3 -3
- package/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +1 -1
- package/sdk-core/core/src/worker/activities/local_activities.rs +68 -24
- package/sdk-core/core/src/worker/activities.rs +26 -15
- package/sdk-core/core/src/worker/client/mocks.rs +10 -4
- package/sdk-core/core/src/worker/client.rs +17 -0
- package/sdk-core/core/src/worker/mod.rs +71 -13
- package/sdk-core/core/src/worker/slot_provider.rs +5 -7
- package/sdk-core/core/src/worker/tuner/fixed_size.rs +4 -3
- package/sdk-core/core/src/worker/tuner/resource_based.rs +171 -32
- package/sdk-core/core/src/worker/tuner.rs +18 -6
- package/sdk-core/core/src/worker/workflow/history_update.rs +43 -13
- package/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +6 -6
- package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +6 -5
- package/sdk-core/core/src/worker/workflow/managed_run.rs +3 -3
- package/sdk-core/core/src/worker/workflow/mod.rs +13 -7
- package/sdk-core/core/src/worker/workflow/wft_extraction.rs +7 -7
- package/sdk-core/core/src/worker/workflow/wft_poller.rs +2 -2
- package/sdk-core/core/src/worker/workflow/workflow_stream.rs +11 -11
- package/sdk-core/core-api/Cargo.toml +1 -0
- package/sdk-core/core-api/src/worker.rs +84 -30
- package/sdk-core/sdk/Cargo.toml +1 -2
- package/sdk-core/sdk/src/lib.rs +1 -1
- package/sdk-core/sdk/src/workflow_context.rs +9 -8
- package/sdk-core/sdk/src/workflow_future.rs +19 -14
- package/sdk-core/sdk-core-protos/Cargo.toml +2 -0
- package/sdk-core/sdk-core-protos/build.rs +6 -1
- package/sdk-core/sdk-core-protos/protos/api_upstream/buf.yaml +1 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv2.json +3207 -158
- package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv3.yaml +2934 -118
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/activity/v1/message.proto +67 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/common/v1/message.proto +47 -1
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/event_type.proto +6 -7
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +2 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/reset.proto +5 -3
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +3 -3
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/update.proto +14 -13
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/failure/v1/message.proto +1 -3
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/history/v1/message.proto +22 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/nexus/v1/message.proto +13 -2
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +26 -6
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/schedule/v1/message.proto +5 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/workflow_metadata.proto +6 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +46 -12
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/update/v1/message.proto +18 -19
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflow/v1/message.proto +27 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +192 -19
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +279 -12
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/activity_result/activity_result.proto +1 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/activity_task/activity_task.proto +1 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +1 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/common/common.proto +1 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/core_interface.proto +17 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/external_data/external_data.proto +1 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +1 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +1 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +1 -1
- package/sdk-core/sdk-core-protos/src/lib.rs +30 -6
- package/sdk-core/test-utils/Cargo.toml +1 -2
- package/sdk-core/test-utils/src/lib.rs +2 -2
- package/sdk-core/tests/heavy_tests.rs +1 -1
- package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +2 -2
- package/sdk-core/tests/integ_tests/metrics_tests.rs +144 -7
- package/sdk-core/tests/integ_tests/queries_tests.rs +1 -1
- package/sdk-core/tests/integ_tests/update_tests.rs +109 -5
- package/sdk-core/tests/integ_tests/worker_tests.rs +44 -8
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +1 -1
- package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -1
- package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +1 -1
- package/sdk-core/tests/integ_tests/workflow_tests.rs +3 -2
- package/src/conversions/slot_supplier_bridge.rs +287 -0
- package/src/conversions.rs +23 -15
- package/src/helpers.rs +35 -1
- package/src/runtime.rs +7 -3
- package/src/worker.rs +1 -1
- package/ts/errors.ts +9 -2
- package/ts/index.ts +19 -4
- package/ts/worker-tuner.ts +123 -1
- package/sdk-core/sdk-core-protos/protos/api_upstream/.gitmodules +0 -3
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
use crate::helpers::{get_optional, js_getter};
|
|
2
|
+
use log::{error, warn};
|
|
3
|
+
use neon::types::JsNull;
|
|
4
|
+
use neon::{
|
|
5
|
+
context::Context,
|
|
6
|
+
context::FunctionContext,
|
|
7
|
+
event::Channel,
|
|
8
|
+
handle::{Handle, Root},
|
|
9
|
+
object::Object,
|
|
10
|
+
prelude::{JsBuffer, JsFunction, JsObject, JsPromise, JsUndefined, JsValue, NeonResult, Value},
|
|
11
|
+
};
|
|
12
|
+
use prost::Message;
|
|
13
|
+
use std::{cell::RefCell, marker::PhantomData, sync::Arc, time::Duration};
|
|
14
|
+
use temporal_sdk_core::api::worker::{
|
|
15
|
+
SlotKind, SlotKindType, SlotMarkUsedContext, SlotReleaseContext, SlotReservationContext,
|
|
16
|
+
SlotSupplier, SlotSupplierPermit,
|
|
17
|
+
};
|
|
18
|
+
use tokio::sync::oneshot;
|
|
19
|
+
|
|
20
|
+
pub struct SlotSupplierBridge<SK> {
|
|
21
|
+
inner: Arc<Root<JsObject>>,
|
|
22
|
+
reserve_cb: Arc<Root<JsFunction>>,
|
|
23
|
+
try_reserve_cb: Arc<Root<JsFunction>>,
|
|
24
|
+
mark_used_cb: Arc<Root<JsFunction>>,
|
|
25
|
+
release_cb: Arc<Root<JsFunction>>,
|
|
26
|
+
channel: Channel,
|
|
27
|
+
_kind: PhantomData<SK>,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
impl<SK> SlotSupplierBridge<SK> {
|
|
31
|
+
pub(crate) fn new(cx: &mut FunctionContext, obj: Handle<'_, JsObject>) -> NeonResult<Self> {
|
|
32
|
+
Ok(Self {
|
|
33
|
+
inner: Arc::new(obj.root(cx)),
|
|
34
|
+
// Callbacks for each function are cached to reduce calling overhead
|
|
35
|
+
reserve_cb: Arc::new(js_getter!(cx, &obj, "reserveSlot", JsFunction).root(cx)),
|
|
36
|
+
try_reserve_cb: Arc::new(js_getter!(cx, &obj, "tryReserveSlot", JsFunction).root(cx)),
|
|
37
|
+
mark_used_cb: Arc::new(js_getter!(cx, &obj, "markSlotUsed", JsFunction).root(cx)),
|
|
38
|
+
release_cb: Arc::new(js_getter!(cx, &obj, "releaseSlot", JsFunction).root(cx)),
|
|
39
|
+
channel: cx.channel(),
|
|
40
|
+
_kind: PhantomData,
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
struct BridgePermitData {
|
|
46
|
+
permit: Arc<Root<JsObject>>,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
struct CallAbortOnDrop {
|
|
50
|
+
chan: Channel,
|
|
51
|
+
aborter: oneshot::Receiver<Root<JsFunction>>,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
impl Drop for CallAbortOnDrop {
|
|
55
|
+
fn drop(&mut self) {
|
|
56
|
+
if let Ok(aborter) = self.aborter.try_recv() {
|
|
57
|
+
let _ = self.chan.try_send(move |mut cx| {
|
|
58
|
+
let cb = aborter.to_inner(&mut cx);
|
|
59
|
+
let this = cx.undefined();
|
|
60
|
+
let _ = cb.call(&mut cx, this, []);
|
|
61
|
+
Ok(())
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
static PERMIT_DATA_FIELD: &str = "permit_data";
|
|
68
|
+
|
|
69
|
+
#[async_trait::async_trait]
|
|
70
|
+
impl<SK: SlotKind + Send + Sync> SlotSupplier for SlotSupplierBridge<SK> {
|
|
71
|
+
type SlotKind = SK;
|
|
72
|
+
|
|
73
|
+
async fn reserve_slot(&self, ctx: &dyn SlotReservationContext) -> SlotSupplierPermit {
|
|
74
|
+
loop {
|
|
75
|
+
let inner = self.inner.clone();
|
|
76
|
+
let rcb = self.reserve_cb.clone();
|
|
77
|
+
let task_queue = ctx.task_queue().to_string();
|
|
78
|
+
let worker_identity = ctx.worker_identity().to_string();
|
|
79
|
+
let worker_build_id = ctx.worker_build_id().to_string();
|
|
80
|
+
let is_sticky = ctx.is_sticky();
|
|
81
|
+
|
|
82
|
+
let (callback_fut, _abort_on_drop) = match self
|
|
83
|
+
.channel
|
|
84
|
+
.send(move |mut cx| {
|
|
85
|
+
let context = Self::mk_reserve_ctx(
|
|
86
|
+
task_queue,
|
|
87
|
+
worker_identity,
|
|
88
|
+
worker_build_id,
|
|
89
|
+
is_sticky,
|
|
90
|
+
&mut cx,
|
|
91
|
+
)?;
|
|
92
|
+
let (aborter_tx, aborter) = oneshot::channel();
|
|
93
|
+
let abort_on_drop = CallAbortOnDrop {
|
|
94
|
+
chan: cx.channel(),
|
|
95
|
+
aborter,
|
|
96
|
+
};
|
|
97
|
+
let aborter_tx = RefCell::new(Some(aborter_tx));
|
|
98
|
+
let abort_func = JsFunction::new(&mut cx, move |mut cx| {
|
|
99
|
+
let func: Handle<JsFunction> = cx.argument(0)?;
|
|
100
|
+
if let Some(aborter_tx) = aborter_tx.take() {
|
|
101
|
+
let _ = aborter_tx.send(func.root(&mut cx));
|
|
102
|
+
}
|
|
103
|
+
Ok(cx.undefined())
|
|
104
|
+
})?
|
|
105
|
+
.upcast();
|
|
106
|
+
|
|
107
|
+
let this = (*inner).clone(&mut cx).into_inner(&mut cx);
|
|
108
|
+
let val = rcb
|
|
109
|
+
.to_inner(&mut cx)
|
|
110
|
+
.call(&mut cx, this, [context, abort_func])?;
|
|
111
|
+
let as_prom = val.downcast_or_throw::<JsPromise, _>(&mut cx)?;
|
|
112
|
+
let fut = as_prom.to_future(&mut cx, |mut cx, result| match result {
|
|
113
|
+
Ok(value) => {
|
|
114
|
+
let permit_obj = JsObject::new(&mut cx);
|
|
115
|
+
permit_obj.set(&mut cx, PERMIT_DATA_FIELD, value)?;
|
|
116
|
+
Ok(Ok(permit_obj.root(&mut cx)))
|
|
117
|
+
}
|
|
118
|
+
Err(_) => Ok(Err(())),
|
|
119
|
+
})?;
|
|
120
|
+
Ok((fut, abort_on_drop))
|
|
121
|
+
})
|
|
122
|
+
.await
|
|
123
|
+
{
|
|
124
|
+
Ok(v) => v,
|
|
125
|
+
Err(e) => {
|
|
126
|
+
warn!("Error reserving slot: {:?}", e);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
match callback_fut.await {
|
|
132
|
+
Ok(Ok(res)) => {
|
|
133
|
+
let permit = SlotSupplierPermit::with_user_data(BridgePermitData {
|
|
134
|
+
permit: Arc::new(res),
|
|
135
|
+
});
|
|
136
|
+
return permit;
|
|
137
|
+
}
|
|
138
|
+
// Error in user function
|
|
139
|
+
Ok(Err(())) => {
|
|
140
|
+
// Nothing to do here. Error in user's function (or an abort).
|
|
141
|
+
// Logging handled on the JS side.
|
|
142
|
+
}
|
|
143
|
+
Err(e) => {
|
|
144
|
+
error!(
|
|
145
|
+
"There was an error in the rust/node bridge while reserving a slot: {}",
|
|
146
|
+
e
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Wait a beat to avoid spamming errors
|
|
151
|
+
tokio::time::sleep(Duration::from_millis(1000)).await;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
fn try_reserve_slot(&self, ctx: &dyn SlotReservationContext) -> Option<SlotSupplierPermit> {
|
|
156
|
+
let inner = self.inner.clone();
|
|
157
|
+
let rcb = self.try_reserve_cb.clone();
|
|
158
|
+
let task_queue = ctx.task_queue().to_string();
|
|
159
|
+
let worker_identity = ctx.worker_identity().to_string();
|
|
160
|
+
let worker_build_id = ctx.worker_build_id().to_string();
|
|
161
|
+
let is_sticky = ctx.is_sticky();
|
|
162
|
+
|
|
163
|
+
// This is... unfortunate but since this method is called from an async context way up
|
|
164
|
+
// the stack, but is not async itself AND we need some way to get the result from the JS
|
|
165
|
+
// callback, we must use this roundabout way of blocking. Simply calling `join` on the
|
|
166
|
+
// channel send won't work - it'll panic because it calls block_on internally.
|
|
167
|
+
let runtime_handle = tokio::runtime::Handle::current();
|
|
168
|
+
let _entered = runtime_handle.enter();
|
|
169
|
+
let callback_res = futures::executor::block_on(self.channel.send(move |mut cx| {
|
|
170
|
+
let context = Self::mk_reserve_ctx(
|
|
171
|
+
task_queue,
|
|
172
|
+
worker_identity,
|
|
173
|
+
worker_build_id,
|
|
174
|
+
is_sticky,
|
|
175
|
+
&mut cx,
|
|
176
|
+
)?;
|
|
177
|
+
|
|
178
|
+
let this = (*inner).clone(&mut cx).into_inner(&mut cx);
|
|
179
|
+
let val = rcb.to_inner(&mut cx).call(&mut cx, this, [context])?;
|
|
180
|
+
if val.is_a::<JsUndefined, _>(&mut cx) || val.is_a::<JsNull, _>(&mut cx) {
|
|
181
|
+
return Ok(None);
|
|
182
|
+
}
|
|
183
|
+
let permit_obj = JsObject::new(&mut cx);
|
|
184
|
+
permit_obj.set(&mut cx, PERMIT_DATA_FIELD, val)?;
|
|
185
|
+
Ok(Some(permit_obj.root(&mut cx)))
|
|
186
|
+
}));
|
|
187
|
+
|
|
188
|
+
// Ignore errors, they'll be logged by JS
|
|
189
|
+
callback_res.ok().flatten().map(|res| {
|
|
190
|
+
SlotSupplierPermit::with_user_data(BridgePermitData {
|
|
191
|
+
permit: Arc::new(res),
|
|
192
|
+
})
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
fn mark_slot_used(&self, ctx: &dyn SlotMarkUsedContext<SlotKind = Self::SlotKind>) {
|
|
197
|
+
let inner = self.inner.clone();
|
|
198
|
+
let cb = self.mark_used_cb.clone();
|
|
199
|
+
let permit_data = ctx
|
|
200
|
+
.permit()
|
|
201
|
+
.user_data::<BridgePermitData>()
|
|
202
|
+
.map(|d| d.permit.clone());
|
|
203
|
+
// Get the slot info as bytes
|
|
204
|
+
let slot_info_bytes = ctx.info().encode_to_vec();
|
|
205
|
+
|
|
206
|
+
self.channel.send(move |mut cx| {
|
|
207
|
+
let context = JsObject::new(&mut cx);
|
|
208
|
+
if let Some(permit_obj) = permit_data {
|
|
209
|
+
let ph: Handle<JsObject> = permit_obj.to_inner(&mut cx);
|
|
210
|
+
let pd = ph.get_value(&mut cx, PERMIT_DATA_FIELD)?;
|
|
211
|
+
context.set(&mut cx, "permit", pd)?;
|
|
212
|
+
}
|
|
213
|
+
let slot_info = JsBuffer::from_slice(&mut cx, &slot_info_bytes)?;
|
|
214
|
+
context.set(&mut cx, "slotInfo", slot_info)?;
|
|
215
|
+
let context = context.as_value(&mut cx);
|
|
216
|
+
|
|
217
|
+
let this = (*inner).clone(&mut cx).into_inner(&mut cx);
|
|
218
|
+
let val = cb.to_inner(&mut cx).call(&mut cx, this, [context])?;
|
|
219
|
+
if val.is_a::<JsUndefined, _>(&mut cx) {
|
|
220
|
+
return Ok(None);
|
|
221
|
+
}
|
|
222
|
+
let as_obj = val.downcast_or_throw::<JsObject, _>(&mut cx)?;
|
|
223
|
+
Ok(Some(as_obj.root(&mut cx)))
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
fn release_slot(&self, ctx: &dyn SlotReleaseContext<SlotKind = Self::SlotKind>) {
|
|
228
|
+
let inner = self.inner.clone();
|
|
229
|
+
let cb = self.release_cb.clone();
|
|
230
|
+
let permit_data = ctx
|
|
231
|
+
.permit()
|
|
232
|
+
.user_data::<BridgePermitData>()
|
|
233
|
+
.map(|d| d.permit.clone());
|
|
234
|
+
// Get the slot info as bytes
|
|
235
|
+
let slot_info_bytes = ctx.info().map(|m| m.encode_to_vec());
|
|
236
|
+
|
|
237
|
+
self.channel.send(move |mut cx| {
|
|
238
|
+
let context = JsObject::new(&mut cx);
|
|
239
|
+
if let Some(permit_obj) = permit_data {
|
|
240
|
+
let ph: Handle<JsObject> = permit_obj.to_inner(&mut cx);
|
|
241
|
+
let pd = ph.get_value(&mut cx, PERMIT_DATA_FIELD)?;
|
|
242
|
+
context.set(&mut cx, "permit", pd)?;
|
|
243
|
+
}
|
|
244
|
+
if let Some(slot_info_bytes) = slot_info_bytes {
|
|
245
|
+
let slot_info = JsBuffer::from_slice(&mut cx, &slot_info_bytes)?;
|
|
246
|
+
context.set(&mut cx, "slotInfo", slot_info)?;
|
|
247
|
+
}
|
|
248
|
+
let context = context.as_value(&mut cx);
|
|
249
|
+
|
|
250
|
+
let this = (*inner).clone(&mut cx).into_inner(&mut cx);
|
|
251
|
+
let val = cb.to_inner(&mut cx).call(&mut cx, this, [context])?;
|
|
252
|
+
if val.is_a::<JsUndefined, _>(&mut cx) {
|
|
253
|
+
return Ok(None);
|
|
254
|
+
}
|
|
255
|
+
let as_obj = val.downcast_or_throw::<JsObject, _>(&mut cx)?;
|
|
256
|
+
Ok(Some(as_obj.root(&mut cx)))
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
impl<SK: SlotKind> SlotSupplierBridge<SK> {
|
|
262
|
+
fn mk_reserve_ctx<'a, C: Context<'a>>(
|
|
263
|
+
task_queue: String,
|
|
264
|
+
worker_identity: String,
|
|
265
|
+
worker_build_id: String,
|
|
266
|
+
is_sticky: bool,
|
|
267
|
+
cx: &mut C,
|
|
268
|
+
) -> NeonResult<Handle<'a, JsValue>> {
|
|
269
|
+
let context = JsObject::new(cx);
|
|
270
|
+
let slottype = cx.string(match SK::kind() {
|
|
271
|
+
SlotKindType::Workflow => "workflow",
|
|
272
|
+
SlotKindType::Activity => "activity",
|
|
273
|
+
SlotKindType::LocalActivity => "local-activity",
|
|
274
|
+
});
|
|
275
|
+
context.set(cx, "slotType", slottype)?;
|
|
276
|
+
let tq = cx.string(task_queue);
|
|
277
|
+
context.set(cx, "taskQueue", tq)?;
|
|
278
|
+
let wid = cx.string(worker_identity);
|
|
279
|
+
context.set(cx, "workerIdentity", wid)?;
|
|
280
|
+
let bid = cx.string(worker_build_id);
|
|
281
|
+
context.set(cx, "workerBuildId", bid)?;
|
|
282
|
+
let is_sticky = cx.boolean(is_sticky);
|
|
283
|
+
context.set(cx, "isSticky", is_sticky)?;
|
|
284
|
+
let context = context.as_value(cx);
|
|
285
|
+
Ok(context)
|
|
286
|
+
}
|
|
287
|
+
}
|
package/src/conversions.rs
CHANGED
|
@@ -5,8 +5,10 @@ use neon::{
|
|
|
5
5
|
prelude::*,
|
|
6
6
|
types::{JsBoolean, JsNumber, JsString},
|
|
7
7
|
};
|
|
8
|
+
use slot_supplier_bridge::SlotSupplierBridge;
|
|
8
9
|
use std::{collections::HashMap, net::SocketAddr, sync::Arc, time::Duration};
|
|
9
10
|
use temporal_client::HttpConnectProxyOptions;
|
|
11
|
+
use temporal_sdk_core::api::worker::SlotKind;
|
|
10
12
|
use temporal_sdk_core::{
|
|
11
13
|
api::telemetry::{Logger, MetricTemporality, TelemetryOptions, TelemetryOptionsBuilder},
|
|
12
14
|
api::{
|
|
@@ -25,6 +27,8 @@ use temporal_sdk_core::{
|
|
|
25
27
|
TlsConfig, TunerHolderOptionsBuilder, Url,
|
|
26
28
|
};
|
|
27
29
|
|
|
30
|
+
mod slot_supplier_bridge;
|
|
31
|
+
|
|
28
32
|
pub enum EphemeralServerConfig {
|
|
29
33
|
TestServer(TestServerConfig),
|
|
30
34
|
DevServer(TemporalDevServerConfig),
|
|
@@ -65,11 +69,11 @@ pub(crate) trait ObjectHandleConversionsExt {
|
|
|
65
69
|
&self,
|
|
66
70
|
cx: &mut FunctionContext,
|
|
67
71
|
) -> NeonResult<HashMap<String, String>>;
|
|
68
|
-
fn
|
|
69
|
-
|
|
72
|
+
fn into_slot_supplier<SK: SlotKind + Send + Sync + 'static>(
|
|
73
|
+
self,
|
|
70
74
|
cx: &mut FunctionContext,
|
|
71
75
|
rbo: &mut Option<ResourceBasedSlotsOptions>,
|
|
72
|
-
) -> NeonResult<SlotSupplierOptions
|
|
76
|
+
) -> NeonResult<SlotSupplierOptions<SK>>;
|
|
73
77
|
}
|
|
74
78
|
|
|
75
79
|
impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
|
|
@@ -409,18 +413,18 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
|
|
|
409
413
|
if let Some(wf_slot_supp) =
|
|
410
414
|
js_optional_getter!(cx, &tuner, "workflowTaskSlotSupplier", JsObject)
|
|
411
415
|
{
|
|
412
|
-
tuner_holder.workflow_slot_options(wf_slot_supp.
|
|
416
|
+
tuner_holder.workflow_slot_options(wf_slot_supp.into_slot_supplier(cx, &mut rbo)?);
|
|
413
417
|
}
|
|
414
418
|
if let Some(act_slot_supp) =
|
|
415
419
|
js_optional_getter!(cx, &tuner, "activityTaskSlotSupplier", JsObject)
|
|
416
420
|
{
|
|
417
|
-
tuner_holder.activity_slot_options(act_slot_supp.
|
|
421
|
+
tuner_holder.activity_slot_options(act_slot_supp.into_slot_supplier(cx, &mut rbo)?);
|
|
418
422
|
}
|
|
419
423
|
if let Some(local_act_slot_supp) =
|
|
420
424
|
js_optional_getter!(cx, &tuner, "localActivityTaskSlotSupplier", JsObject)
|
|
421
425
|
{
|
|
422
426
|
tuner_holder.local_activity_slot_options(
|
|
423
|
-
local_act_slot_supp.
|
|
427
|
+
local_act_slot_supp.into_slot_supplier(cx, &mut rbo)?,
|
|
424
428
|
);
|
|
425
429
|
}
|
|
426
430
|
if let Some(rbo) = rbo {
|
|
@@ -567,20 +571,20 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
|
|
|
567
571
|
}
|
|
568
572
|
}
|
|
569
573
|
|
|
570
|
-
fn
|
|
571
|
-
|
|
574
|
+
fn into_slot_supplier<SK: SlotKind + Send + Sync + 'static>(
|
|
575
|
+
self,
|
|
572
576
|
cx: &mut FunctionContext,
|
|
573
577
|
rbo: &mut Option<ResourceBasedSlotsOptions>,
|
|
574
|
-
) -> NeonResult<SlotSupplierOptions
|
|
575
|
-
match js_value_getter!(cx, self, "type", JsString).as_str() {
|
|
578
|
+
) -> NeonResult<SlotSupplierOptions<SK>> {
|
|
579
|
+
match js_value_getter!(cx, &self, "type", JsString).as_str() {
|
|
576
580
|
"fixed-size" => Ok(SlotSupplierOptions::FixedSize {
|
|
577
|
-
slots: js_value_getter!(cx, self, "numSlots", JsNumber) as usize,
|
|
581
|
+
slots: js_value_getter!(cx, &self, "numSlots", JsNumber) as usize,
|
|
578
582
|
}),
|
|
579
583
|
"resource-based" => {
|
|
580
|
-
let min_slots = js_value_getter!(cx, self, "minimumSlots", JsNumber);
|
|
581
|
-
let max_slots = js_value_getter!(cx, self, "maximumSlots", JsNumber);
|
|
582
|
-
let ramp_throttle = js_value_getter!(cx, self, "rampThrottleMs", JsNumber) as u64;
|
|
583
|
-
if let Some(tuner_opts) = js_optional_getter!(cx, self, "tunerOptions", JsObject) {
|
|
584
|
+
let min_slots = js_value_getter!(cx, &self, "minimumSlots", JsNumber);
|
|
585
|
+
let max_slots = js_value_getter!(cx, &self, "maximumSlots", JsNumber);
|
|
586
|
+
let ramp_throttle = js_value_getter!(cx, &self, "rampThrottleMs", JsNumber) as u64;
|
|
587
|
+
if let Some(tuner_opts) = js_optional_getter!(cx, &self, "tunerOptions", JsObject) {
|
|
584
588
|
let target_mem =
|
|
585
589
|
js_value_getter!(cx, &tuner_opts, "targetMemoryUsage", JsNumber);
|
|
586
590
|
let target_cpu = js_value_getter!(cx, &tuner_opts, "targetCpuUsage", JsNumber);
|
|
@@ -603,6 +607,10 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
|
|
|
603
607
|
),
|
|
604
608
|
))
|
|
605
609
|
}
|
|
610
|
+
"custom" => {
|
|
611
|
+
let ssb = SlotSupplierBridge::new(cx, self)?;
|
|
612
|
+
Ok(SlotSupplierOptions::Custom(Arc::new(ssb)))
|
|
613
|
+
}
|
|
606
614
|
_ => cx.throw_type_error("Invalid slot supplier type"),
|
|
607
615
|
}
|
|
608
616
|
}
|
package/src/helpers.rs
CHANGED
|
@@ -106,6 +106,23 @@ macro_rules! js_optional_getter {
|
|
|
106
106
|
|
|
107
107
|
pub(crate) use js_optional_getter;
|
|
108
108
|
|
|
109
|
+
macro_rules! js_getter {
|
|
110
|
+
($js_cx:expr, $js_obj:expr, $prop_name:expr, $js_type:ty) => {
|
|
111
|
+
match get_optional($js_cx, $js_obj, $prop_name) {
|
|
112
|
+
None => $js_cx.throw_type_error(format!("{} must be defined", $prop_name))?,
|
|
113
|
+
Some(val) => {
|
|
114
|
+
if val.is_a::<$js_type, _>($js_cx) {
|
|
115
|
+
val.downcast_or_throw::<$js_type, _>($js_cx)?
|
|
116
|
+
} else {
|
|
117
|
+
$js_cx.throw_type_error(format!("Invalid {}", $prop_name))?
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
pub(crate) use js_getter;
|
|
125
|
+
|
|
109
126
|
macro_rules! js_optional_value_getter {
|
|
110
127
|
($js_cx:expr, $js_obj:expr, $prop_name:expr, $js_type:ty) => {
|
|
111
128
|
js_optional_getter!($js_cx, $js_obj, $prop_name, $js_type).map(|v| v.value($js_cx))
|
|
@@ -199,7 +216,7 @@ pub fn serde_value_to_js_value<'a>(
|
|
|
199
216
|
serde_json::Value::Bool(b) => Ok(cx.boolean(b).upcast()),
|
|
200
217
|
serde_json::Value::Null => Ok(cx.null().upcast()),
|
|
201
218
|
serde_json::Value::Array(vec) => {
|
|
202
|
-
let arr: Handle<'a, JsArray> = JsArray::new(cx, vec.len()
|
|
219
|
+
let arr: Handle<'a, JsArray> = JsArray::new(cx, vec.len());
|
|
203
220
|
for (i, v) in vec.into_iter().enumerate() {
|
|
204
221
|
let v = serde_value_to_js_value(cx, v)?;
|
|
205
222
|
arr.set(cx, i as u32, v)?;
|
|
@@ -247,6 +264,23 @@ fn snake_to_camel(input: String) -> String {
|
|
|
247
264
|
}
|
|
248
265
|
}
|
|
249
266
|
|
|
267
|
+
#[allow(dead_code)]
|
|
268
|
+
// Useful to help debug JSObject contents
|
|
269
|
+
pub fn log_js_object<'a, 'b, C: Context<'b>>(cx: &mut C, js_object: &Handle<'a, JsObject>) {
|
|
270
|
+
let global = cx.global_object();
|
|
271
|
+
let console = global
|
|
272
|
+
.get::<JsObject, _, _>(cx, "console")
|
|
273
|
+
.expect("Failed to get console object");
|
|
274
|
+
|
|
275
|
+
let log = console
|
|
276
|
+
.get::<JsFunction, _, _>(cx, "log")
|
|
277
|
+
.expect("Failed to get log function");
|
|
278
|
+
|
|
279
|
+
let args = vec![js_object.upcast()]; // Upcast js_object to JsValue
|
|
280
|
+
log.call(cx, console, args)
|
|
281
|
+
.expect("Failed to call console.log");
|
|
282
|
+
}
|
|
283
|
+
|
|
250
284
|
#[cfg(test)]
|
|
251
285
|
mod tests {
|
|
252
286
|
use super::*;
|
package/src/runtime.rs
CHANGED
|
@@ -14,7 +14,7 @@ use temporal_sdk_core::{
|
|
|
14
14
|
ephemeral_server::EphemeralServer as CoreEphemeralServer,
|
|
15
15
|
init_replay_worker, init_worker,
|
|
16
16
|
replay::{HistoryForReplay, ReplayWorkerInput},
|
|
17
|
-
ClientOptions, CoreRuntime, RetryClient, WorkerConfig,
|
|
17
|
+
ClientOptions, CoreRuntime, RetryClient, TokioRuntimeBuilder, WorkerConfig,
|
|
18
18
|
};
|
|
19
19
|
use tokio::sync::{
|
|
20
20
|
mpsc::{channel, unbounded_channel, Sender, UnboundedReceiver, UnboundedSender},
|
|
@@ -126,6 +126,10 @@ pub fn start_bridge_loop(
|
|
|
126
126
|
tokio_builder.enable_all().thread_name("core");
|
|
127
127
|
let telem_opts = telemetry_options.0;
|
|
128
128
|
let meter_maker = telemetry_options.1;
|
|
129
|
+
let tokio_builder: TokioRuntimeBuilder<Box<dyn Fn() + Send + Sync>> = TokioRuntimeBuilder {
|
|
130
|
+
inner: tokio_builder,
|
|
131
|
+
lang_on_thread_start: None,
|
|
132
|
+
};
|
|
129
133
|
let mut core_runtime =
|
|
130
134
|
CoreRuntime::new(telem_opts, tokio_builder).expect("Failed to create CoreRuntime");
|
|
131
135
|
|
|
@@ -254,7 +258,7 @@ pub fn start_bridge_loop(
|
|
|
254
258
|
worker,
|
|
255
259
|
channel,
|
|
256
260
|
callback,
|
|
257
|
-
None
|
|
261
|
+
None,
|
|
258
262
|
));
|
|
259
263
|
}
|
|
260
264
|
Err(err) => send_error(channel.clone(), callback, move |cx| {
|
|
@@ -274,7 +278,7 @@ pub fn start_bridge_loop(
|
|
|
274
278
|
worker,
|
|
275
279
|
channel.clone(),
|
|
276
280
|
callback,
|
|
277
|
-
Some(tunnel)
|
|
281
|
+
Some(tunnel),
|
|
278
282
|
));
|
|
279
283
|
}
|
|
280
284
|
Err(err) => send_error(channel.clone(), callback, move |cx| {
|
package/src/worker.rs
CHANGED
|
@@ -226,12 +226,12 @@ pub fn worker_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
|
226
226
|
let worker_options = cx.argument::<JsObject>(1)?;
|
|
227
227
|
let callback = cx.argument::<JsFunction>(2)?;
|
|
228
228
|
|
|
229
|
-
let config = worker_options.as_worker_config(&mut cx)?;
|
|
230
229
|
match client.borrow().as_ref() {
|
|
231
230
|
None => {
|
|
232
231
|
callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Client")?;
|
|
233
232
|
}
|
|
234
233
|
Some(client) => {
|
|
234
|
+
let config = worker_options.as_worker_config(&mut cx)?;
|
|
235
235
|
let request = RuntimeRequest::InitWorker {
|
|
236
236
|
client: client.core_client.clone(),
|
|
237
237
|
config,
|
package/ts/errors.ts
CHANGED
|
@@ -18,7 +18,14 @@ export class TransportError extends Error {}
|
|
|
18
18
|
* Something unexpected happened, considered fatal
|
|
19
19
|
*/
|
|
20
20
|
@SymbolBasedInstanceOfError('UnexpectedError')
|
|
21
|
-
export class UnexpectedError extends Error {
|
|
21
|
+
export class UnexpectedError extends Error {
|
|
22
|
+
constructor(
|
|
23
|
+
message: string,
|
|
24
|
+
public cause?: unknown
|
|
25
|
+
) {
|
|
26
|
+
super(message);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
22
29
|
|
|
23
30
|
export { IllegalStateError };
|
|
24
31
|
|
|
@@ -47,7 +54,7 @@ export function convertFromNamedError(e: unknown, keepStackTrace: boolean): unkn
|
|
|
47
54
|
return newerr;
|
|
48
55
|
|
|
49
56
|
case 'UnexpectedError':
|
|
50
|
-
newerr = new UnexpectedError(e.message);
|
|
57
|
+
newerr = new UnexpectedError(e.message, e);
|
|
51
58
|
newerr.stack = keepStackTrace ? e.stack : undefined;
|
|
52
59
|
return newerr;
|
|
53
60
|
}
|
package/ts/index.ts
CHANGED
|
@@ -8,6 +8,15 @@ export {
|
|
|
8
8
|
ResourceBasedSlotOptions,
|
|
9
9
|
ResourceBasedTunerOptions,
|
|
10
10
|
FixedSizeSlotSupplier,
|
|
11
|
+
CustomSlotSupplier,
|
|
12
|
+
SlotInfo,
|
|
13
|
+
WorkflowSlotInfo,
|
|
14
|
+
ActivitySlotInfo,
|
|
15
|
+
LocalActivitySlotInfo,
|
|
16
|
+
SlotMarkUsedContext,
|
|
17
|
+
SlotPermit,
|
|
18
|
+
SlotReserveContext,
|
|
19
|
+
SlotReleaseContext,
|
|
11
20
|
} from './worker-tuner';
|
|
12
21
|
|
|
13
22
|
export type { TLSConfig, ProxyConfig, HttpConnectProxyConfig };
|
|
@@ -98,7 +107,7 @@ export interface ClientOptions {
|
|
|
98
107
|
* @experimental
|
|
99
108
|
*/
|
|
100
109
|
export interface ConsoleLogger {
|
|
101
|
-
console: {}; // eslint-disable-line @typescript-eslint/
|
|
110
|
+
console: {}; // eslint-disable-line @typescript-eslint/no-empty-object-type
|
|
102
111
|
}
|
|
103
112
|
|
|
104
113
|
/**
|
|
@@ -134,7 +143,13 @@ export interface OtelCollectorExporter {
|
|
|
134
143
|
/**
|
|
135
144
|
* URL of a gRPC OpenTelemetry collector.
|
|
136
145
|
*
|
|
137
|
-
*
|
|
146
|
+
* Syntax should generally look like `http://server:4317` (the `grpc://` is also fine). Core's OTLP
|
|
147
|
+
* metric exporter does not support the 'OTLP/HTTP' protocol (e.g. `http://server:4318/v1/metrics`).
|
|
148
|
+
* For greater flexibility, you may setup an OTel collector running as a sidecar (e.g. to proxy
|
|
149
|
+
* OTLP/gRPC requests to a remote OTLP/HTTP endpoint).
|
|
150
|
+
*
|
|
151
|
+
* @format Starts with "grpc://" or "http://" for an unsecured connection (typical),
|
|
152
|
+
* or "grpcs://" or "https://" for a TLS connection.
|
|
138
153
|
* @note The `OTEL_EXPORTER_OTLP_ENDPOINT` environment variable, if set, will override this property.
|
|
139
154
|
*/
|
|
140
155
|
url: string;
|
|
@@ -269,8 +284,8 @@ export type CompiledTelemetryOptions = {
|
|
|
269
284
|
logging: {
|
|
270
285
|
filter: string;
|
|
271
286
|
} & (
|
|
272
|
-
| { console: {} /* eslint-disable-line @typescript-eslint/
|
|
273
|
-
| { forward: {} /* eslint-disable-line @typescript-eslint/
|
|
287
|
+
| { console: {} /* eslint-disable-line @typescript-eslint/no-empty-object-type */ }
|
|
288
|
+
| { forward: {} /* eslint-disable-line @typescript-eslint/no-empty-object-type */ }
|
|
274
289
|
);
|
|
275
290
|
metrics?: {
|
|
276
291
|
temporality?: 'cumulative' | 'delta';
|