@temporalio/core-bridge 1.10.2 → 1.11.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.
Files changed (126) hide show
  1. package/Cargo.lock +563 -676
  2. package/Cargo.toml +3 -3
  3. package/lib/index.d.ts +16 -5
  4. package/lib/index.js.map +1 -1
  5. package/lib/worker-tuner.d.ts +57 -0
  6. package/lib/worker-tuner.js +3 -0
  7. package/lib/worker-tuner.js.map +1 -0
  8. package/package.json +3 -3
  9. package/releases/aarch64-apple-darwin/index.node +0 -0
  10. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  11. package/releases/x86_64-apple-darwin/index.node +0 -0
  12. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  13. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  14. package/sdk-core/.github/workflows/heavy.yml +6 -11
  15. package/sdk-core/.github/workflows/per-pr.yml +23 -41
  16. package/sdk-core/Cargo.toml +5 -5
  17. package/sdk-core/README.md +2 -0
  18. package/sdk-core/client/Cargo.toml +4 -2
  19. package/sdk-core/client/src/lib.rs +60 -17
  20. package/sdk-core/client/src/metrics.rs +1 -1
  21. package/sdk-core/client/src/proxy.rs +17 -12
  22. package/sdk-core/client/src/raw.rs +218 -69
  23. package/sdk-core/client/src/retry.rs +19 -9
  24. package/sdk-core/core/Cargo.toml +12 -12
  25. package/sdk-core/core/src/abstractions.rs +3 -3
  26. package/sdk-core/core/src/core_tests/activity_tasks.rs +2 -1
  27. package/sdk-core/core/src/core_tests/determinism.rs +1 -1
  28. package/sdk-core/core/src/core_tests/local_activities.rs +73 -10
  29. package/sdk-core/core/src/core_tests/queries.rs +2 -1
  30. package/sdk-core/core/src/core_tests/updates.rs +162 -4
  31. package/sdk-core/core/src/core_tests/workers.rs +38 -2
  32. package/sdk-core/core/src/core_tests/workflow_tasks.rs +158 -27
  33. package/sdk-core/core/src/internal_flags.rs +17 -7
  34. package/sdk-core/core/src/lib.rs +9 -3
  35. package/sdk-core/core/src/pollers/poll_buffer.rs +1 -10
  36. package/sdk-core/core/src/protosext/mod.rs +0 -1
  37. package/sdk-core/core/src/protosext/protocol_messages.rs +105 -16
  38. package/sdk-core/core/src/retry_logic.rs +22 -2
  39. package/sdk-core/core/src/telemetry/otel.rs +44 -12
  40. package/sdk-core/core/src/test_help/mod.rs +65 -12
  41. package/sdk-core/core/src/worker/activities/local_activities.rs +1 -4
  42. package/sdk-core/core/src/worker/activities.rs +3 -4
  43. package/sdk-core/core/src/worker/client/mocks.rs +7 -6
  44. package/sdk-core/core/src/worker/client.rs +11 -2
  45. package/sdk-core/core/src/worker/mod.rs +49 -24
  46. package/sdk-core/core/src/worker/tuner/resource_based.rs +48 -48
  47. package/sdk-core/core/src/worker/tuner.rs +124 -4
  48. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +1 -1
  49. package/sdk-core/core/src/worker/workflow/history_update.rs +11 -2
  50. package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +18 -3
  51. package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +1 -0
  52. package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +1 -0
  53. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +1 -0
  54. package/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +1 -0
  55. package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +1 -0
  56. package/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +1 -0
  57. package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +4 -4
  58. package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +1 -0
  59. package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +1 -0
  60. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +1 -0
  61. package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +3 -1
  62. package/sdk-core/core/src/worker/workflow/machines/update_state_machine.rs +38 -28
  63. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +4 -2
  64. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +95 -71
  65. package/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +1 -1
  66. package/sdk-core/core/src/worker/workflow/managed_run.rs +214 -14
  67. package/sdk-core/core/src/worker/workflow/mod.rs +49 -36
  68. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +1 -2
  69. package/sdk-core/core-api/src/errors.rs +13 -7
  70. package/sdk-core/core-api/src/lib.rs +9 -1
  71. package/sdk-core/sdk/Cargo.toml +1 -1
  72. package/sdk-core/sdk/src/activity_context.rs +3 -4
  73. package/sdk-core/sdk/src/lib.rs +96 -49
  74. package/sdk-core/sdk/src/workflow_context/options.rs +8 -4
  75. package/sdk-core/sdk/src/workflow_context.rs +53 -49
  76. package/sdk-core/sdk/src/workflow_future.rs +10 -4
  77. package/sdk-core/sdk-core-protos/Cargo.toml +4 -3
  78. package/sdk-core/sdk-core-protos/build.rs +2 -0
  79. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/.github/workflows/build.yaml +18 -0
  80. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/LICENSE +21 -0
  81. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/Makefile +59 -0
  82. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/README.md +25 -0
  83. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/VERSION +1 -0
  84. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/buf.gen.yaml +14 -0
  85. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/buf.lock +8 -0
  86. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/buf.yaml +9 -0
  87. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/cloudservice/v1/request_response.proto +520 -0
  88. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/cloudservice/v1/service.proto +263 -0
  89. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/identity/v1/message.proto +173 -0
  90. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/namespace/v1/message.proto +164 -0
  91. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/operation/v1/message.proto +36 -0
  92. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/region/v1/message.proto +22 -0
  93. package/sdk-core/sdk-core-protos/protos/api_upstream/.github/workflows/trigger-api-go-update.yml +50 -8
  94. package/sdk-core/sdk-core-protos/protos/api_upstream/.gitmodules +3 -0
  95. package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv2.json +132 -54
  96. package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv3.yaml +177 -81
  97. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/command/v1/message.proto +13 -0
  98. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +2 -0
  99. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +8 -3
  100. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/history/v1/message.proto +10 -0
  101. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/nexus/v1/message.proto +3 -3
  102. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +6 -6
  103. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/enhanced_stack_trace.proto +96 -0
  104. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/user_metadata.proto +49 -0
  105. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/workflow_metadata.proto +6 -7
  106. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +55 -24
  107. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflow/v1/message.proto +7 -0
  108. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +21 -4
  109. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +64 -45
  110. package/sdk-core/sdk-core-protos/src/history_builder.rs +8 -1
  111. package/sdk-core/sdk-core-protos/src/lib.rs +40 -10
  112. package/sdk-core/test-utils/src/canned_histories.rs +1 -1
  113. package/sdk-core/tests/fuzzy_workflow.rs +4 -2
  114. package/sdk-core/tests/heavy_tests.rs +3 -3
  115. package/sdk-core/tests/integ_tests/activity_functions.rs +2 -2
  116. package/sdk-core/tests/integ_tests/client_tests.rs +234 -6
  117. package/sdk-core/tests/integ_tests/update_tests.rs +180 -47
  118. package/sdk-core/tests/integ_tests/worker_tests.rs +32 -0
  119. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +47 -3
  120. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +10 -10
  121. package/sdk-core/tests/main.rs +1 -0
  122. package/src/conversions.rs +84 -11
  123. package/src/runtime.rs +5 -17
  124. package/src/worker.rs +27 -6
  125. package/ts/index.ts +24 -5
  126. package/ts/worker-tuner.ts +71 -0
