@temporalio/core-bridge 0.22.0 → 1.0.0-rc.1

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 (139) hide show
  1. package/Cargo.lock +120 -15
  2. package/Cargo.toml +3 -1
  3. package/README.md +1 -1
  4. package/index.d.ts +137 -33
  5. package/package.json +6 -6
  6. package/releases/aarch64-apple-darwin/index.node +0 -0
  7. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  8. package/releases/x86_64-apple-darwin/index.node +0 -0
  9. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  10. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  11. package/sdk-core/.buildkite/docker/docker-compose.yaml +4 -2
  12. package/sdk-core/ARCHITECTURE.md +9 -7
  13. package/sdk-core/README.md +5 -1
  14. package/sdk-core/arch_docs/diagrams/workflow_internals.svg +1 -0
  15. package/sdk-core/bridge-ffi/src/lib.rs +1 -1
  16. package/sdk-core/bridge-ffi/src/wrappers.rs +60 -37
  17. package/sdk-core/client/Cargo.toml +1 -0
  18. package/sdk-core/client/src/lib.rs +50 -15
  19. package/sdk-core/client/src/raw.rs +167 -55
  20. package/sdk-core/client/src/retry.rs +9 -4
  21. package/sdk-core/client/src/workflow_handle/mod.rs +4 -2
  22. package/sdk-core/core/Cargo.toml +2 -0
  23. package/sdk-core/core/benches/workflow_replay.rs +1 -7
  24. package/sdk-core/core/src/abstractions.rs +137 -16
  25. package/sdk-core/core/src/core_tests/activity_tasks.rs +258 -63
  26. package/sdk-core/core/src/core_tests/child_workflows.rs +1 -2
  27. package/sdk-core/core/src/core_tests/determinism.rs +2 -2
  28. package/sdk-core/core/src/core_tests/local_activities.rs +8 -7
  29. package/sdk-core/core/src/core_tests/queries.rs +146 -60
  30. package/sdk-core/core/src/core_tests/replay_flag.rs +1 -1
  31. package/sdk-core/core/src/core_tests/workers.rs +39 -23
  32. package/sdk-core/core/src/core_tests/workflow_cancels.rs +1 -1
  33. package/sdk-core/core/src/core_tests/workflow_tasks.rs +387 -280
  34. package/sdk-core/core/src/lib.rs +8 -5
  35. package/sdk-core/core/src/pollers/poll_buffer.rs +16 -10
  36. package/sdk-core/core/src/protosext/mod.rs +7 -9
  37. package/sdk-core/core/src/retry_logic.rs +73 -16
  38. package/sdk-core/core/src/telemetry/metrics.rs +21 -7
  39. package/sdk-core/core/src/telemetry/mod.rs +182 -110
  40. package/sdk-core/core/src/test_help/mod.rs +341 -109
  41. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +18 -9
  42. package/sdk-core/core/src/worker/activities/local_activities.rs +22 -25
  43. package/sdk-core/core/src/worker/activities.rs +156 -29
  44. package/sdk-core/core/src/worker/client.rs +1 -0
  45. package/sdk-core/core/src/worker/mod.rs +132 -659
  46. package/sdk-core/core/src/{workflow → worker/workflow}/bridge.rs +1 -1
  47. package/sdk-core/core/src/{workflow → worker/workflow}/driven_workflow.rs +1 -1
  48. package/sdk-core/core/src/{workflow → worker/workflow}/history_update.rs +16 -2
  49. package/sdk-core/core/src/{workflow → worker/workflow}/machines/activity_state_machine.rs +39 -4
  50. package/sdk-core/core/src/{workflow → worker/workflow}/machines/cancel_external_state_machine.rs +5 -2
  51. package/sdk-core/core/src/{workflow → worker/workflow}/machines/cancel_workflow_state_machine.rs +1 -1
  52. package/sdk-core/core/src/{workflow → worker/workflow}/machines/child_workflow_state_machine.rs +2 -4
  53. package/sdk-core/core/src/{workflow → worker/workflow}/machines/complete_workflow_state_machine.rs +0 -0
  54. package/sdk-core/core/src/{workflow → worker/workflow}/machines/continue_as_new_workflow_state_machine.rs +1 -1
  55. package/sdk-core/core/src/{workflow → worker/workflow}/machines/fail_workflow_state_machine.rs +0 -0
  56. package/sdk-core/core/src/{workflow → worker/workflow}/machines/local_activity_state_machine.rs +2 -5
  57. package/sdk-core/core/src/{workflow → worker/workflow}/machines/mod.rs +1 -1
  58. package/sdk-core/core/src/{workflow → worker/workflow}/machines/mutable_side_effect_state_machine.rs +0 -0
  59. package/sdk-core/core/src/{workflow → worker/workflow}/machines/patch_state_machine.rs +1 -1
  60. package/sdk-core/core/src/{workflow → worker/workflow}/machines/side_effect_state_machine.rs +0 -0
  61. package/sdk-core/core/src/{workflow → worker/workflow}/machines/signal_external_state_machine.rs +4 -2
  62. package/sdk-core/core/src/{workflow → worker/workflow}/machines/timer_state_machine.rs +1 -2
  63. package/sdk-core/core/src/{workflow → worker/workflow}/machines/transition_coverage.rs +1 -1
  64. package/sdk-core/core/src/{workflow → worker/workflow}/machines/upsert_search_attributes_state_machine.rs +5 -7
  65. package/sdk-core/core/src/{workflow → worker/workflow}/machines/workflow_machines/local_acts.rs +2 -2
  66. package/sdk-core/core/src/{workflow → worker/workflow}/machines/workflow_machines.rs +40 -16
  67. package/sdk-core/core/src/{workflow → worker/workflow}/machines/workflow_task_state_machine.rs +0 -0
  68. package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +198 -0
  69. package/sdk-core/core/src/worker/workflow/managed_run.rs +627 -0
  70. package/sdk-core/core/src/worker/workflow/mod.rs +1115 -0
  71. package/sdk-core/core/src/worker/workflow/run_cache.rs +143 -0
  72. package/sdk-core/core/src/worker/workflow/wft_poller.rs +88 -0
  73. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +936 -0
  74. package/sdk-core/core-api/src/errors.rs +3 -10
  75. package/sdk-core/core-api/src/lib.rs +2 -1
  76. package/sdk-core/core-api/src/worker.rs +26 -2
  77. package/sdk-core/etc/dynamic-config.yaml +2 -0
  78. package/sdk-core/integ-with-otel.sh +1 -1
  79. package/sdk-core/protos/api_upstream/Makefile +4 -4
  80. package/sdk-core/protos/api_upstream/api-linter.yaml +2 -0
  81. package/sdk-core/protos/api_upstream/buf.yaml +8 -9
  82. package/sdk-core/protos/api_upstream/temporal/api/cluster/v1/message.proto +83 -0
  83. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +7 -1
  84. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/cluster.proto +40 -0
  85. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +3 -0
  86. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +3 -1
  87. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/schedule.proto +60 -0
  88. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +3 -0
  89. package/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +32 -4
  90. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +69 -19
  91. package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +13 -0
  92. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +163 -0
  93. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +97 -0
  94. package/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +300 -0
  95. package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +25 -0
  96. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +180 -3
  97. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +53 -3
  98. package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +2 -2
  99. package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +6 -5
  100. package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +27 -6
  101. package/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +2 -1
  102. package/sdk-core/protos/local/temporal/sdk/core/common/common.proto +0 -64
  103. package/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +2 -1
  104. package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +11 -8
  105. package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +30 -25
  106. package/sdk-core/sdk/src/activity_context.rs +12 -5
  107. package/sdk-core/sdk/src/app_data.rs +37 -0
  108. package/sdk-core/sdk/src/lib.rs +76 -43
  109. package/sdk-core/sdk/src/workflow_context/options.rs +8 -6
  110. package/sdk-core/sdk/src/workflow_context.rs +14 -19
  111. package/sdk-core/sdk/src/workflow_future.rs +11 -6
  112. package/sdk-core/sdk-core-protos/src/history_builder.rs +19 -5
  113. package/sdk-core/sdk-core-protos/src/history_info.rs +11 -6
  114. package/sdk-core/sdk-core-protos/src/lib.rs +87 -176
  115. package/sdk-core/test-utils/src/histfetch.rs +1 -1
  116. package/sdk-core/test-utils/src/lib.rs +93 -77
  117. package/sdk-core/tests/integ_tests/client_tests.rs +2 -2
  118. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +11 -9
  119. package/sdk-core/tests/integ_tests/polling_tests.rs +12 -0
  120. package/sdk-core/tests/integ_tests/queries_tests.rs +39 -22
  121. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +49 -4
  122. package/sdk-core/tests/integ_tests/workflow_tests/appdata_propagation.rs +61 -0
  123. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -1
  124. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +74 -13
  125. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +19 -0
  126. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -1
  127. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +6 -3
  128. package/sdk-core/tests/integ_tests/workflow_tests.rs +10 -23
  129. package/sdk-core/tests/load_tests.rs +8 -3
  130. package/sdk-core/tests/main.rs +7 -3
  131. package/src/conversions.rs +149 -70
  132. package/src/errors.rs +10 -21
  133. package/src/lib.rs +400 -319
  134. package/sdk-core/core/src/pending_activations.rs +0 -173
  135. package/sdk-core/core/src/worker/wft_delivery.rs +0 -81
  136. package/sdk-core/core/src/workflow/mod.rs +0 -478
  137. package/sdk-core/core/src/workflow/workflow_tasks/cache_manager.rs +0 -194
  138. package/sdk-core/core/src/workflow/workflow_tasks/concurrency_manager.rs +0 -418
  139. package/sdk-core/core/src/workflow/workflow_tasks/mod.rs +0 -989
