@temporalio/core-bridge 1.9.2 → 1.10.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 (177) hide show
  1. package/Cargo.lock +754 -473
  2. package/Cargo.toml +3 -3
  3. package/lib/index.d.ts +33 -2
  4. package/lib/index.js.map +1 -1
  5. package/package.json +4 -4
  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/scripts/build.js +4 -3
  12. package/sdk-core/.cargo/config.toml +2 -4
  13. package/sdk-core/.github/workflows/heavy.yml +1 -1
  14. package/sdk-core/.github/workflows/per-pr.yml +6 -4
  15. package/sdk-core/Cargo.toml +10 -3
  16. package/sdk-core/README.md +4 -6
  17. package/sdk-core/client/Cargo.toml +13 -5
  18. package/sdk-core/client/src/lib.rs +123 -34
  19. package/sdk-core/client/src/metrics.rs +70 -18
  20. package/sdk-core/client/src/proxy.rs +85 -0
  21. package/sdk-core/client/src/raw.rs +67 -5
  22. package/sdk-core/client/src/worker_registry/mod.rs +5 -3
  23. package/sdk-core/client/src/workflow_handle/mod.rs +3 -1
  24. package/sdk-core/core/Cargo.toml +31 -37
  25. package/sdk-core/core/src/abstractions/take_cell.rs +3 -3
  26. package/sdk-core/core/src/abstractions.rs +176 -108
  27. package/sdk-core/core/src/core_tests/activity_tasks.rs +4 -13
  28. package/sdk-core/core/src/core_tests/determinism.rs +2 -1
  29. package/sdk-core/core/src/core_tests/local_activities.rs +3 -3
  30. package/sdk-core/core/src/core_tests/mod.rs +3 -3
  31. package/sdk-core/core/src/core_tests/queries.rs +42 -5
  32. package/sdk-core/core/src/core_tests/workers.rs +2 -3
  33. package/sdk-core/core/src/core_tests/workflow_tasks.rs +115 -15
  34. package/sdk-core/core/src/ephemeral_server/mod.rs +109 -136
  35. package/sdk-core/core/src/internal_flags.rs +8 -8
  36. package/sdk-core/core/src/lib.rs +16 -11
  37. package/sdk-core/core/src/pollers/mod.rs +11 -5
  38. package/sdk-core/core/src/pollers/poll_buffer.rs +48 -29
  39. package/sdk-core/core/src/protosext/mod.rs +32 -32
  40. package/sdk-core/core/src/protosext/protocol_messages.rs +14 -24
  41. package/sdk-core/core/src/retry_logic.rs +2 -2
  42. package/sdk-core/core/src/telemetry/log_export.rs +10 -9
  43. package/sdk-core/core/src/telemetry/metrics.rs +233 -330
  44. package/sdk-core/core/src/telemetry/mod.rs +11 -38
  45. package/sdk-core/core/src/telemetry/otel.rs +355 -0
  46. package/sdk-core/core/src/telemetry/prometheus_server.rs +36 -23
  47. package/sdk-core/core/src/test_help/mod.rs +80 -59
  48. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +6 -6
  49. package/sdk-core/core/src/worker/activities/local_activities.rs +46 -43
  50. package/sdk-core/core/src/worker/activities.rs +45 -46
  51. package/sdk-core/core/src/worker/client/mocks.rs +8 -7
  52. package/sdk-core/core/src/worker/client.rs +40 -39
  53. package/sdk-core/core/src/worker/mod.rs +72 -42
  54. package/sdk-core/core/src/worker/slot_provider.rs +28 -28
  55. package/sdk-core/core/src/worker/slot_supplier.rs +1 -0
  56. package/sdk-core/core/src/worker/tuner/fixed_size.rs +52 -0
  57. package/sdk-core/core/src/worker/tuner/resource_based.rs +561 -0
  58. package/sdk-core/core/src/worker/tuner.rs +122 -0
  59. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +6 -6
  60. package/sdk-core/core/src/worker/workflow/history_update.rs +27 -53
  61. package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +4 -17
  62. package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +1 -10
  63. package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +4 -11
  64. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +17 -35
  65. package/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +0 -8
  66. package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +1 -5
  67. package/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +0 -5
  68. package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +0 -5
  69. package/sdk-core/core/src/worker/workflow/machines/mod.rs +0 -14
  70. package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +0 -5
  71. package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +0 -5
  72. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +1 -10
  73. package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +3 -10
  74. package/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +12 -8
  75. package/sdk-core/core/src/worker/workflow/machines/update_state_machine.rs +0 -10
  76. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +6 -13
  77. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +27 -37
  78. package/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +3 -14
  79. package/sdk-core/core/src/worker/workflow/managed_run.rs +84 -54
  80. package/sdk-core/core/src/worker/workflow/mod.rs +63 -160
  81. package/sdk-core/core/src/worker/workflow/run_cache.rs +22 -13
  82. package/sdk-core/core/src/worker/workflow/wft_extraction.rs +16 -3
  83. package/sdk-core/core/src/worker/workflow/wft_poller.rs +15 -12
  84. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +39 -78
  85. package/sdk-core/core-api/Cargo.toml +6 -5
  86. package/sdk-core/core-api/src/errors.rs +8 -0
  87. package/sdk-core/core-api/src/telemetry/metrics.rs +75 -4
  88. package/sdk-core/core-api/src/telemetry.rs +7 -1
  89. package/sdk-core/core-api/src/worker.rs +212 -56
  90. package/sdk-core/fsm/Cargo.toml +3 -0
  91. package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +1 -1
  92. package/sdk-core/sdk/Cargo.toml +5 -7
  93. package/sdk-core/sdk/src/app_data.rs +3 -3
  94. package/sdk-core/sdk/src/lib.rs +5 -3
  95. package/sdk-core/sdk/src/workflow_context/options.rs +1 -1
  96. package/sdk-core/sdk/src/workflow_context.rs +10 -9
  97. package/sdk-core/sdk/src/workflow_future.rs +1 -1
  98. package/sdk-core/sdk-core-protos/Cargo.toml +8 -6
  99. package/sdk-core/sdk-core-protos/build.rs +1 -10
  100. package/sdk-core/sdk-core-protos/protos/api_upstream/.github/PULL_REQUEST_TEMPLATE.md +3 -0
  101. package/sdk-core/sdk-core-protos/protos/api_upstream/.github/workflows/ci.yml +26 -0
  102. package/sdk-core/sdk-core-protos/protos/api_upstream/Makefile +42 -20
  103. package/sdk-core/sdk-core-protos/protos/api_upstream/README.md +2 -0
  104. package/sdk-core/sdk-core-protos/protos/api_upstream/api-linter.yaml +36 -26
  105. package/sdk-core/sdk-core-protos/protos/api_upstream/buf.lock +2 -0
  106. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/struct.proto +95 -0
  107. package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv2.json +9632 -0
  108. package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv3.yaml +7337 -0
  109. package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/payload_description.txt +2 -0
  110. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/command/v1/message.proto +45 -11
  111. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/common/v1/message.proto +22 -4
  112. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/command_type.proto +2 -0
  113. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/common.proto +44 -0
  114. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/event_type.proto +18 -3
  115. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +20 -0
  116. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +30 -0
  117. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/update.proto +7 -8
  118. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/workflow.proto +23 -5
  119. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/errordetails/v1/message.proto +20 -0
  120. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/failure/v1/message.proto +25 -0
  121. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/history/v1/message.proto +141 -15
  122. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/namespace/v1/message.proto +12 -0
  123. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/nexus/v1/message.proto +193 -0
  124. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +73 -6
  125. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +46 -4
  126. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/schedule/v1/message.proto +4 -0
  127. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/workflow_metadata.proto +2 -2
  128. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +116 -0
  129. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflow/v1/message.proto +134 -0
  130. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +274 -29
  131. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +57 -1
  132. package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +10 -12
  133. package/sdk-core/sdk-core-protos/src/history_builder.rs +1 -1
  134. package/sdk-core/sdk-core-protos/src/lib.rs +54 -51
  135. package/sdk-core/sdk-core-protos/src/task_token.rs +11 -2
  136. package/sdk-core/test-utils/Cargo.toml +7 -4
  137. package/sdk-core/test-utils/src/histfetch.rs +1 -1
  138. package/sdk-core/test-utils/src/lib.rs +44 -62
  139. package/sdk-core/tests/fuzzy_workflow.rs +5 -2
  140. package/sdk-core/tests/heavy_tests.rs +114 -17
  141. package/sdk-core/tests/integ_tests/activity_functions.rs +1 -1
  142. package/sdk-core/tests/integ_tests/client_tests.rs +2 -2
  143. package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +38 -26
  144. package/sdk-core/tests/integ_tests/metrics_tests.rs +126 -17
  145. package/sdk-core/tests/integ_tests/polling_tests.rs +118 -2
  146. package/sdk-core/tests/integ_tests/update_tests.rs +3 -5
  147. package/sdk-core/tests/integ_tests/visibility_tests.rs +3 -3
  148. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +1 -1
  149. package/sdk-core/tests/integ_tests/workflow_tests/appdata_propagation.rs +1 -1
  150. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -1
  151. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -1
  152. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +3 -3
  153. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +5 -4
  154. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -2
  155. package/sdk-core/tests/integ_tests/workflow_tests/eager.rs +6 -10
  156. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +9 -7
  157. package/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +1 -1
  158. package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +14 -9
  159. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -1
  160. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +6 -13
  161. package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +9 -6
  162. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +5 -5
  163. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +1 -1
  164. package/sdk-core/tests/integ_tests/workflow_tests.rs +115 -11
  165. package/sdk-core/tests/main.rs +2 -2
  166. package/src/conversions.rs +57 -0
  167. package/src/lib.rs +1 -0
  168. package/src/runtime.rs +51 -35
  169. package/ts/index.ts +67 -3
  170. package/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +0 -117
  171. package/sdk-core/core/src/worker/workflow/workflow_stream/tonic_status_serde.rs +0 -24
  172. package/sdk-core/sdk/src/payload_converter.rs +0 -11
  173. package/sdk-core/sdk-core-protos/protos/api_upstream/.buildkite/Dockerfile +0 -2
  174. package/sdk-core/sdk-core-protos/protos/api_upstream/.buildkite/docker-compose.yml +0 -15
  175. package/sdk-core/sdk-core-protos/protos/api_upstream/.buildkite/pipeline.yml +0 -10
  176. package/sdk-core/test-utils/src/wf_input_saver.rs +0 -50
  177. package/sdk-core/tests/wf_input_replay.rs +0 -32