@@ -7,7 +7,7 @@ use std::{
7
7
  };
8
8
  use temporal_client::WorkflowOptions;
9
9
  use temporal_sdk::{
10
- interceptors::WorkerInterceptor, ActContext, ActivityCancelledError, CancellableFuture,
10
+ interceptors::WorkerInterceptor, ActContext, ActivityError, CancellableFuture,
11
11
  LocalActivityOptions, WfContext, WorkflowResult,
12
12
  };
13
13
  use temporal_sdk_core::replay::HistoryForReplay;
@@ -172,7 +172,7 @@ async fn local_act_retry_timer_backoff() {
172
172
  Ok(().into())
173
173
  });
174
174
  worker.register_activity("echo", |_: ActContext, _: String| async {
175
- Result::<(), _>::Err(anyhow!("Oh no I failed!"))
175
+ Result::<(), _>::Err(anyhow!("Oh no I failed!").into())
176
176
  });
177
177
 
178
178
  let run_id = worker
@@ -223,7 +223,7 @@ async fn cancel_immediate(#[case] cancel_type: ActivityCancellationType) {
223
223
  tokio::select! {
224
224
  _ = tokio::time::sleep(Duration::from_secs(10)) => {},
225
225
  _ = ctx.cancelled() => {
226
- return Err(anyhow!(ActivityCancelledError::default()))
226
+ return Err(ActivityError::cancelled())
227
227
  }
228
228
  _ = manual_cancel_act.cancelled() => {}
229
229
  }
@@ -326,22 +326,22 @@ async fn cancel_after_act_starts(
326
326
  async move {
327
327
  if cancel_on_backoff.is_some() {
328
328
  if ctx.is_cancelled() {
329
- return Err(anyhow!(ActivityCancelledError::default()));
329
+ return Err(ActivityError::cancelled());
330
330
  }
331
331
  // Just fail constantly so we get stuck on the backoff timer
332
- return Err(anyhow!("Oh no I failed!"));
332
+ return Err(anyhow!("Oh no I failed!").into());
333
333
  } else {
334
334
  tokio::select! {
335
335
  _ = tokio::time::sleep(Duration::from_secs(100)) => {},
336
336
  _ = ctx.cancelled() => {
337
- return Err(anyhow!(ActivityCancelledError::default()))
337
+ return Err(ActivityError::cancelled())
338
338
  }
339
339
  _ = manual_cancel_act.cancelled() => {
340
340
  return Ok(())
341
341
  }
342
342
  }
343
343
  }
344
- Err(anyhow!("Oh no I failed!"))
344
+ Err(anyhow!("Oh no I failed!").into())
345
345
  }
346
346
  });