@@ -6,9 +6,12 @@ use temporal_sdk::{
6
6
  interceptors::WorkerInterceptor, ActContext, ActivityCancelledError, CancellableFuture,
7
7
  LocalActivityOptions, WfContext, WorkflowResult,
8
8
  };
9
- use temporal_sdk_core_protos::coresdk::{
10
- common::RetryPolicy, workflow_commands::ActivityCancellationType,
11
- workflow_completion::WorkflowActivationCompletion, AsJsonPayloadExt,
9
+ use temporal_sdk_core_protos::{
10
+ coresdk::{
11
+ workflow_commands::ActivityCancellationType,
12
+ workflow_completion::WorkflowActivationCompletion, AsJsonPayloadExt,
13
+ },
14
+ temporal::api::common::v1::RetryPolicy,
12
15
  };
13
16
  use temporal_sdk_core_test_utils::CoreWfStarter;
14
17
  use tokio_util::sync::CancellationToken;
@@ -494,12 +497,15 @@ async fn schedule_to_close_timeout_across_timer_backoff(#[case] cached: bool) {
494
497
  worker.run_until_done().await.unwrap();
495
498
  }
496
499
 
500
+ #[rstest::rstest]
497
501
  #[tokio::test]
498
- async fn eviction_wont_make_local_act_get_dropped() {
499
- let wf_name = "eviction_wont_make_local_act_get_dropped";
500
- let mut starter = CoreWfStarter::new(wf_name);
502
+ async fn eviction_wont_make_local_act_get_dropped(#[values(true, false)] short_wft_timeout: bool) {
503
+ let wf_name = format!(
504
+ "eviction_wont_make_local_act_get_dropped_{}",
505
+ short_wft_timeout
506
+ );
507
+ let mut starter = CoreWfStarter::new(&wf_name);
501
508
  starter.max_cached_workflows(0);
502
- starter.wft_timeout(Duration::from_secs(1));
503
509
  let mut worker = starter.worker().await;
504
510
  worker.register_wf(wf_name.to_owned(), local_act_then_timer_then_wait);
505
511
  worker.register_activity("echo_activity", |_ctx: ActContext, str: String| async {
@@ -507,13 +513,16 @@ async fn eviction_wont_make_local_act_get_dropped() {
507
513
  Ok(str)
508
514
  });
509
515
 
516
+ let opts = if short_wft_timeout {
517
+ WorkflowOptions {
518
+ task_timeout: Some(Duration::from_secs(1)),
519
+ ..Default::default()
520
+ }
521
+ } else {
522
+ Default::default()
523
+ };
510
524
  worker
511
- .submit_wf(
512
- wf_name.to_owned(),
513
- wf_name.to_owned(),
514
- vec![],
515
- WorkflowOptions::default(),
516
- )
525
+ .submit_wf(wf_name.to_owned(), wf_name.to_owned(), vec![], opts)
517
526
  .await
518
527
  .unwrap();
519
528
  worker.run_until_done().await.unwrap();
@@ -571,3 +580,55 @@ async fn timer_backoff_concurrent_with_non_timer_backoff() {
571
580
  .unwrap();
572
581
  worker.run_until_done().await.unwrap();
573
582
  }
583
+
584
+ #[tokio::test]
585
+ async fn repro_nondeterminism_with_timer_bug() {
586
+ let wf_name = "repro_nondeterminism_with_timer_bug";
587
+ let mut starter = CoreWfStarter::new(wf_name);
588
+ let mut worker = starter.worker().await;
589
+
590
+ worker.register_wf(wf_name.to_owned(), |ctx: WfContext| async move {
591
+ let t1 = ctx.timer(Duration::from_secs(30));
592
+ let r1 = ctx.local_activity(LocalActivityOptions {
593
+ activity_type: "delay".to_string(),
594
+ input: "hi".as_json_payload().expect("serializes fine"),
595
+ retry_policy: RetryPolicy {
596
+ initial_interval: Some(Duration::from_micros(15).into()),
597
+ backoff_coefficient: 1_000.,
598
+ maximum_interval: Some(Duration::from_millis(1500).into()),
599
+ maximum_attempts: 4,
600
+ non_retryable_error_types: vec![],
601
+ },
602
+ timer_backoff_threshold: Some(Duration::from_secs(1)),
603
+ ..Default::default()
604
+ });
605
+ tokio::pin!(t1);
606
+ tokio::select! {
607
+ _ = &mut t1 => {},
608
+ _ = r1 => {
609
+ t1.cancel(&ctx);
610
+ },
611
+ };
612
+ ctx.timer(Duration::from_secs(1)).await;
613
+ Ok(().into())
614
+ });
615
+ worker.register_activity("delay", |_: ActContext, _: String| async {
616
+ tokio::time::sleep(Duration::from_secs(2)).await;
617
+ Ok(())
618
+ });
619
+
620
+ let run_id = worker
621
+ .submit_wf(
622
+ wf_name.to_owned(),
623
+ wf_name.to_owned(),
624
+ vec![],
625
+ WorkflowOptions::default(),
626
+ )
627
+ .await
628
+ .unwrap();
629
+ worker.run_until_done().await.unwrap();
630
+ starter
631
+ .fetch_history_and_replay(wf_name, run_id, worker.inner_mut())
632
+ .await
633
+ .unwrap();
634
+ }
@@ -112,6 +112,25 @@ async fn replay_using_wf_function() {
112
112
  worker.run().await.unwrap();
113
113
  }
114
114
 
115
+ #[tokio::test]
116
+ async fn replay_ok_ending_with_terminated_or_timed_out() {
117
+ let mut t1 = canned_histories::single_timer("1");
118
+ t1.add_workflow_execution_terminated();
119
+ let mut t2 = canned_histories::single_timer("1");
120
+ t2.add_workflow_execution_timed_out();
121
+ telemetry_init(&get_integ_telem_options()).unwrap();
122
+ for t in [t1, t2] {
123
+ let func = timers_wf(1);
124
+ let (worker, _) = init_core_replay_preloaded(
125
+ "replay_ok_terminate",
126
+ &t.get_full_history_info().unwrap().into(),
127
+ );
128
+ let mut worker = Worker::new_from_core(worker, "replay_ok_terminate".to_string());
129
+ worker.register_wf(DEFAULT_WORKFLOW_TYPE, func);
130
+ worker.run().await.unwrap();
131
+ }
132
+ }
133
+
115
134
  fn timers_wf(num_timers: u32) -> WorkflowFunction {
116
135
  WorkflowFunction::new(move |ctx: WfContext| async move {
117
136
  for _ in 1..=num_timers {
@@ -15,7 +15,7 @@ async fn reset_workflow() {
15
15
  let wf_name = "reset_me_wf";
16
16
  let mut starter = CoreWfStarter::new(wf_name);
17
17
  let mut worker = starter.worker().await;
18
- worker.auto_shutdown = false;
18
+ worker.fetch_results = false;
19
19
  let notify = Arc::new(Notify::new());
20
20
 
21
21
  let wf_notify = notify.clone();
@@ -1,7 +1,7 @@
1
1
  use std::collections::HashMap;
2
2
  use temporal_client::{WorkflowClientTrait, WorkflowOptions};
3
3
  use temporal_sdk::{WfContext, WorkflowResult};
4
- use temporal_sdk_core_protos::coresdk::AsJsonPayloadExt;
4
+ use temporal_sdk_core_protos::coresdk::{AsJsonPayloadExt, FromJsonPayloadExt};
5
5
  use temporal_sdk_core_test_utils::CoreWfStarter;
6
6
  use uuid::Uuid;
7
7
 
@@ -61,6 +61,9 @@ async fn sends_upsert() {
61
61
  payload.metadata.get("encoding").unwrap()
62
62
  );
63
63
  }
64
- assert_eq!("\"goodbye\"", txt_attr_payload.to_string());
65
- assert_eq!("98", int_attr_payload.to_string());
64
+ assert_eq!(
65
+ "goodbye",
66
+ String::from_json_payload(txt_attr_payload).unwrap()
67
+ );
68
+ assert_eq!(98, usize::from_json_payload(int_attr_payload).unwrap());
66
69
  }
@@ -1,4 +1,5 @@
1
1
  mod activities;
2
+ mod appdata_propagation;
2
3
  mod cancel_external;
3
4
  mod cancel_wf;
4
5
  mod child_workflows;
@@ -420,7 +421,8 @@ async fn wft_timeout_doesnt_create_unsolvable_autocomplete() {
420
421
  let mut wf_starter = CoreWfStarter::new("wft_timeout_doesnt_create_unsolvable_autocomplete");
421
422
  wf_starter
422
423
  // Test needs eviction on and a short timeout
423
- .max_cached_workflows(0usize)
424
+ .max_cached_workflows(0)
425
+ .max_wft(1)
424
426
  .wft_timeout(Duration::from_secs(1));
425
427
  let core = wf_starter.get_worker().await;
426
428
  let client = wf_starter.get_client().await;
@@ -445,26 +447,6 @@ async fn wft_timeout_doesnt_create_unsolvable_autocomplete() {
445
447
  .unwrap();
446
448
  wf_task
447
449
  };
448
- let poll_sched_act_poll = || async {
449
- poll_sched_act().await;
450
- let wf_task = core.poll_workflow_activation().await.unwrap();
451
- assert_matches!(
452
- wf_task.jobs.as_slice(),
453
- [
454
- WorkflowActivationJob {
455
- variant: Some(workflow_activation_job::Variant::SignalWorkflow(_)),
456
- },
457
- WorkflowActivationJob {
458
- variant: Some(workflow_activation_job::Variant::ResolveActivity(_)),
459
- },
460
- WorkflowActivationJob {
461
- variant: Some(workflow_activation_job::Variant::SignalWorkflow(_)),
462
- }
463
- ]
464
- );
465
- wf_task
466
- };
467
-
468
450
  wf_starter.start_wf().await;
469
451
 
470
452
  // Poll and schedule the activity
@@ -503,7 +485,8 @@ async fn wft_timeout_doesnt_create_unsolvable_autocomplete() {
503
485
  .await
504
486
  .unwrap();
505
487
  // Start from the beginning
506
- let wf_task = poll_sched_act_poll().await;
488
+ poll_sched_act().await;
489
+ let wf_task = core.poll_workflow_activation().await.unwrap();
507
490
  // Time out this time
508
491
  sleep(Duration::from_secs(2)).await;
509
492
  // Poll again, which should not have any work to do and spin, until the complete goes through.
@@ -526,7 +509,11 @@ async fn wft_timeout_doesnt_create_unsolvable_autocomplete() {
526
509
  .await
527
510
  .unwrap();
528
511
  // Do it all over again, without timing out this time
529
- let wf_task = poll_sched_act_poll().await;
512
+ poll_sched_act().await;
513
+ let wf_task = core.poll_workflow_activation().await.unwrap();
514
+ // Server can sometimes arbitrarily re-order the activity complete to be after the second signal
515
+ // Seeing 3 jobs is enough info.
516
+ assert_eq!(wf_task.jobs.len(), 3);
530
517
  core.complete_execution(&wf_task.run_id).await;
531
518
  }
532
519
 
@@ -113,6 +113,7 @@ async fn activity_load() {
113
113
  #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
114
114
  async fn workflow_load() {
115
115
  const SIGNAME: &str = "signame";
116
+ let num_workflows = 200;
116
117
  let wf_name = "workflow_load";
117
118
  let mut starter = CoreWfStarter::new("workflow_load");
118
119
  starter
@@ -151,7 +152,7 @@ async fn workflow_load() {
151
152
  let client = starter.get_client().await;
152
153
 
153
154
  let mut workflow_handles = vec![];
154
- for i in 0..200 {
155
+ for i in 0..num_workflows {
155
156
  let wfid = format!("{}_{}", wf_name, i);
156
157
  let rid = worker
157
158
  .submit_wf(
@@ -167,7 +168,7 @@ async fn workflow_load() {
167
168
 
168
169
  let sig_sender = async {
169
170
  loop {
170
- let sends: FuturesUnordered<_> = (0..200)
171
+ let sends: FuturesUnordered<_> = (0..num_workflows)
171
172
  .map(|i| {
172
173
  client.signal_workflow_execution(
173
174
  format!("{}_{}", wf_name, i),
@@ -177,7 +178,11 @@ async fn workflow_load() {
177
178
  )
178
179
  })
179
180
  .collect();
180
- sends.map(|_| Ok(())).forward(sink::drain()).await.unwrap();
181
+ sends
182
+ .map(|_| Ok(()))
183
+ .forward(sink::drain())
184
+ .await
185
+ .expect("Sending signals works");
181
186
  tokio::time::sleep(Duration::from_secs(2)).await;
182
187
  }
183
188
  };
@@ -28,7 +28,7 @@ mod integ_tests {
28
28
  let opts = get_integ_server_options();
29
29
  let telem_d = telemetry_init(&get_integ_telem_options()).unwrap();
30
30
  let mut retrying_client = opts
31
- .connect_no_namespace(telem_d.get_metric_meter())
31
+ .connect_no_namespace(telem_d.get_metric_meter(), None)
32
32
  .await
33
33
  .unwrap();
34
34
 
@@ -73,7 +73,8 @@ mod integ_tests {
73
73
  .unwrap();
74
74
  let sgo = ClientOptionsBuilder::default()
75
75
  .target_url(Url::from_str("https://localhost:7233").unwrap())
76
- .worker_binary_id("binident".to_string())
76
+ .client_name("tls_tester")
77
+ .client_version("clientver")
77
78
  .tls_cfg(TlsConfig {
78
79
  server_root_ca_cert: Some(root),
79
80
  domain: Some("tls-sample".to_string()),
@@ -84,7 +85,10 @@ mod integ_tests {
84
85
  })
85
86
  .build()
86
87
  .unwrap();
87
- let con = sgo.connect(NAMESPACE.to_string(), None).await.unwrap();
88
+ let con = sgo
89
+ .connect(NAMESPACE.to_string(), None, None)
90
+ .await
91
+ .unwrap();
88
92
  con.list_namespaces().await.unwrap();
89
93
  }
90
94
  }
@@ -1,48 +1,48 @@
1
1
  use log::LevelFilter;
2
+ use neon::types::buffer::TypedArray;
2
3
  use neon::{
3
4
  context::Context,
4
5
  handle::Handle,
5
6
  prelude::*,
6
- types::{JsNumber, JsString},
7
+ types::{JsBoolean, JsNumber, JsString},
7
8
  };
8
9
  use opentelemetry::trace::{SpanContext, SpanId, TraceFlags, TraceId, TraceState};
9
- use std::{fmt::Display, net::SocketAddr, str::FromStr, time::Duration};
10
+ use std::{collections::HashMap, fmt::Display, net::SocketAddr, str::FromStr, time::Duration};
10
11
  use temporal_sdk_core::{
11
12
  api::worker::{WorkerConfig, WorkerConfigBuilder},
12
- ClientOptions, ClientOptionsBuilder, ClientTlsConfig, RetryConfig, TelemetryOptions,
13
- TelemetryOptionsBuilder, TlsConfig, Url,
13
+ ClientOptions, ClientOptionsBuilder, ClientTlsConfig, Logger, MetricsExporter,
14
+ OtelCollectorOptions, RetryConfig, TelemetryOptions, TelemetryOptionsBuilder, TlsConfig,
15
+ TraceExporter, Url,
14
16
  };
15
17
 
16
- macro_rules! js_value_getter {
17
- ($js_cx:expr, $js_obj:ident, $prop_name:expr, $js_type:ty) => {
18
- $js_obj
19
- .get($js_cx, $prop_name)?
20
- .downcast::<$js_type, _>($js_cx)
21
- .map_err(|_| {
22
- $js_cx
23
- .throw_type_error::<_, Option<Vec<u8>>>(format!("Invalid {}", $prop_name))
24
- .unwrap_err()
25
- })?
26
- .value($js_cx)
27
- };
28
- }
29
-
18
+ #[macro_export]
30
19
  macro_rules! js_optional_getter {
31
20
  ($js_cx:expr, $js_obj:expr, $prop_name:expr, $js_type:ty) => {
32
21
  match get_optional($js_cx, $js_obj, $prop_name) {
33
- Some(val) => Some(val.downcast::<$js_type, _>($js_cx).map_err(|_| {
34
- $js_cx
35
- .throw_type_error::<_, Option<Vec<u8>>>(format!("Invalid {}", $prop_name))
36
- .unwrap_err()
37
- })?),
38
22
  None => None,
23
+ Some(val) => {
24
+ if val.is_a::<$js_type, _>($js_cx) {
25
+ Some(val.downcast_or_throw::<$js_type, _>($js_cx)?)
26
+ } else {
27
+ Some($js_cx.throw_type_error(format!("Invalid {}", $prop_name))?)
28
+ }
29
+ }
30
+ }
31
+ };
32
+ }
33
+
34
+ macro_rules! js_value_getter {
35
+ ($js_cx:expr, $js_obj:expr, $prop_name:expr, $js_type:ty) => {
36
+ match js_optional_getter!($js_cx, $js_obj, $prop_name, $js_type) {
37
+ Some(val) => val.value($js_cx),
38
+ None => $js_cx.throw_type_error(format!("{} must be defined", $prop_name))?,
39
39
  }
40
40
  };
41
41
  }
42
42
 
43
43
  /// Helper for extracting an optional attribute from [obj].
44
44
  /// If [obj].[attr] is undefined or not present, None is returned
45
- fn get_optional<'a, C, K>(
45
+ pub fn get_optional<'a, C, K>(
46
46
  cx: &mut C,
47
47
  obj: &Handle<JsObject>,
48
48
  attr: K,
@@ -51,7 +51,7 @@ where
51
51
  K: neon::object::PropertyKey,
52
52
  C: Context<'a>,
53
53
  {
54
- match obj.get(cx, attr) {
54
+ match obj.get_value(cx, attr) {
55
55
  Err(_) => None,
56
56
  Ok(val) => match val.is_a::<JsUndefined, _>(cx) {
57
57
  true => None,
@@ -75,7 +75,7 @@ where
75
75
  cx.throw_type_error::<_, Option<Vec<u8>>>(format!("Invalid {}", attr))
76
76
  .unwrap_err()
77
77
  })?;
78
- Ok(Some(cx.borrow(&buf, |data| data.as_slice::<u8>().to_vec())))
78
+ Ok(Some(buf.as_slice(cx).to_vec()))
79
79
  } else {
80
80
  Ok(None)
81
81
  }
@@ -97,7 +97,7 @@ where
97
97
  cx.throw_type_error::<_, Option<Vec<u8>>>(format!("Invalid {}", attr))
98
98
  .unwrap_err()
99
99
  })?;
100
- Ok(cx.borrow(&buf, |data| data.as_slice::<u8>().to_vec()))
100
+ Ok(buf.as_slice(cx).to_vec())
101
101
  } else {
102
102
  cx.throw_type_error::<_, Vec<u8>>(format!("Invalid or missing {}", full_attr_path))
103
103
  }
@@ -108,6 +108,10 @@ pub(crate) trait ObjectHandleConversionsExt {
108
108
  fn as_client_options(&self, ctx: &mut FunctionContext) -> NeonResult<ClientOptions>;
109
109
  fn as_telemetry_options(&self, cx: &mut FunctionContext) -> NeonResult<TelemetryOptions>;
110
110
  fn as_worker_config(&self, cx: &mut FunctionContext) -> NeonResult<WorkerConfig>;
111
+ fn as_hash_map_of_string_to_string(
112
+ &self,
113
+ cx: &mut FunctionContext,
114
+ ) -> NeonResult<HashMap<String, String>>;
111
115
  }
112
116
 
113
117
  impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
@@ -124,6 +128,22 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
124
128
  ))
125
129
  }
126
130
 
131
+ fn as_hash_map_of_string_to_string(
132
+ &self,
133
+ cx: &mut FunctionContext,
134
+ ) -> NeonResult<HashMap<String, String>> {
135
+ let props = self.get_own_property_names(cx)?;
136
+ let props = props.to_vec(cx)?;
137
+ let mut map = HashMap::new();
138
+ for k in props {
139
+ let k = k.to_string(cx)?;
140
+ let v = self.get::<JsString, _, _>(cx, k)?.value(cx);
141
+ let k = k.value(cx);
142
+ map.insert(k, v);
143
+ }
144
+ Ok(map)
145
+ }
146
+
127
147
  fn as_client_options(&self, cx: &mut FunctionContext) -> NeonResult<ClientOptions> {
128
148
  let url = match Url::parse(&js_value_getter!(cx, self, "url", JsString)) {
129
149
  Ok(url) => url,
@@ -167,7 +187,7 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
167
187
 
168
188
  let retry_config = match js_optional_getter!(cx, self, "retry", JsObject) {
169
189
  None => RetryConfig::default(),
170
- Some(retry_config) => RetryConfig {
190
+ Some(ref retry_config) => RetryConfig {
171
191
  initial_interval: Duration::from_millis(js_value_getter!(
172
192
  cx,
173
193
  retry_config,
@@ -198,68 +218,119 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
198
218
  },
199
219
  };
200
220
 
201
- let mut gateway_opts = ClientOptionsBuilder::default();
221
+ let mut client_options = ClientOptionsBuilder::default();
202
222
  if let Some(tls_cfg) = tls_cfg {
203
- gateway_opts.tls_cfg(tls_cfg);
223
+ client_options.tls_cfg(tls_cfg);
204
224
  }
205
- Ok(gateway_opts
225
+
226
+ Ok(client_options
206
227
  .client_name("temporal-typescript".to_string())
207
228
  .client_version(js_value_getter!(cx, self, "sdkVersion", JsString))
208
229
  .target_url(url)
209
- .identity(js_value_getter!(cx, self, "identity", JsString))
210
- .worker_binary_id(js_value_getter!(cx, self, "workerBinaryId", JsString))
211
230
  .retry_config(retry_config)
212
231
  .build()
213
232
  .expect("Core server gateway options must be valid"))
214
233
  }
215
234
 
216
235
  fn as_telemetry_options(&self, cx: &mut FunctionContext) -> NeonResult<TelemetryOptions> {
217
- let log_forwarding_level_str =
218
- js_value_getter!(cx, self, "logForwardingLevel", JsString).replace("WARNING", "WARN");
219
- let log_forwarding_level =
220
- LevelFilter::from_str(&log_forwarding_level_str).unwrap_or(LevelFilter::Off);
221
236
  let mut telemetry_opts = TelemetryOptionsBuilder::default();
222
- if let Some(url) = js_optional_getter!(cx, self, "oTelCollectorUrl", JsString) {
223
- let url = match Url::parse(&url.value(cx)) {
224
- Ok(url) => url,
225
- Err(_) => cx.throw_type_error("Invalid telemetryOptions.oTelCollectorUrl")?,
226
- };
227
- telemetry_opts.otel_collector_url(url);
237
+
238
+ if let Some(tf) = js_optional_getter!(cx, self, "tracingFilter", JsString) {
239
+ telemetry_opts.tracing_filter(tf.value(cx));
228
240
  }
229
- if let Some(addr) = js_optional_getter!(cx, self, "prometheusMetricsBindAddress", JsString)
230
- {
231
- match addr.value(cx).parse::<SocketAddr>() {
232
- Ok(address) => telemetry_opts.prometheus_export_bind_address(address),
233
- Err(_) => {
234
- cx.throw_type_error("Invalid telemetryOptions.prometheusMetricsBindAddress")?
241
+ telemetry_opts.no_temporal_prefix_for_metrics(
242
+ js_optional_getter!(cx, self, "noTemporalPrefixForMetrics", JsBoolean)
243
+ .map(|b| b.value(cx))
244
+ .unwrap_or_default(),
245
+ );
246
+ if let Some(ref logging) = js_optional_getter!(cx, self, "logging", JsObject) {
247
+ if let Some(_) = get_optional(cx, logging, "console") {
248
+ telemetry_opts.logging(Logger::Console);
249
+ } else if let Some(ref forward) = js_optional_getter!(cx, logging, "forward", JsObject)
250
+ {
251
+ let level = js_value_getter!(cx, forward, "level", JsString);
252
+ match LevelFilter::from_str(&level) {
253
+ Ok(level) => {
254
+ telemetry_opts.logging(Logger::Forward(level));
255
+ }
256
+ Err(err) => cx.throw_type_error(format!(
257
+ "Invalid telemetryOptions.logging.forward.level: {}",
258
+ err
259
+ ))?,
235
260
  }
236
- };
261
+ } else {
262
+ cx.throw_type_error(format!(
263
+ "Invalid telemetryOptions.logging, missing `console` or `forward` option"
264
+ ))?
265
+ }
237
266
  }
238
- if let Some(tf) = js_optional_getter!(cx, self, "tracingFilter", JsString) {
239
- telemetry_opts.tracing_filter(tf.value(cx));
267
+ if let Some(metrics) = js_optional_getter!(cx, self, "metrics", JsObject) {
268
+ if let Some(ref prom) = js_optional_getter!(cx, &metrics, "prometheus", JsObject) {
269
+ let addr = js_value_getter!(cx, prom, "bindAddress", JsString);
270
+ match addr.parse::<SocketAddr>() {
271
+ Ok(address) => telemetry_opts.metrics(MetricsExporter::Prometheus(address)),
272
+ Err(_) => cx.throw_type_error(
273
+ "Invalid telemetryOptions.metrics.prometheus.bindAddress",
274
+ )?,
275
+ };
276
+ } else if let Some(ref otel) = js_optional_getter!(cx, &metrics, "otel", JsObject) {
277
+ let url = js_value_getter!(cx, otel, "url", JsString);
278
+ let url = match Url::parse(&url) {
279
+ Ok(url) => url,
280
+ Err(_) => cx.throw_type_error("Invalid telemetryOptions.metrics.otel.url")?,
281
+ };
282
+ let headers =
283
+ if let Some(headers) = js_optional_getter!(cx, otel, "headers", JsObject) {
284
+ headers.as_hash_map_of_string_to_string(cx)?
285
+ } else {
286
+ Default::default()
287
+ };
288
+ telemetry_opts
289
+ .metrics(MetricsExporter::Otel(OtelCollectorOptions { url, headers }));
290
+ } else {
291
+ cx.throw_type_error(format!(
292
+ "Invalid telemetryOptions.metrics, missing `prometheus` or `otel` option"
293
+ ))?
294
+ }
240
295
  }
241
- telemetry_opts
242
- .log_forwarding_level(log_forwarding_level)
243
- .build()
244
- .map_err(|reason| {
245
- cx.throw_type_error::<_, TelemetryOptions>(format!("{}", reason))
246
- .unwrap_err()
247
- })
296
+
297
+ if let Some(tracing) = js_optional_getter!(cx, self, "tracing", JsObject) {
298
+ if let Some(ref otel) = js_optional_getter!(cx, &tracing, "otel", JsObject) {
299
+ let url = js_value_getter!(cx, otel, "url", JsString);
300
+ let url = match Url::parse(&url) {
301
+ Ok(url) => url,
302
+ Err(_) => cx.throw_type_error("Invalid telemetryOptions.tracing.otel.url")?,
303
+ };
304
+ let headers =
305
+ if let Some(headers) = js_optional_getter!(cx, otel, "headers", JsObject) {
306
+ headers.as_hash_map_of_string_to_string(cx)?
307
+ } else {
308
+ Default::default()
309
+ };
310
+ telemetry_opts.tracing(TraceExporter::Otel(OtelCollectorOptions { url, headers }));
311
+ } else {
312
+ cx.throw_type_error(format!(
313
+ "Invalid telemetryOptions.tracing, missing `otel` option"
314
+ ))?
315
+ }
316
+ }
317
+ telemetry_opts.build().map_err(|reason| {
318
+ cx.throw_type_error::<_, TelemetryOptions>(format!("{}", reason))
319
+ .unwrap_err()
320
+ })
248
321
  }
249
322
 
250
323
  fn as_worker_config(&self, cx: &mut FunctionContext) -> NeonResult<WorkerConfig> {
251
324
  let namespace = js_value_getter!(cx, self, "namespace", JsString);
252
325
  let task_queue = js_value_getter!(cx, self, "taskQueue", JsString);
326
+ let enable_remote_activities =
327
+ js_value_getter!(cx, self, "enableNonLocalActivities", JsBoolean);
253
328
  let max_outstanding_activities =
254
329
  js_value_getter!(cx, self, "maxConcurrentActivityTaskExecutions", JsNumber) as usize;
330
+ let max_outstanding_local_activities =
331
+ js_value_getter!(cx, self, "maxConcurrentLocalActivityExecutions", JsNumber) as usize;
255
332
  let max_outstanding_workflow_tasks =
256
333
  js_value_getter!(cx, self, "maxConcurrentWorkflowTaskExecutions", JsNumber) as usize;
257
- let max_concurrent_wft_polls =
258
- js_value_getter!(cx, self, "maxConcurrentWorkflowTaskPolls", JsNumber) as usize;
259
- let max_concurrent_at_polls =
260
- js_value_getter!(cx, self, "maxConcurrentActivityTaskPolls", JsNumber) as usize;
261
- let nonsticky_to_sticky_poll_ratio =
262
- js_value_getter!(cx, self, "nonStickyToStickyPollRatio", JsNumber) as f32;
263
334
  let sticky_queue_schedule_to_start_timeout = Duration::from_millis(js_value_getter!(
264
335
  cx,
265
336
  self,
@@ -283,20 +354,28 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
283
354
  JsNumber
284
355
  ) as u64);
285
356
 
357
+ let max_worker_activities_per_second =
358
+ js_optional_getter!(cx, self, "maxActivitiesPerSecond", JsNumber)
359
+ .map(|num| num.value(cx) as f64);
360
+ let max_task_queue_activities_per_second =
361
+ js_optional_getter!(cx, self, "maxTaskQueueActivitiesPerSecond", JsNumber)
362
+ .map(|num| num.value(cx) as f64);
363
+
286
364
  match WorkerConfigBuilder::default()
287
- .no_remote_activities(false) // TODO: make this configurable once Core implements local activities
288
- .max_concurrent_at_polls(max_concurrent_at_polls)
289
- .max_concurrent_wft_polls(max_concurrent_wft_polls)
365
+ .worker_build_id(js_value_getter!(cx, self, "buildId", JsString))
366
+ .client_identity_override(Some(js_value_getter!(cx, self, "identity", JsString)))
367
+ .no_remote_activities(!enable_remote_activities)
290
368
  .max_outstanding_workflow_tasks(max_outstanding_workflow_tasks)
291
369
  .max_outstanding_activities(max_outstanding_activities)
370
+ .max_outstanding_local_activities(max_outstanding_local_activities)
292
371
  .max_cached_workflows(max_cached_workflows)
293
- .nonsticky_to_sticky_poll_ratio(nonsticky_to_sticky_poll_ratio)
294
372
  .sticky_queue_schedule_to_start_timeout(sticky_queue_schedule_to_start_timeout)
295
373
  .namespace(namespace)
296
374
  .task_queue(task_queue)
297
375
  .max_heartbeat_throttle_interval(max_heartbeat_throttle_interval)
298
376
  .default_heartbeat_throttle_interval(default_heartbeat_throttle_interval)
299
- .max_outstanding_local_activities(10_usize) // TODO: Pass in
377
+ .max_worker_activities_per_second(max_worker_activities_per_second)
378
+ .max_task_queue_activities_per_second(max_task_queue_activities_per_second)
300
379
  .build()
301
380
  {
302
381
  Ok(worker_cfg) => Ok(worker_cfg),