@@ -1,6 +1,6 @@
1
1
  //! This module contains very generic helpers that can be used codebase-wide
2
2
 
3
- pub mod take_cell;
3
+ pub(crate) mod take_cell;
4
4
 
5
5
  use crate::MetricsContext;
6
6
  use derive_more::DebugCustom;
@@ -11,102 +11,149 @@ use std::{
11
11
  Arc,
12
12
  },
13
13
  };
14
- use tokio::sync::{AcquireError, OwnedSemaphorePermit, Semaphore, TryAcquireError};
14
+ use temporal_sdk_core_api::worker::{
15
+ SlotKind, SlotReservationContext, SlotSupplier, SlotSupplierPermit,
16
+ };
17
+ use tokio::sync::watch;
15
18
  use tokio_util::sync::CancellationToken;
16
19
 
17
- /// Wraps a [Semaphore] with a function call that is fed the available permits any time a permit is
18
- /// acquired or restored through the provided methods
20
+ /// Wraps a [SlotSupplier] and turns successful slot reservations into permit structs, as well
21
+ /// as handling associated metrics tracking.
19
22
  #[derive(Clone)]
20
- pub(crate) struct MeteredSemaphore {
21
- sem: Arc<Semaphore>,
22
- /// The number of permit owners who have acquired a permit from the semaphore, but are not yet
23
- /// meaningfully using that permit. This is useful for giving a more semantically accurate count
24
- /// of used task slots, since we typically wait for a permit first before polling, but that slot
25
- /// isn't used in the sense the user expects until we actually also get the corresponding task.
23
+ pub(crate) struct MeteredPermitDealer<SK: SlotKind> {
24
+ supplier: Arc<dyn SlotSupplier<SlotKind = SK> + Send + Sync>,
25
+ /// The number of permit owners who have acquired a permit, but are not yet meaningfully using
26
+ /// that permit. This is useful for giving a more semantically accurate count of used task
27
+ /// slots, since we typically wait for a permit first before polling, but that slot isn't used
28
+ /// in the sense the user expects until we actually also get the corresponding task.
26
29
  unused_claimants: Arc<AtomicUsize>,
30
+ /// The number of permits that have been handed out
31
+ extant_permits: (watch::Sender<usize>, watch::Receiver<usize>),
32
+ /// The maximum number of extant permits which are allowed. Once the number of extant permits
33
+ /// is at this number, no more permits will be requested from the supplier until one is freed.
34
+ /// This avoids requesting slots when we are at the workflow cache size limit. If and when
35
+ /// we add user-defined cache sizing, that logic will need to live with the supplier and
36
+ /// there will need to be some associated refactoring.
37
+ max_permits: Option<usize>,
27
38
  metrics_ctx: MetricsContext,
28
- record_fn: fn(&MetricsContext, usize),
29
39
  }
30
40
 
31
- impl MeteredSemaphore {
32
- pub fn new(
33
- inital_permits: usize,
41
+ impl<SK> MeteredPermitDealer<SK>
42
+ where
43
+ SK: SlotKind + 'static,
44
+ {
45
+ pub(crate) fn new(
46
+ supplier: Arc<dyn SlotSupplier<SlotKind = SK> + Send + Sync>,
34
47
  metrics_ctx: MetricsContext,
35
- record_fn: fn(&MetricsContext, usize),
48
+ max_permits: Option<usize>,
36
49
  ) -> Self {
37
50
  Self {
38
- sem: Arc::new(Semaphore::new(inital_permits)),
51
+ supplier,
39
52
  unused_claimants: Arc::new(AtomicUsize::new(0)),
53
+ extant_permits: watch::channel(0),
40
54
  metrics_ctx,
41
- record_fn,
55
+ max_permits,
42
56
  }
43
57
  }
44
58
 
45
- pub fn available_permits(&self) -> usize {
46
- self.sem.available_permits()
59
+ pub(crate) fn available_permits(&self) -> Option<usize> {
60
+ self.supplier.available_slots()
47
61
  }
48
62
 
49
63
  #[cfg(test)]
50
- pub fn unused_permits(&self) -> usize {
51
- self.sem.available_permits() + self.unused_claimants.load(Ordering::Acquire)
64
+ pub(crate) fn unused_permits(&self) -> Option<usize> {
65
+ self.available_permits()
66
+ .map(|ap| ap + self.unused_claimants.load(Ordering::Acquire))
52
67
  }
53
68
 
54
- pub async fn acquire_owned(&self) -> Result<OwnedMeteredSemPermit, AcquireError> {
55
- let res = self.sem.clone().acquire_owned().await?;
69
+ pub(crate) async fn acquire_owned(&self) -> Result<OwnedMeteredSemPermit<SK>, ()> {
70
+ if let Some(max) = self.max_permits {
71
+ self.extant_permits
72
+ .1
73
+ .clone()
74
+ .wait_for(|&ep| ep < max)
75
+ .await
76
+ .expect("Extant permit channel is never closed");
77
+ }
78
+ let res = self.supplier.reserve_slot(self).await;
56
79
  Ok(self.build_owned(res))
57
80
  }
58
81
 
59
- pub fn try_acquire_owned(&self) -> Result<OwnedMeteredSemPermit, TryAcquireError> {
60
- let res = self.sem.clone().try_acquire_owned()?;
61
- Ok(self.build_owned(res))
82
+ pub(crate) fn try_acquire_owned(&self) -> Result<OwnedMeteredSemPermit<SK>, ()> {
83
+ if let Some(max) = self.max_permits {
84
+ if *self.extant_permits.1.borrow() >= max {
85
+ return Err(());
86
+ }
87
+ }
88
+ if let Some(res) = self.supplier.try_reserve_slot(self) {
89
+ Ok(self.build_owned(res))
90
+ } else {
91
+ Err(())
92
+ }
62
93
  }
63
94
 
64
- fn build_owned(&self, res: OwnedSemaphorePermit) -> OwnedMeteredSemPermit {
95
+ fn build_owned(&self, res: SlotSupplierPermit) -> OwnedMeteredSemPermit<SK> {
65
96
  self.unused_claimants.fetch_add(1, Ordering::Release);
66
- self.record();
97
+ self.extant_permits.0.send_modify(|ep| *ep += 1);
98
+ // Eww
99
+ let uc_c = self.unused_claimants.clone();
100
+ let ep_rx_c = self.extant_permits.1.clone();
101
+ let ep_tx_c = self.extant_permits.0.clone();
102
+ let supp = self.supplier.clone();
103
+ let supp_c = self.supplier.clone();
104
+ let supp_c_c = self.supplier.clone();
105
+ let mets = self.metrics_ctx.clone();
106
+ let metric_rec =
107
+ // When being called from the drop impl, the permit isn't actually dropped yet, so
108
+ // account for that with the `add_one` parameter.
109
+ move |add_one: bool| {
110
+ let extra = usize::from(add_one);
111
+ let unused = uc_c.load(Ordering::Acquire);
112
+ if let Some(avail) = supp.available_slots() {
113
+ mets.available_task_slots(avail + unused + extra);
114
+ }
115
+ mets.task_slots_used((ep_rx_c.borrow().saturating_sub(unused) + extra) as u64);
116
+ };
117
+ let mrc = metric_rec.clone();
118
+ mrc(false);
119
+
67
120
  OwnedMeteredSemPermit {
68
- inner: res,
121
+ _inner: res,
69
122
  unused_claimants: Some(self.unused_claimants.clone()),
70
- record_fn: self.record_owned(),
123
+ use_fn: Box::new(move |info| {
124
+ supp_c.mark_slot_used(info);
125
+ metric_rec(false)
126
+ }),
127
+ release_fn: Box::new(move || {
128
+ supp_c_c.release_slot();
129
+ ep_tx_c.send_modify(|ep| *ep -= 1);
130
+ mrc(true)
131
+ }),
71
132
  }
72
133
  }
134
+ }
73
135
 
74
- fn record(&self) {
75
- (self.record_fn)(
76
- &self.metrics_ctx,
77
- self.sem.available_permits() + self.unused_claimants.load(Ordering::Acquire),
78
- );
79
- }
80
-
81
- fn record_owned(&self) -> Box<dyn Fn(bool) + Send + Sync> {
82
- let rcf = self.record_fn;
83
- let mets = self.metrics_ctx.clone();
84
- let sem = self.sem.clone();
85
- let uc = self.unused_claimants.clone();
86
- // When being called from the drop impl, the semaphore permit isn't actually dropped yet,
87
- // so account for that.
88
- Box::new(move |add_one: bool| {
89
- let extra = usize::from(add_one);
90
- rcf(
91
- &mets,
92
- sem.available_permits() + uc.load(Ordering::Acquire) + extra,
93
- )
94
- })
136
+ impl<SK: SlotKind> SlotReservationContext for MeteredPermitDealer<SK> {
137
+ fn num_issued_slots(&self) -> usize {
138
+ *self.extant_permits.1.borrow()
95
139
  }
96
140
  }
97
141
 
98
- /// A version of [MeteredSemaphore] that can be closed and supports waiting for close to complete.
142
+ /// A version of [MeteredPermitDealer] that can be closed and supports waiting for close to complete.
99
143
  /// Once closed, no permits will be handed out.
100
144
  /// Close completes when all permits have been returned.
101
- pub(crate) struct ClosableMeteredSemaphore {
102
- inner: Arc<MeteredSemaphore>,
145
+ pub(crate) struct ClosableMeteredPermitDealer<SK: SlotKind> {
146
+ inner: Arc<MeteredPermitDealer<SK>>,
103
147
  outstanding_permits: AtomicUsize,
104
148
  close_requested: AtomicBool,
105
149
  close_complete_token: CancellationToken,
106
150
  }
107
151
 
108
- impl ClosableMeteredSemaphore {
109
- pub fn new_arc(sem: Arc<MeteredSemaphore>) -> Arc<Self> {
152
+ impl<SK> ClosableMeteredPermitDealer<SK>
153
+ where
154
+ SK: SlotKind,
155
+ {
156
+ pub(crate) fn new_arc(sem: Arc<MeteredPermitDealer<SK>>) -> Arc<Self> {
110
157
  Arc::new(Self {
111
158
  inner: sem,
112
159
  outstanding_permits: Default::default(),
@@ -116,14 +163,17 @@ impl ClosableMeteredSemaphore {
116
163
  }
117
164
  }
118
165
 
119
- impl ClosableMeteredSemaphore {
166
+ impl<SK> ClosableMeteredPermitDealer<SK>
167
+ where
168
+ SK: SlotKind + 'static,
169
+ {
120
170
  #[cfg(test)]
121
- pub fn unused_permits(&self) -> usize {
171
+ pub(crate) fn unused_permits(&self) -> Option<usize> {
122
172
  self.inner.unused_permits()
123
173
  }
124
174
 
125
175
  /// Request to close the semaphore and prevent new permits from being acquired.
126
- pub fn close(&self) {
176
+ pub(crate) fn close(&self) {
127
177
  self.close_requested.store(true, Ordering::Release);
128
178
  if self.outstanding_permits.load(Ordering::Acquire) == 0 {
129
179
  self.close_complete_token.cancel();
@@ -131,16 +181,16 @@ impl ClosableMeteredSemaphore {
131
181
  }
132
182
 
133
183
  /// Returns after close has been requested and all outstanding permits have been returned.
134
- pub async fn close_complete(&self) {
184
+ pub(crate) async fn close_complete(&self) {
135
185
  self.close_complete_token.cancelled().await;
136
186
  }
137
187
 
138
188
  /// Acquire a permit if one is available and close was not requested.
139
- pub fn try_acquire_owned(
189
+ pub(crate) fn try_acquire_owned(
140
190
  self: &Arc<Self>,
141
- ) -> Result<TrackedOwnedMeteredSemPermit, TryAcquireError> {
191
+ ) -> Result<TrackedOwnedMeteredSemPermit<SK>, ()> {
142
192
  if self.close_requested.load(Ordering::Acquire) {
143
- return Err(TryAcquireError::Closed);
193
+ return Err(());
144
194
  }
145
195
  self.outstanding_permits.fetch_add(1, Ordering::Release);
146
196
  let res = self.inner.try_acquire_owned();
@@ -168,72 +218,64 @@ impl ClosableMeteredSemaphore {
168
218
 
169
219
  /// Tracks an OwnedMeteredSemPermit and calls on_drop when dropped.
170
220
  #[derive(DebugCustom)]
221
+ #[clippy::has_significant_drop]
171
222
  #[debug(fmt = "Tracked({inner:?})")]
172
- pub(crate) struct TrackedOwnedMeteredSemPermit {
173
- inner: Option<OwnedMeteredSemPermit>,
223
+ pub(crate) struct TrackedOwnedMeteredSemPermit<SK: SlotKind> {
224
+ inner: Option<OwnedMeteredSemPermit<SK>>,
174
225
  on_drop: Box<dyn Fn() + Send + Sync>,
175
226
  }
176
- impl From<TrackedOwnedMeteredSemPermit> for OwnedMeteredSemPermit {
177
- fn from(mut value: TrackedOwnedMeteredSemPermit) -> Self {
227
+ impl<SK: SlotKind> From<TrackedOwnedMeteredSemPermit<SK>> for OwnedMeteredSemPermit<SK> {
228
+ fn from(mut value: TrackedOwnedMeteredSemPermit<SK>) -> Self {
178
229
  value
179
230
  .inner
180
231
  .take()
181
232
  .expect("Inner permit should be available")
182
233
  }
183
234
  }
184
- impl Drop for TrackedOwnedMeteredSemPermit {
235
+ impl<SK: SlotKind> Drop for TrackedOwnedMeteredSemPermit<SK> {
185
236
  fn drop(&mut self) {
186
237
  (self.on_drop)();
187
238
  }
188
239
  }
189
240
 
190
- /// Wraps an [OwnedSemaphorePermit] to update metrics when it's dropped
191
- pub(crate) struct OwnedMeteredSemPermit {
192
- inner: OwnedSemaphorePermit,
193
- /// See [MeteredSemaphore::unused_claimants]. If present when dropping, used to decrement the
241
+ /// Wraps an [SlotSupplierPermit] to update metrics & when it's dropped
242
+ #[clippy::has_significant_drop]
243
+ pub(crate) struct OwnedMeteredSemPermit<SK: SlotKind> {
244
+ _inner: SlotSupplierPermit,
245
+ /// See [MeteredPermitDealer::unused_claimants]. If present when dropping, used to decrement the
194
246
  /// count.
195
247
  unused_claimants: Option<Arc<AtomicUsize>>,
196
- record_fn: Box<dyn Fn(bool) + Send + Sync>,
248
+ #[allow(clippy::type_complexity)] // not really tho, bud
249
+ use_fn: Box<dyn Fn(SK::Info<'_>) + Send + Sync>,
250
+ release_fn: Box<dyn Fn() + Send + Sync>,
197
251
  }
198
- impl Drop for OwnedMeteredSemPermit {
252
+ impl<SK: SlotKind> Drop for OwnedMeteredSemPermit<SK> {
199
253
  fn drop(&mut self) {
200
254
  if let Some(uc) = self.unused_claimants.take() {
201
255
  uc.fetch_sub(1, Ordering::Release);
202
256
  }
203
- (self.record_fn)(true)
257
+ (self.release_fn)()
204
258
  }
205
259
  }
206
- impl Debug for OwnedMeteredSemPermit {
260
+ impl<SK: SlotKind> Debug for OwnedMeteredSemPermit<SK> {
207
261
  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
208
- self.inner.fmt(f)
262
+ f.write_str("OwnedMeteredSemPermit()")
209
263
  }
210
264
  }
211
- impl OwnedMeteredSemPermit {
265
+ impl<SK: SlotKind> OwnedMeteredSemPermit<SK> {
212
266
  /// Should be called once this permit is actually being "used" for the work it was meant to
213
267
  /// permit.
214
- pub(crate) fn into_used(mut self) -> UsedMeteredSemPermit {
268
+ pub(crate) fn into_used(mut self, info: SK::Info<'_>) -> UsedMeteredSemPermit<SK> {
215
269
  if let Some(uc) = self.unused_claimants.take() {
216
270
  uc.fetch_sub(1, Ordering::Release);
217
- (self.record_fn)(false)
218
271
  }
272
+ (self.use_fn)(info);
219
273
  UsedMeteredSemPermit(self)
220
274
  }
221
275
  }
222
276
 
223
277
  #[derive(Debug)]
224
- pub(crate) struct UsedMeteredSemPermit(OwnedMeteredSemPermit);
225
- impl UsedMeteredSemPermit {
226
- #[cfg(feature = "save_wf_inputs")]
227
- pub(crate) fn fake_deserialized() -> Self {
228
- let sem = Arc::new(Semaphore::new(1));
229
- let inner = sem.try_acquire_owned().unwrap();
230
- Self(OwnedMeteredSemPermit {
231
- inner,
232
- unused_claimants: None,
233
- record_fn: Box::new(|_| {}),
234
- })
235
- }
236
- }
278
+ pub(crate) struct UsedMeteredSemPermit<SK: SlotKind>(#[allow(dead_code)] OwnedMeteredSemPermit<SK>);
237
279
 
238
280
  macro_rules! dbg_panic {
239
281
  ($($arg:tt)*) => {
@@ -244,13 +286,26 @@ macro_rules! dbg_panic {
244
286
  pub(crate) use dbg_panic;
245
287
 
246
288
  #[cfg(test)]
247
- mod tests {
289
+ pub(crate) mod tests {
248
290
  use super::*;
291
+ use crate::{advance_fut, worker::tuner::FixedSizeSlotSupplier};
292
+ use futures_util::FutureExt;
293
+ use temporal_sdk_core_api::worker::WorkflowSlotKind;
249
294
 
250
- #[tokio::test]
251
- async fn closable_semaphore_permit_drop_returns_permit() {
252
- let inner = MeteredSemaphore::new(2, MetricsContext::no_op(), |_, _| {});
253
- let sem = ClosableMeteredSemaphore::new_arc(Arc::new(inner));
295
+ pub(crate) fn fixed_size_permit_dealer<SK: SlotKind + Send + Sync + 'static>(
296
+ size: usize,
297
+ ) -> MeteredPermitDealer<SK> {
298
+ MeteredPermitDealer::new(
299
+ Arc::new(FixedSizeSlotSupplier::new(size)),
300
+ MetricsContext::no_op(),
301
+ None,
302
+ )
303
+ }
304
+
305
+ #[test]
306
+ fn closable_semaphore_permit_drop_returns_permit() {
307
+ let inner = fixed_size_permit_dealer::<WorkflowSlotKind>(2);
308
+ let sem = ClosableMeteredPermitDealer::new_arc(Arc::new(inner));
254
309
  let perm = sem.try_acquire_owned().unwrap();
255
310
  let permits = sem.outstanding_permits.load(Ordering::Acquire);
256
311
  assert_eq!(permits, 1);
@@ -261,8 +316,8 @@ mod tests {
261
316
 
262
317
  #[tokio::test]
263
318
  async fn closable_semaphore_permit_drop_after_close_resolves_close_complete() {
264
- let inner = MeteredSemaphore::new(2, MetricsContext::no_op(), |_, _| {});
265
- let sem = ClosableMeteredSemaphore::new_arc(Arc::new(inner));
319
+ let inner = fixed_size_permit_dealer::<WorkflowSlotKind>(2);
320
+ let sem = ClosableMeteredPermitDealer::new_arc(Arc::new(inner));
266
321
  let perm = sem.try_acquire_owned().unwrap();
267
322
  sem.close();
268
323
  drop(perm);
@@ -271,18 +326,31 @@ mod tests {
271
326
 
272
327
  #[tokio::test]
273
328
  async fn closable_semaphore_close_complete_ready_if_unused() {
274
- let inner = MeteredSemaphore::new(2, MetricsContext::no_op(), |_, _| {});
275
- let sem = ClosableMeteredSemaphore::new_arc(Arc::new(inner));
329
+ let inner = fixed_size_permit_dealer::<WorkflowSlotKind>(2);
330
+ let sem = ClosableMeteredPermitDealer::new_arc(Arc::new(inner));
276
331
  sem.close();
277
332
  sem.close_complete().await;
278
333
  }
279
334
 
280
- #[tokio::test]
281
- async fn closable_semaphore_does_not_hand_out_permits_after_closed() {
282
- let inner = MeteredSemaphore::new(2, MetricsContext::no_op(), |_, _| {});
283
- let sem = ClosableMeteredSemaphore::new_arc(Arc::new(inner));
335
+ #[test]
336
+ fn closable_semaphore_does_not_hand_out_permits_after_closed() {
337
+ let inner = fixed_size_permit_dealer::<WorkflowSlotKind>(2);
338
+ let sem = ClosableMeteredPermitDealer::new_arc(Arc::new(inner));
284
339
  sem.close();
285
- let perm = sem.try_acquire_owned().unwrap_err();
286
- assert_matches!(perm, TryAcquireError::Closed);
340
+ sem.try_acquire_owned().unwrap_err();
341
+ }
342
+
343
+ #[tokio::test]
344
+ async fn respects_max_extant_permits() {
345
+ let mut sem = fixed_size_permit_dealer::<WorkflowSlotKind>(2);
346
+ sem.max_permits = Some(1);
347
+ let perm = sem.try_acquire_owned().unwrap();
348
+ sem.try_acquire_owned().unwrap_err();
349
+ let acquire_fut = sem.acquire_owned();
350
+ // Will be pending
351
+ advance_fut!(acquire_fut);
352
+ drop(perm);
353
+ // Now it'll proceed
354
+ acquire_fut.await.unwrap();
287
355
  }
288
356
  }
@@ -7,7 +7,7 @@ use crate::{
7
7
  QueueResponse, ResponseType, WorkerExt, WorkflowCachingPolicy, TEST_Q,
8
8
  },
9
9
  worker::client::mocks::{mock_manual_workflow_client, mock_workflow_client},
10
- ActivityHeartbeat, Worker, WorkerConfigBuilder,
10
+ ActivityHeartbeat, Worker,
11
11
  };
12
12
  use futures::FutureExt;
13
13
  use itertools::Itertools;
@@ -618,11 +618,8 @@ async fn max_tq_acts_set_passed_to_poll_properly() {
618
618
  })
619
619
  });
620
620
 
621
- let cfg = WorkerConfigBuilder::default()
622
- .namespace("enchi")
623
- .task_queue("cat")
621
+ let cfg = test_worker_cfg()
624
622
  .max_concurrent_at_polls(1_usize)
625
- .worker_build_id("test_bin_id")
626
623
  .max_task_queue_activities_per_second(rate)
627
624
  .build()
628
625
  .unwrap();
@@ -921,7 +918,7 @@ async fn activity_tasks_from_completion_reserve_slots() {
921
918
  let mut mock = build_mock_pollers(mh);
922
919
  mock.worker_cfg(|cfg| {
923
920
  cfg.max_cached_workflows = 2;
924
- cfg.max_outstanding_activities = 2;
921
+ cfg.max_outstanding_activities = Some(2);
925
922
  });
926
923
  mock.set_act_poller(mock_poller_from_resps(act_tasks));
927
924
  let core = Arc::new(mock_worker(mock));
@@ -1030,13 +1027,7 @@ async fn cant_complete_activity_with_unset_result_payload() {
1030
1027
  })
1031
1028
  });
1032
1029
 
1033
- let cfg = WorkerConfigBuilder::default()
1034
- .namespace("enchi")
1035
- .task_queue("cat")
1036
- .worker_build_id("enchi_loves_salmon")
1037
- .build()
1038
- .unwrap();
1039
- let worker = Worker::new_test(cfg, mock_client);
1030
+ let worker = Worker::new_test(test_worker_cfg().build().unwrap(), mock_client);
1040
1031
  let t = worker.poll_activity_task().await.unwrap();
1041
1032
  let res = worker
1042
1033
  .complete_activity_task(ActivityTaskCompletion {
@@ -21,7 +21,8 @@ use temporal_sdk_core_protos::{
21
21
  };
22
22
 
23
23
  static DID_FAIL: AtomicBool = AtomicBool::new(false);
24
- pub async fn timer_wf_fails_once(ctx: WfContext) -> WorkflowResult<()> {
24
+
25
+ pub(crate) async fn timer_wf_fails_once(ctx: WfContext) -> WorkflowResult<()> {
25
26
  ctx.timer(Duration::from_secs(1)).await;
26
27
  if DID_FAIL
27
28
  .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
@@ -112,7 +112,7 @@ async fn local_act_two_wfts_before_marker(#[case] replay: bool, #[case] cached:
112
112
  worker.run_until_done().await.unwrap();
113
113
  }
114
114
 
115
- pub async fn local_act_fanout_wf(ctx: WfContext) -> WorkflowResult<()> {
115
+ pub(crate) async fn local_act_fanout_wf(ctx: WfContext) -> WorkflowResult<()> {
116
116
  let las: Vec<_> = (1..=50)
117
117
  .map(|i| {
118
118
  ctx.local_activity(LocalActivityOptions {
@@ -187,7 +187,7 @@ async fn local_act_heartbeat(#[case] shutdown_middle: bool) {
187
187
  mh.enforce_correct_number_of_polls = false;
188
188
  let mut worker = mock_sdk_cfg(mh, |wc| {
189
189
  wc.max_cached_workflows = 1;
190
- wc.max_outstanding_workflow_tasks = 1;
190
+ wc.max_outstanding_workflow_tasks = Some(1);
191
191
  });
192
192
  let core = worker.core_worker.clone();
193
193
 
@@ -1084,7 +1084,7 @@ async fn local_act_records_nonfirst_attempts_ok() {
1084
1084
  }));
1085
1085
  let mut worker = mock_sdk_cfg(mh, |wc| {
1086
1086
  wc.max_cached_workflows = 1;
1087
- wc.max_outstanding_workflow_tasks = 1;
1087
+ wc.max_outstanding_workflow_tasks = Some(1);
1088
1088
  });
1089
1089
 
1090
1090
  worker.register_wf(
@@ -16,6 +16,7 @@ use crate::{
16
16
  Worker,
17
17
  };
18
18
  use futures::FutureExt;
19
+ use once_cell::sync::Lazy;
19
20
  use std::time::Duration;
20
21
  use temporal_sdk_core_api::Worker as WorkerTrait;
21
22
  use temporal_sdk_core_protos::coresdk::workflow_completion::WorkflowActivationCompletion;
@@ -45,9 +46,8 @@ async fn after_shutdown_server_is_not_polled() {
45
46
  }
46
47
 
47
48
  // Better than cloning a billion arcs...
48
- lazy_static::lazy_static! {
49
- static ref BARR: Barrier = Barrier::new(3);
50
- }
49
+ static BARR: Lazy<Barrier> = Lazy::new(|| Barrier::new(3));
50
+
51
51
  #[tokio::test]
52
52
  async fn shutdown_interrupts_both_polls() {
53
53
  let mut mock_client = mock_manual_workflow_client();
@@ -893,8 +893,9 @@ async fn build_id_set_properly_on_query_on_first_task() {
893
893
  core.drain_pollers_and_shutdown().await;
894
894
  }
895
895
 
896
+ #[rstest::rstest]
896
897
  #[tokio::test]
897
- async fn queries_arent_lost_in_buffer_void() {
898
+ async fn queries_arent_lost_in_buffer_void(#[values(false, true)] buffered_because_cache: bool) {
898
899
  let wfid = "fake_wf_id";
899
900
  let mut t = TestHistoryBuilder::default();
900
901
  t.add_by_type(EventType::WorkflowExecutionStarted);
@@ -902,7 +903,24 @@ async fn queries_arent_lost_in_buffer_void() {
902
903
  t.add_we_signaled("sig", vec![]);
903
904
  t.add_full_wf_task();
904
905
  t.add_workflow_execution_completed();
905
- let tasks = [
906
+
907
+ let mut tasks = if buffered_because_cache {
908
+ // A history for another wf which will occupy the only cache slot initially
909
+ let mut t1 = TestHistoryBuilder::default();
910
+ t1.add_by_type(EventType::WorkflowExecutionStarted);
911
+ t1.add_full_wf_task();
912
+ t1.add_workflow_execution_completed();
913
+
914
+ vec![hist_to_poll_resp(
915
+ &t1,
916
+ "cache_occupier".to_owned(),
917
+ 1.into(),
918
+ )]
919
+ } else {
920
+ vec![]
921
+ };
922
+
923
+ tasks.extend([
906
924
  hist_to_poll_resp(&t, wfid.to_owned(), 1.into()),
907
925
  {
908
926
  let mut pr = hist_to_poll_resp(&t, wfid.to_owned(), 1.into());
@@ -923,8 +941,7 @@ async fn queries_arent_lost_in_buffer_void() {
923
941
  pr
924
942
  },
925
943
  hist_to_poll_resp(&t, wfid.to_owned(), 2.into()),
926
- ]
927
- .map(|r| r.resp);
944
+ ]);
928
945
 
929
946
  let mut mock = mock_workflow_client();
930
947
  mock.expect_complete_workflow_task()
@@ -932,10 +949,30 @@ async fn queries_arent_lost_in_buffer_void() {
932
949
  mock.expect_respond_legacy_query()
933
950
  .times(2)
934
951
  .returning(|_, _| Ok(Default::default()));
935
- let mut mock = MocksHolder::from_wft_stream(mock, stream::iter(tasks));
952
+ let mut mock =
953
+ MocksHolder::from_wft_stream(mock, stream::iter(tasks.into_iter().map(|r| r.resp)));
936
954
  mock.worker_cfg(|wc| wc.max_cached_workflows = 1);
937
955
  let core = mock_worker(mock);
938
956
 
957
+ if buffered_because_cache {
958
+ // Poll for and complete the occupying workflow
959
+ let task = core.poll_workflow_activation().await.unwrap();
960
+ core.complete_execution(&task.run_id).await;
961
+ // Get the cache removal task
962
+ let task = core.poll_workflow_activation().await.unwrap();
963
+ assert_matches!(
964
+ task.jobs.as_slice(),
965
+ [WorkflowActivationJob {
966
+ variant: Some(workflow_activation_job::Variant::RemoveFromCache(_)),
967
+ }]
968
+ );
969
+ // Wait a beat to ensure the other task(s) have a chance to be buffered
970
+ tokio::time::sleep(Duration::from_millis(100)).await;
971
+ core.complete_workflow_activation(WorkflowActivationCompletion::empty(task.run_id))
972
+ .await
973
+ .unwrap();
974
+ }
975
+
939
976
  let task = core.poll_workflow_activation().await.unwrap();
940
977
  core.complete_workflow_activation(WorkflowActivationCompletion::empty(task.run_id))
941
978
  .await
@@ -175,7 +175,7 @@ async fn complete_with_task_not_found_during_shutdown() {
175
175
  // Initiate shutdown before completing the activation
176
176
  let shutdown_fut = async {
177
177
  core.shutdown().await;
178
- complete_order.borrow_mut().push(3);
178
+ complete_order.borrow_mut().push(2);
179
179
  };
180
180
  let poll_fut = async {
181
181
  // This will return shutdown once the completion goes through
@@ -183,7 +183,6 @@ async fn complete_with_task_not_found_during_shutdown() {
183
183
  core.poll_workflow_activation().await.unwrap_err(),
184
184
  PollWfError::ShutDown
185
185
  );
186
- complete_order.borrow_mut().push(2);
187
186
  };
188
187
  let complete_fut = async {
189
188
  core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
@@ -195,7 +194,7 @@ async fn complete_with_task_not_found_during_shutdown() {
195
194
  complete_order.borrow_mut().push(1);
196
195
  };
197
196
  tokio::join!(shutdown_fut, poll_fut, complete_fut);
198
- assert_eq!(&complete_order.into_inner(), &[1, 2, 3])
197
+ assert_eq!(&complete_order.into_inner(), &[1, 2])
199
198
  }
200
199
 
201
200
  #[tokio::test]