347
347
 
@@ -405,7 +405,7 @@ async fn x_to_close_timeout(#[case] is_schedule: bool) {
405
405
  tokio::select! {
406
406
  _ = tokio::time::sleep(Duration::from_secs(100)) => {},
407
407
  _ = ctx.cancelled() => {
408
- return Err(anyhow!(ActivityCancelledError::default()))
408
+ return Err(ActivityError::cancelled())
409
409
  }
410
410
  };
411
411
  Ok(())
@@ -452,7 +452,7 @@ async fn schedule_to_close_timeout_across_timer_backoff(#[case] cached: bool) {
452
452
  let num_attempts: &'static _ = Box::leak(Box::new(AtomicU8::new(0)));
453
453
  worker.register_activity("echo", move |_: ActContext, _: String| async {
454
454
  num_attempts.fetch_add(1, Ordering::Relaxed);
455
- Result::<(), _>::Err(anyhow!("Oh no I failed!"))
455
+ Result::<(), _>::Err(anyhow!("Oh no I failed!").into())
456
456
  });
457
457
 
458
458
  starter.start_with_worker(wf_name, &mut worker).await;
@@ -528,7 +528,7 @@ async fn timer_backoff_concurrent_with_non_timer_backoff() {
528
528
  Ok(().into())
529
529
  });
530
530
  worker.register_activity("echo", |_: ActContext, _: String| async {
531
- Result::<(), _>::Err(anyhow!("Oh no I failed!"))
531
+ Result::<(), _>::Err(anyhow!("Oh no I failed!").into())
532
532
  });
533
533
 
534
534
  starter.start_with_worker(wf_name, &mut worker).await;
@@ -16,6 +16,7 @@ mod integ_tests {
16
16
  mod queries_tests;
17
17
  mod update_tests;
18
18
  mod visibility_tests;
19
+ mod worker_tests;
19
20
  mod workflow_tests;
20
21
 
21
22
  use std::str::FromStr;
@@ -20,7 +20,9 @@ use temporal_sdk_core::{
20
20
  TestServerConfigBuilder,
21
21
  },
22
22
  telemetry::{build_otlp_metric_exporter, start_prometheus_metric_exporter},
23
- ClientOptions, ClientOptionsBuilder, ClientTlsConfig, RetryConfig, TlsConfig, Url,
23
+ ClientOptions, ClientOptionsBuilder, ClientTlsConfig, ResourceBasedSlotsOptions,
24
+ ResourceBasedSlotsOptionsBuilder, ResourceSlotOptions, RetryConfig, SlotSupplierOptions,
25
+ TlsConfig, TunerHolderOptionsBuilder, Url,
24
26
  };
25
27
 
26
28
  pub enum EphemeralServerConfig {
@@ -49,7 +51,7 @@ type BoxedMeterMaker = Box<dyn FnOnce() -> Result<Arc<dyn CoreMeter>, String> +
49
51
 
50
52
  pub(crate) type TelemOptsRes = (TelemetryOptions, Option<BoxedMeterMaker>);
51
53
 
52
- pub trait ObjectHandleConversionsExt {
54
+ pub(crate) trait ObjectHandleConversionsExt {
53
55
  fn set_default(&self, cx: &mut FunctionContext, key: &str, value: &str) -> NeonResult<()>;
54
56
  fn as_client_options(&self, ctx: &mut FunctionContext) -> NeonResult<ClientOptions>;
55
57
  fn as_telemetry_options(&self, cx: &mut FunctionContext) -> NeonResult<TelemOptsRes>;
@@ -63,6 +65,11 @@ pub trait ObjectHandleConversionsExt {
63
65
  &self,
64
66
  cx: &mut FunctionContext,
65
67
  ) -> NeonResult<HashMap<String, String>>;
68
+ fn as_slot_supplier(
69
+ &self,
70
+ cx: &mut FunctionContext,
71
+ rbo: &mut Option<ResourceBasedSlotsOptions>,
72
+ ) -> NeonResult<SlotSupplierOptions>;
66
73
  }
67
74
 
68
75
  impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
@@ -354,12 +361,6 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
354
361
  let task_queue = js_value_getter!(cx, self, "taskQueue", JsString);
355
362
  let enable_remote_activities =
356
363
  js_value_getter!(cx, self, "enableNonLocalActivities", JsBoolean);
357
- let max_outstanding_activities =
358
- js_value_getter!(cx, self, "maxConcurrentActivityTaskExecutions", JsNumber) as usize;
359
- let max_outstanding_local_activities =
360
- js_value_getter!(cx, self, "maxConcurrentLocalActivityExecutions", JsNumber) as usize;
361
- let max_outstanding_workflow_tasks =
362
- js_value_getter!(cx, self, "maxConcurrentWorkflowTaskExecutions", JsNumber) as usize;
363
364
  let max_concurrent_wft_polls =
364
365
  js_value_getter!(cx, self, "maxConcurrentWorkflowTaskPolls", JsNumber) as usize;
365
366
  let max_concurrent_at_polls =
@@ -401,14 +402,46 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
401
402
  let nonsticky_to_sticky_poll_ratio =
402
403
  js_value_getter!(cx, self, "nonStickyToStickyPollRatio", JsNumber) as f32;
403
404
 
405
+ let tuner = if let Some(tuner) = js_optional_getter!(cx, self, "tuner", JsObject) {
406
+ let mut tuner_holder = TunerHolderOptionsBuilder::default();
407
+ let mut rbo = None;
408
+
409
+ if let Some(wf_slot_supp) =
410
+ js_optional_getter!(cx, &tuner, "workflowTaskSlotSupplier", JsObject)
411
+ {
412
+ tuner_holder.workflow_slot_options(wf_slot_supp.as_slot_supplier(cx, &mut rbo)?);
413
+ }
414
+ if let Some(act_slot_supp) =
415
+ js_optional_getter!(cx, &tuner, "activityTaskSlotSupplier", JsObject)
416
+ {
417
+ tuner_holder.activity_slot_options(act_slot_supp.as_slot_supplier(cx, &mut rbo)?);
418
+ }
419
+ if let Some(local_act_slot_supp) =
420
+ js_optional_getter!(cx, &tuner, "localActivityTaskSlotSupplier", JsObject)
421
+ {
422
+ tuner_holder.local_activity_slot_options(
423
+ local_act_slot_supp.as_slot_supplier(cx, &mut rbo)?,
424
+ );
425
+ }
426
+ if let Some(rbo) = rbo {
427
+ tuner_holder.resource_based_options(rbo);
428
+ }
429
+ match tuner_holder.build_tuner_holder() {
430
+ Err(e) => {
431
+ return cx.throw_error(format!("Invalid tuner options: {:?}", e));
432
+ }
433
+ Ok(th) => Arc::new(th),
434
+ }
435
+ } else {
436
+ return cx.throw_error("Missing tuner");
437
+ };
438
+
404
439
  match WorkerConfigBuilder::default()
405
440
  .worker_build_id(js_value_getter!(cx, self, "buildId", JsString))
406
441
  .client_identity_override(Some(js_value_getter!(cx, self, "identity", JsString)))
407
442
  .use_worker_versioning(js_value_getter!(cx, self, "useVersioning", JsBoolean))
408
443
  .no_remote_activities(!enable_remote_activities)
409
- .max_outstanding_workflow_tasks(max_outstanding_workflow_tasks)
410
- .max_outstanding_activities(max_outstanding_activities)
411
- .max_outstanding_local_activities(max_outstanding_local_activities)
444
+ .tuner(tuner)
412
445
  .max_concurrent_wft_polls(max_concurrent_wft_polls)
413
446
  .max_concurrent_at_polls(max_concurrent_at_polls)
414
447
  .nonsticky_to_sticky_poll_ratio(nonsticky_to_sticky_poll_ratio)
@@ -533,4 +566,44 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
533
566
  )),
534
567
  }
535
568
  }
569
+
570
+ fn as_slot_supplier(
571
+ &self,
572
+ cx: &mut FunctionContext,
573
+ rbo: &mut Option<ResourceBasedSlotsOptions>,
574
+ ) -> NeonResult<SlotSupplierOptions> {
575
+ match js_value_getter!(cx, self, "type", JsString).as_str() {
576
+ "fixed-size" => Ok(SlotSupplierOptions::FixedSize {
577
+ slots: js_value_getter!(cx, self, "numSlots", JsNumber) as usize,
578
+ }),
579
+ "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 target_mem =
585
+ js_value_getter!(cx, &tuner_opts, "targetMemoryUsage", JsNumber);
586
+ let target_cpu = js_value_getter!(cx, &tuner_opts, "targetCpuUsage", JsNumber);
587
+ *rbo = Some(
588
+ ResourceBasedSlotsOptionsBuilder::default()
589
+ .target_cpu_usage(target_cpu)
590
+ .target_mem_usage(target_mem)
591
+ .build()
592
+ .expect("Building ResourceBasedSlotsOptions can't fail"),
593
+ )
594
+ } else {
595
+ return cx
596
+ .throw_type_error("Resource based slot supplier requires tunerOptions");
597
+ };
598
+ Ok(SlotSupplierOptions::ResourceBased(
599
+ ResourceSlotOptions::new(
600
+ min_slots as usize,
601
+ max_slots as usize,
602
+ Duration::from_millis(ramp_throttle),
603
+ ),
604
+ ))
605
+ }
606
+ _ => cx.throw_type_error("Invalid slot supplier type"),
607
+ }
608
+ }
536
609
  }
package/src/runtime.rs CHANGED
@@ -250,15 +250,12 @@ pub fn start_bridge_loop(
250
250
  let client = (*client).clone();
251
251
  match init_worker(&core_runtime, config, client) {
252
252
  Ok(worker) => {
253
- let (tx, rx) = unbounded_channel();
254
253
  core_runtime.tokio_handle().spawn(start_worker_loop(
255
254
  worker,
256
- rx,
257
- channel.clone(),
255
+ channel,
256
+ callback,
257
+ None
258
258
  ));
259
- send_result(channel.clone(), callback, |cx| {
260
- Ok(cx.boxed(RefCell::new(Some(WorkerHandle { sender: tx }))))
261
- });
262
259
  }
263
260
  Err(err) => send_error(channel.clone(), callback, move |cx| {
264
261
  make_named_error_from_error(cx, UNEXPECTED_ERROR, err.deref())
@@ -273,21 +270,12 @@ pub fn start_bridge_loop(
273
270
  let (tunnel, stream) = HistoryForReplayTunnel::new(runtime);
274
271
  match init_replay_worker(ReplayWorkerInput::new(config, Box::pin(stream))) {
275
272
  Ok(worker) => {
276
- let (tx, rx) = unbounded_channel();
277
273
  core_runtime.tokio_handle().spawn(start_worker_loop(
278
274
  worker,
279
- rx,
280
275
  channel.clone(),
276
+ callback,
277
+ Some(tunnel)
281
278
  ));
282
- send_result(channel.clone(), callback, |cx| {
283
- let worker =
284
- cx.boxed(RefCell::new(Some(WorkerHandle { sender: tx })));
285
- let tunnel = cx.boxed(tunnel);
286
- let retme = cx.empty_object();
287
- retme.set(cx, "worker", worker)?;
288
- retme.set(cx, "pusher", tunnel)?;
289
- Ok(retme)
290
- })
291
279
  }
292
280
  Err(err) => send_error(channel.clone(), callback, move |cx| {
293
281
  make_named_error_from_error(cx, UNEXPECTED_ERROR, err.deref())
package/src/worker.rs CHANGED
@@ -18,7 +18,7 @@ use temporal_sdk_core::{
18
18
  },
19
19
  Worker as CoreWorker,
20
20
  };
21
- use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
21
+ use tokio::sync::mpsc::{unbounded_channel, UnboundedSender};
22
22
  use tokio_stream::wrappers::UnboundedReceiverStream;
23
23
 
24
24
  /// Worker struct, hold a reference for the channel sender responsible for sending requests from
@@ -70,9 +70,34 @@ pub enum WorkerRequest {
70
70
  /// Returns when the given channel is dropped.
71
71
  pub async fn start_worker_loop(
72
72
  worker: CoreWorker,
73
- rx: UnboundedReceiver<WorkerRequest>,
74
73
  channel: Arc<Channel>,
74
+ callback: Root<JsFunction>,
75
+ is_replay: Option<HistoryForReplayTunnel>,
75
76
  ) {
77
+ if is_replay.is_none() {
78
+ if let Err(e) = worker.validate().await {
79
+ send_error(channel, callback, move |cx| {
80
+ make_named_error_from_error(cx, TRANSPORT_ERROR, e)
81
+ });
82
+ return;
83
+ }
84
+ }
85
+ let (tx, rx) = unbounded_channel();
86
+ // Return the worker after validation has happened
87
+ if let Some(tunnel) = is_replay {
88
+ send_result(channel.clone(), callback, |cx| {
89
+ let worker = cx.boxed(RefCell::new(Some(WorkerHandle { sender: tx })));
90
+ let tunnel = cx.boxed(tunnel);
91
+ let retme = cx.empty_object();
92
+ retme.set(cx, "worker", worker)?;
93
+ retme.set(cx, "pusher", tunnel)?;
94
+ Ok(retme)
95
+ })
96
+ } else {
97
+ send_result(channel.clone(), callback, |cx| {
98
+ Ok(cx.boxed(RefCell::new(Some(WorkerHandle { sender: tx }))))
99
+ });
100
+ }
76
101
  UnboundedReceiverStream::new(rx)
77
102
  .for_each_concurrent(None, |request| {
78
103
  let worker = &worker;
@@ -158,10 +183,6 @@ async fn handle_poll_workflow_activation_request(
158
183
  send_error(channel, callback, move |cx| match err {
159
184
  PollWfError::ShutDown => make_named_error_from_error(cx, SHUTDOWN_ERROR, err),
160
185
  PollWfError::TonicError(_) => make_named_error_from_error(cx, TRANSPORT_ERROR, err),
161
- PollWfError::AutocompleteError(CompleteWfError::MalformedWorkflowCompletion {
162
- reason,
163
- ..
164
- }) => Ok(JsError::type_error(cx, reason)?),
165
186
  });
166
187
  }
167
188
  }
package/ts/index.ts CHANGED
@@ -1,5 +1,14 @@
1
1
  import { LogLevel, Duration } from '@temporalio/common';
2
2
  import type { TLSConfig, ProxyConfig, HttpConnectProxyConfig } from '@temporalio/common/lib/internal-non-workflow';
3
+ import { WorkerTuner } from './worker-tuner';
4
+
5
+ export {
6
+ WorkerTuner,
7
+ SlotSupplier,
8
+ ResourceBasedSlotOptions,
9
+ ResourceBasedTunerOptions,
10
+ FixedSizeSlotSupplier,
11
+ } from './worker-tuner';
3
12
 
4
13
  export type { TLSConfig, ProxyConfig, HttpConnectProxyConfig };
5
14
 
@@ -287,9 +296,11 @@ export interface WorkerOptions {
287
296
  */
288
297
  taskQueue: string;
289
298
 
290
- maxConcurrentActivityTaskExecutions: number;
291
- maxConcurrentWorkflowTaskExecutions: number;
292
- maxConcurrentLocalActivityExecutions: number;
299
+ /**
300
+ * The tuner the worker will use
301
+ */
302
+ tuner: WorkerTuner;
303
+
293
304
  nonStickyToStickyPollRatio: number;
294
305
 
295
306
  /**
@@ -410,12 +421,16 @@ export interface TimeSkippingServerConfig {
410
421
  port?: number;
411
422
  /**
412
423
  * Extra args to pass to the executable command.
424
+ *
425
+ * Note that the Test Server implementation may be changed to another one in the future. Therefore, there is
426
+ * no guarantee that server options, and particularly those provided through the `extraArgs` array, will continue to
427
+ * be supported in the future.
413
428
  */
414
429
  extraArgs?: string[];
415
430
  }
416
431
 
417
432
  /**
418
- * Configuration for the Temporal CLI dev server.
433
+ * Configuration for the Temporal CLI Dev Server.
419
434
  */
420
435
  export interface DevServerConfig {
421
436
  type: 'dev-server';
@@ -453,6 +468,10 @@ export interface DevServerConfig {
453
468
  port?: number;
454
469
  /**
455
470
  * Extra args to pass to the executable command.
471
+ *
472
+ * Note that the Dev Server implementation may be changed to another one in the future. Therefore, there is no
473
+ * guarantee that Dev Server options, and particularly those provided through the `extraArgs` array, will continue to
474
+ * be supported in the future.
456
475
  */
457
476
  extraArgs?: string[];
458
477
  }
@@ -460,7 +479,7 @@ export interface DevServerConfig {
460
479
  /**
461
480
  * Configuration for spawning an ephemeral Temporal server.
462
481
  *
463
- * Both the time-skipping test server and Temporal CLI dev server are supported.
482
+ * Both the time-skipping Test Server and Temporal CLI dev server are supported.
464
483
  */
465
484
  export type EphemeralServerConfig = TimeSkippingServerConfig | DevServerConfig;
466
485
 
@@ -0,0 +1,71 @@
1
+ /**
2
+ * A worker tuner allows the customization of the performance characteristics of workers by
3
+ * controlling how "slots" are handed out for different task types. In order to poll for and then
4
+ * run tasks, a slot must first be reserved by the {@link SlotSupplier} returned by the tuner.
5
+ *
6
+ * @experimental
7
+ */
8
+ export interface WorkerTuner {
9
+ workflowTaskSlotSupplier: SlotSupplier;
10
+ activityTaskSlotSupplier: SlotSupplier;
11
+ localActivityTaskSlotSupplier: SlotSupplier;
12
+ }
13
+
14
+ /**
15
+ * Controls how slots are handed out for a specific task type.
16
+ *
17
+ * For now, only {@link ResourceBasedSlotOptions} and {@link FixedSizeSlotSupplier} are supported,
18
+ * but we may add support for custom tuners in the future.
19
+ *
20
+ * @experimental
21
+ */
22
+ export type SlotSupplier = ResourceBasedSlotsForType | FixedSizeSlotSupplier;
23
+
24
+ /**
25
+ * Options for a specific slot type within a {@link ResourceBasedSlotsForType}
26
+ *
27
+ * @experimental
28
+ */
29
+ export interface ResourceBasedSlotOptions {
30
+ // Amount of slots that will be issued regardless of any other checks
31
+ minimumSlots: number;
32
+ // Maximum amount of slots permitted
33
+ maximumSlots: number;
34
+ // Minimum time we will wait (after passing the minimum slots number) between handing out new
35
+ // slots in milliseconds.
36
+ rampThrottleMs: number;
37
+ }
38
+
39
+ /**
40
+ * @experimental
41
+ */
42
+ type ResourceBasedSlotsForType = ResourceBasedSlotOptions & {
43
+ type: 'resource-based';
44
+ tunerOptions: ResourceBasedTunerOptions;
45
+ };
46
+
47
+ /**
48
+ * Options for a {@link ResourceBasedTuner} to control target resource usage
49
+ *
50
+ * @experimental
51
+ */
52
+ export interface ResourceBasedTunerOptions {
53
+ // A value between 0 and 1 that represents the target (system) memory usage. It's not recommended
54
+ // to set this higher than 0.8, since how much memory a workflow may use is not predictable, and
55
+ // you don't want to encounter OOM errors.
56
+ targetMemoryUsage: number;
57
+ // A value between 0 and 1 that represents the target (system) CPU usage. This can be set to 1.0
58
+ // if desired, but it's recommended to leave some headroom for other processes.
59
+ targetCpuUsage: number;
60
+ }
61
+
62
+ /**
63
+ * A fixed-size slot supplier that will never issue more than a fixed number of slots.
64
+ *
65
+ * @experimental
66
+ */
67
+ export interface FixedSizeSlotSupplier {
68
+ type: 'fixed-size';
69
+ // The maximum number of slots that can be issued
70
+ numSlots: number;
71
+ }