@temporalio/core-bridge 1.11.3 → 1.11.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +396 -489
- package/Cargo.toml +3 -2
- package/lib/errors.js +2 -2
- package/lib/errors.js.map +1 -1
- package/lib/index.d.ts +8 -2
- package/lib/index.js.map +1 -1
- package/lib/worker-tuner.d.ts +111 -1
- package/package.json +3 -3
- package/releases/aarch64-apple-darwin/index.node +0 -0
- package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
- package/releases/x86_64-apple-darwin/index.node +0 -0
- package/releases/x86_64-pc-windows-msvc/index.node +0 -0
- package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
- package/sdk-core/.github/workflows/per-pr.yml +3 -3
- package/sdk-core/Cargo.toml +0 -1
- package/sdk-core/client/Cargo.toml +1 -2
- package/sdk-core/client/src/lib.rs +21 -13
- package/sdk-core/client/src/metrics.rs +1 -1
- package/sdk-core/client/src/raw.rs +46 -1
- package/sdk-core/core/Cargo.toml +7 -7
- package/sdk-core/core/benches/workflow_replay.rs +1 -1
- package/sdk-core/core/src/abstractions/take_cell.rs +1 -1
- package/sdk-core/core/src/abstractions.rs +98 -10
- package/sdk-core/core/src/core_tests/activity_tasks.rs +8 -2
- package/sdk-core/core/src/core_tests/local_activities.rs +1 -1
- package/sdk-core/core/src/core_tests/mod.rs +3 -3
- package/sdk-core/core/src/core_tests/updates.rs +104 -9
- package/sdk-core/core/src/core_tests/workers.rs +72 -3
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +6 -7
- package/sdk-core/core/src/debug_client.rs +78 -0
- package/sdk-core/core/src/ephemeral_server/mod.rs +15 -5
- package/sdk-core/core/src/lib.rs +30 -4
- package/sdk-core/core/src/pollers/mod.rs +1 -1
- package/sdk-core/core/src/pollers/poll_buffer.rs +7 -7
- package/sdk-core/core/src/replay/mod.rs +4 -4
- package/sdk-core/core/src/telemetry/log_export.rs +2 -2
- package/sdk-core/core/src/telemetry/metrics.rs +69 -1
- package/sdk-core/core/src/telemetry/otel.rs +2 -2
- package/sdk-core/core/src/test_help/mod.rs +3 -3
- package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +3 -3
- package/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +1 -1
- package/sdk-core/core/src/worker/activities/local_activities.rs +68 -24
- package/sdk-core/core/src/worker/activities.rs +26 -15
- package/sdk-core/core/src/worker/client/mocks.rs +10 -4
- package/sdk-core/core/src/worker/client.rs +17 -0
- package/sdk-core/core/src/worker/mod.rs +71 -13
- package/sdk-core/core/src/worker/slot_provider.rs +5 -7
- package/sdk-core/core/src/worker/tuner/fixed_size.rs +4 -3
- package/sdk-core/core/src/worker/tuner/resource_based.rs +171 -32
- package/sdk-core/core/src/worker/tuner.rs +18 -6
- package/sdk-core/core/src/worker/workflow/history_update.rs +43 -13
- package/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +6 -6
- package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +6 -5
- package/sdk-core/core/src/worker/workflow/managed_run.rs +3 -3
- package/sdk-core/core/src/worker/workflow/mod.rs +13 -7
- package/sdk-core/core/src/worker/workflow/wft_extraction.rs +7 -7
- package/sdk-core/core/src/worker/workflow/wft_poller.rs +2 -2
- package/sdk-core/core/src/worker/workflow/workflow_stream.rs +11 -11
- package/sdk-core/core-api/Cargo.toml +1 -0
- package/sdk-core/core-api/src/worker.rs +84 -30
- package/sdk-core/sdk/Cargo.toml +1 -2
- package/sdk-core/sdk/src/lib.rs +1 -1
- package/sdk-core/sdk/src/workflow_context.rs +9 -8
- package/sdk-core/sdk/src/workflow_future.rs +19 -14
- package/sdk-core/sdk-core-protos/Cargo.toml +2 -0
- package/sdk-core/sdk-core-protos/build.rs +6 -1
- package/sdk-core/sdk-core-protos/protos/api_upstream/buf.yaml +1 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv2.json +3207 -158
- package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv3.yaml +2934 -118
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/activity/v1/message.proto +67 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/common/v1/message.proto +47 -1
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/event_type.proto +6 -7
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +2 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/reset.proto +5 -3
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +3 -3
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/update.proto +14 -13
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/failure/v1/message.proto +1 -3
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/history/v1/message.proto +22 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/nexus/v1/message.proto +13 -2
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +26 -6
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/schedule/v1/message.proto +5 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/workflow_metadata.proto +6 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +46 -12
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/update/v1/message.proto +18 -19
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflow/v1/message.proto +27 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +192 -19
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +279 -12
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/activity_result/activity_result.proto +1 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/activity_task/activity_task.proto +1 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +1 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/common/common.proto +1 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/core_interface.proto +17 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/external_data/external_data.proto +1 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +1 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +1 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +1 -1
- package/sdk-core/sdk-core-protos/src/lib.rs +30 -6
- package/sdk-core/test-utils/Cargo.toml +1 -2
- package/sdk-core/test-utils/src/lib.rs +2 -2
- package/sdk-core/tests/heavy_tests.rs +1 -1
- package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +2 -2
- package/sdk-core/tests/integ_tests/metrics_tests.rs +144 -7
- package/sdk-core/tests/integ_tests/queries_tests.rs +1 -1
- package/sdk-core/tests/integ_tests/update_tests.rs +109 -5
- package/sdk-core/tests/integ_tests/worker_tests.rs +44 -8
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +1 -1
- package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -1
- package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +1 -1
- package/sdk-core/tests/integ_tests/workflow_tests.rs +3 -2
- package/src/conversions/slot_supplier_bridge.rs +287 -0
- package/src/conversions.rs +23 -15
- package/src/helpers.rs +35 -1
- package/src/runtime.rs +7 -3
- package/src/worker.rs +1 -1
- package/ts/index.ts +19 -4
- package/ts/worker-tuner.ts +123 -1
- package/sdk-core/sdk-core-protos/protos/api_upstream/.gitmodules +0 -3
|
@@ -3,7 +3,7 @@ use parking_lot::Mutex;
|
|
|
3
3
|
use std::{
|
|
4
4
|
marker::PhantomData,
|
|
5
5
|
sync::{
|
|
6
|
-
atomic::{AtomicU64, Ordering},
|
|
6
|
+
atomic::{AtomicU64, AtomicUsize, Ordering},
|
|
7
7
|
Arc, OnceLock,
|
|
8
8
|
},
|
|
9
9
|
time::{Duration, Instant},
|
|
@@ -11,7 +11,8 @@ use std::{
|
|
|
11
11
|
use temporal_sdk_core_api::{
|
|
12
12
|
telemetry::metrics::{CoreMeter, GaugeF64, MetricAttributes, TemporalMeter},
|
|
13
13
|
worker::{
|
|
14
|
-
ActivitySlotKind, LocalActivitySlotKind,
|
|
14
|
+
ActivitySlotKind, LocalActivitySlotKind, SlotInfo, SlotInfoTrait, SlotKind, SlotKindType,
|
|
15
|
+
SlotMarkUsedContext, SlotReleaseContext, SlotReservationContext, SlotSupplier,
|
|
15
16
|
SlotSupplierPermit, WorkerTuner, WorkflowSlotKind,
|
|
16
17
|
},
|
|
17
18
|
};
|
|
@@ -124,6 +125,12 @@ pub(crate) struct ResourceBasedSlotsForType<MI, SK> {
|
|
|
124
125
|
|
|
125
126
|
last_slot_issued_tx: watch::Sender<Instant>,
|
|
126
127
|
last_slot_issued_rx: watch::Receiver<Instant>,
|
|
128
|
+
|
|
129
|
+
// Only used for workflow slots - count of issued non-sticky slots
|
|
130
|
+
issued_nonsticky: AtomicUsize,
|
|
131
|
+
// Only used for workflow slots - count of issued sticky slots
|
|
132
|
+
issued_sticky: AtomicUsize,
|
|
133
|
+
|
|
127
134
|
_slot_kind: PhantomData<SK>,
|
|
128
135
|
}
|
|
129
136
|
/// Allows for the full customization of the PID options for a resource based tuner
|
|
@@ -241,42 +248,51 @@ where
|
|
|
241
248
|
|
|
242
249
|
async fn reserve_slot(&self, ctx: &dyn SlotReservationContext) -> SlotSupplierPermit {
|
|
243
250
|
loop {
|
|
244
|
-
if
|
|
245
|
-
return
|
|
251
|
+
if let Some(value) = self.issue_if_below_minimums(ctx) {
|
|
252
|
+
return value;
|
|
253
|
+
}
|
|
254
|
+
let must_wait_for = self
|
|
255
|
+
.opts
|
|
256
|
+
.ramp_throttle
|
|
257
|
+
.saturating_sub(self.time_since_last_issued());
|
|
258
|
+
if must_wait_for > Duration::from_millis(0) {
|
|
259
|
+
tokio::time::sleep(must_wait_for).await;
|
|
260
|
+
}
|
|
261
|
+
if let Some(p) = self.try_reserve_slot(ctx) {
|
|
262
|
+
return p;
|
|
246
263
|
} else {
|
|
247
|
-
|
|
248
|
-
.opts
|
|
249
|
-
.ramp_throttle
|
|
250
|
-
.saturating_sub(self.time_since_last_issued());
|
|
251
|
-
if must_wait_for > Duration::from_millis(0) {
|
|
252
|
-
tokio::time::sleep(must_wait_for).await;
|
|
253
|
-
}
|
|
254
|
-
if let Some(p) = self.try_reserve_slot(ctx) {
|
|
255
|
-
return p;
|
|
256
|
-
} else {
|
|
257
|
-
tokio::time::sleep(Duration::from_millis(10)).await;
|
|
258
|
-
}
|
|
264
|
+
tokio::time::sleep(Duration::from_millis(10)).await;
|
|
259
265
|
}
|
|
260
266
|
}
|
|
261
267
|
}
|
|
262
268
|
|
|
263
269
|
fn try_reserve_slot(&self, ctx: &dyn SlotReservationContext) -> Option<SlotSupplierPermit> {
|
|
264
|
-
let
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
+
if let v @ Some(_) = self.issue_if_below_minimums(ctx) {
|
|
271
|
+
return v;
|
|
272
|
+
}
|
|
273
|
+
if self.time_since_last_issued() > self.opts.ramp_throttle
|
|
274
|
+
&& ctx.num_issued_slots() < self.opts.max_slots
|
|
275
|
+
&& self.inner.pid_decision()
|
|
276
|
+
&& self.inner.can_reserve()
|
|
270
277
|
{
|
|
271
|
-
Some(self.issue_slot())
|
|
278
|
+
Some(self.issue_slot(ctx))
|
|
272
279
|
} else {
|
|
273
280
|
None
|
|
274
281
|
}
|
|
275
282
|
}
|
|
276
283
|
|
|
277
|
-
fn mark_slot_used(&self,
|
|
284
|
+
fn mark_slot_used(&self, _ctx: &dyn SlotMarkUsedContext<SlotKind = Self::SlotKind>) {}
|
|
278
285
|
|
|
279
|
-
fn release_slot(&self) {
|
|
286
|
+
fn release_slot(&self, ctx: &dyn SlotReleaseContext<SlotKind = Self::SlotKind>) {
|
|
287
|
+
// Really could use specialization here
|
|
288
|
+
if let Some(SlotInfo::Workflow(info)) = ctx.info().map(|i| i.downcast()) {
|
|
289
|
+
if info.is_sticky {
|
|
290
|
+
self.issued_sticky.fetch_sub(1, Ordering::Relaxed);
|
|
291
|
+
} else {
|
|
292
|
+
self.issued_nonsticky.fetch_sub(1, Ordering::Relaxed);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
280
296
|
}
|
|
281
297
|
|
|
282
298
|
impl<MI, SK> ResourceBasedSlotsForType<MI, SK>
|
|
@@ -291,11 +307,41 @@ where
|
|
|
291
307
|
last_slot_issued_tx: tx,
|
|
292
308
|
last_slot_issued_rx: rx,
|
|
293
309
|
inner,
|
|
310
|
+
issued_nonsticky: Default::default(),
|
|
311
|
+
issued_sticky: Default::default(),
|
|
294
312
|
_slot_kind: PhantomData,
|
|
295
313
|
}
|
|
296
314
|
}
|
|
297
315
|
|
|
298
|
-
|
|
316
|
+
// Always be willing to hand out at least 1 slot for sticky and 1 for non-sticky to
|
|
317
|
+
// avoid getting stuck.
|
|
318
|
+
fn issue_if_below_minimums(
|
|
319
|
+
&self,
|
|
320
|
+
ctx: &dyn SlotReservationContext,
|
|
321
|
+
) -> Option<SlotSupplierPermit> {
|
|
322
|
+
if ctx.num_issued_slots() < self.opts.min_slots {
|
|
323
|
+
return Some(self.issue_slot(ctx));
|
|
324
|
+
}
|
|
325
|
+
if SK::kind() == SlotKindType::Workflow
|
|
326
|
+
&& (ctx.is_sticky() && self.issued_sticky.load(Ordering::Relaxed) == 0
|
|
327
|
+
|| !ctx.is_sticky() && self.issued_nonsticky.load(Ordering::Relaxed) == 0)
|
|
328
|
+
{
|
|
329
|
+
return Some(self.issue_slot(ctx));
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
None
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
fn issue_slot(&self, ctx: &dyn SlotReservationContext) -> SlotSupplierPermit {
|
|
336
|
+
// Always be willing to hand out at least 1 slot for sticky and 1 for non-sticky to avoid
|
|
337
|
+
// getting stuck.
|
|
338
|
+
if SK::kind() == SlotKindType::Workflow {
|
|
339
|
+
if ctx.is_sticky() {
|
|
340
|
+
self.issued_sticky.fetch_add(1, Ordering::Relaxed);
|
|
341
|
+
} else {
|
|
342
|
+
self.issued_nonsticky.fetch_add(1, Ordering::Relaxed);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
299
345
|
let _ = self.last_slot_issued_tx.send(Instant::now());
|
|
300
346
|
SlotSupplierPermit::default()
|
|
301
347
|
}
|
|
@@ -406,7 +452,7 @@ impl<MI: SystemResourceInfo + Sync + Send> ResourceController<MI> {
|
|
|
406
452
|
#[derive(Debug)]
|
|
407
453
|
pub struct RealSysInfo {
|
|
408
454
|
sys: Mutex<sysinfo::System>,
|
|
409
|
-
total_mem:
|
|
455
|
+
total_mem: AtomicU64,
|
|
410
456
|
cur_mem_usage: AtomicU64,
|
|
411
457
|
cur_cpu_usage: AtomicU64,
|
|
412
458
|
last_refresh: AtomicCell<Instant>,
|
|
@@ -421,7 +467,7 @@ impl RealSysInfo {
|
|
|
421
467
|
last_refresh: AtomicCell::new(Instant::now()),
|
|
422
468
|
cur_mem_usage: AtomicU64::new(0),
|
|
423
469
|
cur_cpu_usage: AtomicU64::new(0),
|
|
424
|
-
total_mem,
|
|
470
|
+
total_mem: AtomicU64::new(total_mem),
|
|
425
471
|
};
|
|
426
472
|
s.refresh();
|
|
427
473
|
s
|
|
@@ -441,17 +487,28 @@ impl RealSysInfo {
|
|
|
441
487
|
lock.refresh_cpu_usage();
|
|
442
488
|
let mem = lock.used_memory();
|
|
443
489
|
let cpu = lock.global_cpu_usage() as f64 / 100.;
|
|
490
|
+
if let Some(cgroup_limits) = lock.cgroup_limits() {
|
|
491
|
+
self.total_mem
|
|
492
|
+
.store(cgroup_limits.total_memory, Ordering::Release);
|
|
493
|
+
self.cur_mem_usage.store(
|
|
494
|
+
cgroup_limits.total_memory - cgroup_limits.free_memory,
|
|
495
|
+
Ordering::Release,
|
|
496
|
+
);
|
|
497
|
+
}
|
|
444
498
|
self.cur_mem_usage.store(mem, Ordering::Release);
|
|
445
499
|
self.cur_cpu_usage.store(cpu.to_bits(), Ordering::Release);
|
|
446
500
|
self.last_refresh.store(Instant::now());
|
|
447
501
|
}
|
|
448
502
|
}
|
|
503
|
+
|
|
449
504
|
impl SystemResourceInfo for RealSysInfo {
|
|
450
505
|
fn total_mem(&self) -> u64 {
|
|
451
|
-
self.total_mem
|
|
506
|
+
self.total_mem.load(Ordering::Acquire)
|
|
452
507
|
}
|
|
453
508
|
|
|
454
509
|
fn used_mem(&self) -> u64 {
|
|
510
|
+
// TODO: This should really happen on a background thread since it's getting called from
|
|
511
|
+
// the async reserve
|
|
455
512
|
self.refresh_if_needed();
|
|
456
513
|
self.cur_mem_usage.load(Ordering::Acquire)
|
|
457
514
|
}
|
|
@@ -512,10 +569,47 @@ mod tests {
|
|
|
512
569
|
max_slots: 100,
|
|
513
570
|
ramp_throttle: Duration::from_millis(0),
|
|
514
571
|
});
|
|
515
|
-
let pd = MeteredPermitDealer::new(
|
|
572
|
+
let pd = MeteredPermitDealer::new(
|
|
573
|
+
rbs.clone(),
|
|
574
|
+
MetricsContext::no_op(),
|
|
575
|
+
None,
|
|
576
|
+
Arc::new(Default::default()),
|
|
577
|
+
);
|
|
578
|
+
let pd_s = pd.clone().into_sticky();
|
|
579
|
+
// Start with too high usage
|
|
580
|
+
used.store(90_000, Ordering::Release);
|
|
581
|
+
// Show workflow will always allow 1 each of sticky/non-sticky
|
|
582
|
+
assert!(rbs.try_reserve_slot(&pd).is_some());
|
|
583
|
+
assert!(rbs.try_reserve_slot(&pd_s).is_some());
|
|
584
|
+
assert!(rbs.try_reserve_slot(&pd).is_none());
|
|
585
|
+
assert!(rbs.try_reserve_slot(&pd_s).is_none());
|
|
586
|
+
used.store(0, Ordering::Release);
|
|
587
|
+
// Now it's willing to hand out slots again when usage is zero
|
|
516
588
|
assert!(rbs.try_reserve_slot(&pd).is_some());
|
|
589
|
+
assert!(rbs.try_reserve_slot(&pd_s).is_some());
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
#[test]
|
|
593
|
+
fn mem_activity_sync() {
|
|
594
|
+
let (fmis, used) = FakeMIS::new();
|
|
595
|
+
let rbs = Arc::new(ResourceController::new_with_sysinfo(test_options(), fmis))
|
|
596
|
+
.as_kind::<ActivitySlotKind>(ResourceSlotOptions {
|
|
597
|
+
min_slots: 0,
|
|
598
|
+
max_slots: 100,
|
|
599
|
+
ramp_throttle: Duration::from_millis(0),
|
|
600
|
+
});
|
|
601
|
+
let pd = MeteredPermitDealer::new(
|
|
602
|
+
rbs.clone(),
|
|
603
|
+
MetricsContext::no_op(),
|
|
604
|
+
None,
|
|
605
|
+
Arc::new(Default::default()),
|
|
606
|
+
);
|
|
607
|
+
// Start with too high usage
|
|
517
608
|
used.store(90_000, Ordering::Release);
|
|
518
609
|
assert!(rbs.try_reserve_slot(&pd).is_none());
|
|
610
|
+
used.store(0, Ordering::Release);
|
|
611
|
+
// Now it's willing to hand out slots again when usage is zero
|
|
612
|
+
assert!(rbs.try_reserve_slot(&pd).is_some());
|
|
519
613
|
}
|
|
520
614
|
|
|
521
615
|
#[tokio::test]
|
|
@@ -528,7 +622,47 @@ mod tests {
|
|
|
528
622
|
max_slots: 100,
|
|
529
623
|
ramp_throttle: Duration::from_millis(0),
|
|
530
624
|
});
|
|
531
|
-
let pd = MeteredPermitDealer::new(
|
|
625
|
+
let pd = MeteredPermitDealer::new(
|
|
626
|
+
rbs.clone(),
|
|
627
|
+
MetricsContext::no_op(),
|
|
628
|
+
None,
|
|
629
|
+
Arc::new(Default::default()),
|
|
630
|
+
);
|
|
631
|
+
let pd_s = pd.clone().into_sticky();
|
|
632
|
+
let order = crossbeam_queue::ArrayQueue::new(2);
|
|
633
|
+
// Show workflow will always allow 1 each of sticky/non-sticky
|
|
634
|
+
let _p1 = rbs.reserve_slot(&pd).await;
|
|
635
|
+
let _p2 = rbs.reserve_slot(&pd_s).await;
|
|
636
|
+
// Now we need to have some memory get freed before the next call resolves
|
|
637
|
+
let waits_free = async {
|
|
638
|
+
rbs.reserve_slot(&pd).await;
|
|
639
|
+
order.push(2).unwrap();
|
|
640
|
+
};
|
|
641
|
+
let frees = async {
|
|
642
|
+
used.store(70_000, Ordering::Release);
|
|
643
|
+
order.push(1).unwrap();
|
|
644
|
+
};
|
|
645
|
+
tokio::join!(waits_free, frees);
|
|
646
|
+
assert_eq!(order.pop(), Some(1));
|
|
647
|
+
assert_eq!(order.pop(), Some(2));
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
#[tokio::test]
|
|
651
|
+
async fn mem_activity_async() {
|
|
652
|
+
let (fmis, used) = FakeMIS::new();
|
|
653
|
+
used.store(90_000, Ordering::Release);
|
|
654
|
+
let rbs = Arc::new(ResourceController::new_with_sysinfo(test_options(), fmis))
|
|
655
|
+
.as_kind::<ActivitySlotKind>(ResourceSlotOptions {
|
|
656
|
+
min_slots: 0,
|
|
657
|
+
max_slots: 100,
|
|
658
|
+
ramp_throttle: Duration::from_millis(0),
|
|
659
|
+
});
|
|
660
|
+
let pd = MeteredPermitDealer::new(
|
|
661
|
+
rbs.clone(),
|
|
662
|
+
MetricsContext::no_op(),
|
|
663
|
+
None,
|
|
664
|
+
Arc::new(Default::default()),
|
|
665
|
+
);
|
|
532
666
|
let order = crossbeam_queue::ArrayQueue::new(2);
|
|
533
667
|
let waits_free = async {
|
|
534
668
|
rbs.reserve_slot(&pd).await;
|
|
@@ -552,7 +686,12 @@ mod tests {
|
|
|
552
686
|
max_slots: 100,
|
|
553
687
|
ramp_throttle: Duration::from_millis(0),
|
|
554
688
|
});
|
|
555
|
-
let pd = MeteredPermitDealer::new(
|
|
689
|
+
let pd = MeteredPermitDealer::new(
|
|
690
|
+
rbs.clone(),
|
|
691
|
+
MetricsContext::no_op(),
|
|
692
|
+
None,
|
|
693
|
+
Arc::new(Default::default()),
|
|
694
|
+
);
|
|
556
695
|
used.store(90_000, Ordering::Release);
|
|
557
696
|
let _p1 = pd.try_acquire_owned().unwrap();
|
|
558
697
|
let _p2 = pd.try_acquire_owned().unwrap();
|
|
@@ -11,7 +11,7 @@ use std::sync::{Arc, OnceLock};
|
|
|
11
11
|
use temporal_sdk_core_api::{
|
|
12
12
|
telemetry::metrics::TemporalMeter,
|
|
13
13
|
worker::{
|
|
14
|
-
ActivitySlotKind, LocalActivitySlotKind, SlotSupplier, WorkerConfig, WorkerTuner,
|
|
14
|
+
ActivitySlotKind, LocalActivitySlotKind, SlotKind, SlotSupplier, WorkerConfig, WorkerTuner,
|
|
15
15
|
WorkflowSlotKind,
|
|
16
16
|
},
|
|
17
17
|
};
|
|
@@ -32,13 +32,13 @@ pub struct TunerHolder {
|
|
|
32
32
|
pub struct TunerHolderOptions {
|
|
33
33
|
/// Options for workflow slots
|
|
34
34
|
#[builder(default, setter(strip_option))]
|
|
35
|
-
pub workflow_slot_options: Option<SlotSupplierOptions
|
|
35
|
+
pub workflow_slot_options: Option<SlotSupplierOptions<WorkflowSlotKind>>,
|
|
36
36
|
/// Options for activity slots
|
|
37
37
|
#[builder(default, setter(strip_option))]
|
|
38
|
-
pub activity_slot_options: Option<SlotSupplierOptions
|
|
38
|
+
pub activity_slot_options: Option<SlotSupplierOptions<ActivitySlotKind>>,
|
|
39
39
|
/// Options for local activity slots
|
|
40
40
|
#[builder(default, setter(strip_option))]
|
|
41
|
-
pub local_activity_slot_options: Option<SlotSupplierOptions
|
|
41
|
+
pub local_activity_slot_options: Option<SlotSupplierOptions<LocalActivitySlotKind>>,
|
|
42
42
|
/// Options that will apply to all resource based slot suppliers. Must be set if any slot
|
|
43
43
|
/// options are [SlotSupplierOptions::ResourceBased]
|
|
44
44
|
#[builder(default, setter(strip_option))]
|
|
@@ -67,6 +67,9 @@ impl TunerHolderOptions {
|
|
|
67
67
|
.workflow_task_slot_supplier(),
|
|
68
68
|
);
|
|
69
69
|
}
|
|
70
|
+
Some(SlotSupplierOptions::Custom(ss)) => {
|
|
71
|
+
builder.workflow_slot_supplier(ss);
|
|
72
|
+
}
|
|
70
73
|
None => {}
|
|
71
74
|
}
|
|
72
75
|
match self.activity_slot_options {
|
|
@@ -82,6 +85,9 @@ impl TunerHolderOptions {
|
|
|
82
85
|
.activity_task_slot_supplier(),
|
|
83
86
|
);
|
|
84
87
|
}
|
|
88
|
+
Some(SlotSupplierOptions::Custom(ss)) => {
|
|
89
|
+
builder.activity_slot_supplier(ss);
|
|
90
|
+
}
|
|
85
91
|
None => {}
|
|
86
92
|
}
|
|
87
93
|
match self.local_activity_slot_options {
|
|
@@ -97,6 +103,9 @@ impl TunerHolderOptions {
|
|
|
97
103
|
.local_activity_slot_supplier(),
|
|
98
104
|
);
|
|
99
105
|
}
|
|
106
|
+
Some(SlotSupplierOptions::Custom(ss)) => {
|
|
107
|
+
builder.local_activity_slot_supplier(ss);
|
|
108
|
+
}
|
|
100
109
|
None => {}
|
|
101
110
|
}
|
|
102
111
|
Ok(builder.build())
|
|
@@ -104,8 +113,8 @@ impl TunerHolderOptions {
|
|
|
104
113
|
}
|
|
105
114
|
|
|
106
115
|
/// Options for known kinds of slot suppliers
|
|
107
|
-
#[derive(Clone, Debug)]
|
|
108
|
-
pub enum SlotSupplierOptions {
|
|
116
|
+
#[derive(Clone, derive_more::Debug)]
|
|
117
|
+
pub enum SlotSupplierOptions<SK: SlotKind> {
|
|
109
118
|
/// Options for a [FixedSizeSlotSupplier]
|
|
110
119
|
FixedSize {
|
|
111
120
|
/// The number of slots the fixed supplier will have
|
|
@@ -113,6 +122,9 @@ pub enum SlotSupplierOptions {
|
|
|
113
122
|
},
|
|
114
123
|
/// Options for a [ResourceBasedSlots]
|
|
115
124
|
ResourceBased(ResourceSlotOptions),
|
|
125
|
+
/// A user-implemented slot supplier
|
|
126
|
+
#[debug("Custom")]
|
|
127
|
+
Custom(Arc<dyn SlotSupplier<SlotKind = SK> + Send + Sync>),
|
|
116
128
|
}
|
|
117
129
|
|
|
118
130
|
impl TunerHolderOptionsBuilder {
|
|
@@ -5,9 +5,8 @@ use crate::{
|
|
|
5
5
|
workflow::{CacheMissFetchReq, PermittedWFT, PreparedWFT},
|
|
6
6
|
},
|
|
7
7
|
};
|
|
8
|
-
use
|
|
8
|
+
use futures_util::{future::BoxFuture, FutureExt, Stream, TryFutureExt};
|
|
9
9
|
use itertools::Itertools;
|
|
10
|
-
use once_cell::sync::Lazy;
|
|
11
10
|
use std::{
|
|
12
11
|
collections::VecDeque,
|
|
13
12
|
fmt::Debug,
|
|
@@ -15,18 +14,21 @@ use std::{
|
|
|
15
14
|
mem,
|
|
16
15
|
mem::transmute,
|
|
17
16
|
pin::Pin,
|
|
18
|
-
sync::Arc,
|
|
17
|
+
sync::{Arc, LazyLock},
|
|
19
18
|
task::{Context, Poll},
|
|
20
19
|
};
|
|
21
20
|
use temporal_sdk_core_protos::temporal::api::{
|
|
22
21
|
enums::v1::EventType,
|
|
23
|
-
history::v1::{
|
|
22
|
+
history::v1::{
|
|
23
|
+
history_event, history_event::Attributes, History, HistoryEvent,
|
|
24
|
+
WorkflowTaskCompletedEventAttributes,
|
|
25
|
+
},
|
|
24
26
|
};
|
|
25
27
|
use tracing::Instrument;
|
|
26
28
|
|
|
27
|
-
static EMPTY_FETCH_ERR:
|
|
28
|
-
|
|
29
|
-
static EMPTY_TASK_ERR:
|
|
29
|
+
static EMPTY_FETCH_ERR: LazyLock<tonic::Status> =
|
|
30
|
+
LazyLock::new(|| tonic::Status::unknown("Fetched empty history page"));
|
|
31
|
+
static EMPTY_TASK_ERR: LazyLock<tonic::Status> = LazyLock::new(|| {
|
|
30
32
|
tonic::Status::unknown("Received an empty workflow task with no queries or history")
|
|
31
33
|
});
|
|
32
34
|
|
|
@@ -166,7 +168,7 @@ impl HistoryPaginator {
|
|
|
166
168
|
}
|
|
167
169
|
|
|
168
170
|
pub(super) async fn from_fetchreq(
|
|
169
|
-
mut req: CacheMissFetchReq
|
|
171
|
+
mut req: Box<CacheMissFetchReq>,
|
|
170
172
|
client: Arc<dyn WorkerClient>,
|
|
171
173
|
) -> Result<PermittedWFT, tonic::Status> {
|
|
172
174
|
let mut paginator = Self {
|
|
@@ -697,6 +699,7 @@ fn find_end_index_of_next_wft_seq(
|
|
|
697
699
|
}
|
|
698
700
|
let mut last_index = 0;
|
|
699
701
|
let mut saw_any_command_event = false;
|
|
702
|
+
let mut wft_started_event_id_to_index = vec![];
|
|
700
703
|
for (ix, e) in events.iter().enumerate() {
|
|
701
704
|
last_index = ix;
|
|
702
705
|
|
|
@@ -714,12 +717,13 @@ fn find_end_index_of_next_wft_seq(
|
|
|
714
717
|
}
|
|
715
718
|
|
|
716
719
|
if e.event_type() == EventType::WorkflowTaskStarted {
|
|
720
|
+
wft_started_event_id_to_index.push((e.event_id, ix));
|
|
717
721
|
if let Some(next_event) = events.get(ix + 1) {
|
|
718
|
-
let
|
|
722
|
+
let next_event_type = next_event.event_type();
|
|
719
723
|
// If the next event is WFT timeout or fail, or abrupt WF execution end, that
|
|
720
724
|
// doesn't conclude a WFT sequence.
|
|
721
725
|
if matches!(
|
|
722
|
-
|
|
726
|
+
next_event_type,
|
|
723
727
|
EventType::WorkflowTaskFailed
|
|
724
728
|
| EventType::WorkflowTaskTimedOut
|
|
725
729
|
| EventType::WorkflowExecutionTimedOut
|
|
@@ -731,11 +735,38 @@ fn find_end_index_of_next_wft_seq(
|
|
|
731
735
|
// If we've never seen an interesting event and the next two events are a completion
|
|
732
736
|
// followed immediately again by scheduled, then this is a WFT heartbeat and also
|
|
733
737
|
// doesn't conclude the sequence.
|
|
734
|
-
else if
|
|
738
|
+
else if next_event_type == EventType::WorkflowTaskCompleted {
|
|
735
739
|
if let Some(next_next_event) = events.get(ix + 2) {
|
|
736
740
|
if next_next_event.event_type() == EventType::WorkflowTaskScheduled {
|
|
737
741
|
continue;
|
|
738
742
|
} else {
|
|
743
|
+
// If we see an update accepted command after WFT completed, we want to
|
|
744
|
+
// conclude the WFT sequence where that update should have been
|
|
745
|
+
// processed. We don't need to check for any other command types,
|
|
746
|
+
// because the only thing that can run before an update validator is a
|
|
747
|
+
// signal handler - but if a signal handler ran then there would have
|
|
748
|
+
// been a previous signal event, and we would've already concluded the
|
|
749
|
+
// previous WFT sequence.
|
|
750
|
+
if let Some(
|
|
751
|
+
Attributes::WorkflowExecutionUpdateAcceptedEventAttributes(
|
|
752
|
+
ref attr,
|
|
753
|
+
),
|
|
754
|
+
) = next_next_event.attributes
|
|
755
|
+
{
|
|
756
|
+
// Find index of closest WFT started before sequencing id
|
|
757
|
+
if let Some(ret_ix) = wft_started_event_id_to_index
|
|
758
|
+
.iter()
|
|
759
|
+
.rev()
|
|
760
|
+
.find_map(|(eid, ix)| {
|
|
761
|
+
if *eid < attr.accepted_request_sequencing_event_id {
|
|
762
|
+
return Some(*ix);
|
|
763
|
+
}
|
|
764
|
+
None
|
|
765
|
+
})
|
|
766
|
+
{
|
|
767
|
+
return NextWFTSeqEndIndex::Complete(ret_ix);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
739
770
|
return NextWFTSeqEndIndex::Complete(ix);
|
|
740
771
|
}
|
|
741
772
|
} else if !has_last_wft && !saw_any_command_event {
|
|
@@ -766,8 +797,7 @@ mod tests {
|
|
|
766
797
|
test_help::{canned_histories, hist_to_poll_resp, mock_sdk_cfg, MockPollCfg, ResponseType},
|
|
767
798
|
worker::client::mocks::mock_workflow_client,
|
|
768
799
|
};
|
|
769
|
-
use
|
|
770
|
-
use futures_util::TryStreamExt;
|
|
800
|
+
use futures_util::{StreamExt, TryStreamExt};
|
|
771
801
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
772
802
|
use temporal_client::WorkflowOptions;
|
|
773
803
|
use temporal_sdk::WfContext;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
//! never ever be removed from behind `#[cfg(test)]` compilation.
|
|
5
5
|
|
|
6
6
|
use dashmap::{mapref::entry::Entry, DashMap, DashSet};
|
|
7
|
-
use
|
|
7
|
+
use std::sync::LazyLock;
|
|
8
8
|
use std::{
|
|
9
9
|
path::PathBuf,
|
|
10
10
|
sync::{
|
|
@@ -16,11 +16,11 @@ use std::{
|
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
// During test we want to know about which transitions we've covered in state machines
|
|
19
|
-
static COVERED_TRANSITIONS:
|
|
20
|
-
|
|
21
|
-
static COVERAGE_SENDER:
|
|
22
|
-
|
|
23
|
-
static THREAD_HANDLE:
|
|
19
|
+
static COVERED_TRANSITIONS: LazyLock<DashMap<String, DashSet<CoveredTransition>>> =
|
|
20
|
+
LazyLock::new(DashMap::new);
|
|
21
|
+
static COVERAGE_SENDER: LazyLock<SyncSender<(String, CoveredTransition)>> =
|
|
22
|
+
LazyLock::new(spawn_save_coverage_at_end);
|
|
23
|
+
static THREAD_HANDLE: LazyLock<Mutex<Option<JoinHandle<()>>>> = LazyLock::new(|| Mutex::new(None));
|
|
24
24
|
|
|
25
25
|
#[derive(Eq, PartialEq, Hash, Debug)]
|
|
26
26
|
struct CoveredTransition {
|
|
@@ -635,8 +635,8 @@ impl WorkflowMachines {
|
|
|
635
635
|
event.attributes,
|
|
636
636
|
Some(history_event::Attributes::WorkflowExecutionUpdateAdmittedEventAttributes(_)),
|
|
637
637
|
) {
|
|
638
|
-
// The server has sent a durable update admitted event: create the message that
|
|
639
|
-
// for a non-durable update request message.
|
|
638
|
+
// The server has sent a durable update admitted event: create the message that
|
|
639
|
+
// would have been sent for a non-durable update request message.
|
|
640
640
|
let msg = IncomingProtocolMessage::try_from(&event).context(
|
|
641
641
|
"Failed to create protocol message from WorkflowExecutionUpdateAdmittedEvent",
|
|
642
642
|
)?;
|
|
@@ -745,9 +745,10 @@ impl WorkflowMachines {
|
|
|
745
745
|
history_event::Attributes::WorkflowExecutionUpdateAcceptedEventAttributes(ref atts),
|
|
746
746
|
) = e.attributes
|
|
747
747
|
{
|
|
748
|
-
// We've encountered an UpdateAccepted event during replay: pretend that we received
|
|
749
|
-
// would have when receiving an update request under not-replay. If
|
|
750
|
-
// UpdateAdmitted event, then use the message that we
|
|
748
|
+
// We've encountered an UpdateAccepted event during replay: pretend that we received
|
|
749
|
+
// the message we would have when receiving an update request under not-replay. If
|
|
750
|
+
// this event was preceded by an UpdateAdmitted event, then use the message that we
|
|
751
|
+
// created when we encountered that.
|
|
751
752
|
delayed_actions.push(DelayedAction::ProtocolMessage(
|
|
752
753
|
update_admitted_event_messages
|
|
753
754
|
.remove(&atts.protocol_instance_id)
|
|
@@ -369,7 +369,7 @@ impl ManagedRun {
|
|
|
369
369
|
mut commands: Vec<WFCommand>,
|
|
370
370
|
used_flags: Vec<u32>,
|
|
371
371
|
resp_chan: Option<oneshot::Sender<ActivationCompleteResult>>,
|
|
372
|
-
) -> Result<RunUpdateAct, NextPageReq
|
|
372
|
+
) -> Result<RunUpdateAct, Box<NextPageReq>> {
|
|
373
373
|
let activation_was_only_eviction = self.activation_is_eviction();
|
|
374
374
|
let (task_token, has_pending_query, start_time) = if let Some(entry) = self.wft.as_ref() {
|
|
375
375
|
(
|
|
@@ -447,10 +447,10 @@ impl ManagedRun {
|
|
|
447
447
|
return if let Some(paginator) = self.paginator.take() {
|
|
448
448
|
debug!("Need to fetch a history page before next WFT can be applied");
|
|
449
449
|
self.completion_waiting_on_page_fetch = Some(rac);
|
|
450
|
-
Err(NextPageReq {
|
|
450
|
+
Err(Box::new(NextPageReq {
|
|
451
451
|
paginator,
|
|
452
452
|
span: Span::current(),
|
|
453
|
-
})
|
|
453
|
+
}))
|
|
454
454
|
} else {
|
|
455
455
|
Ok(self.update_to_acts(Err(RunUpdateErr {
|
|
456
456
|
source: WFMachinesError::Fatal(
|
|
@@ -38,8 +38,7 @@ use crate::{
|
|
|
38
38
|
MetricsContext,
|
|
39
39
|
};
|
|
40
40
|
use anyhow::anyhow;
|
|
41
|
-
use
|
|
42
|
-
use futures_util::{future::abortable, stream};
|
|
41
|
+
use futures_util::{future::abortable, stream, stream::BoxStream, Stream, StreamExt};
|
|
43
42
|
use itertools::Itertools;
|
|
44
43
|
use prost_types::TimestampError;
|
|
45
44
|
use std::{
|
|
@@ -121,7 +120,7 @@ pub(crate) struct Workflows {
|
|
|
121
120
|
/// If set, can be used to reserve activity task slots for eager-return of new activity tasks.
|
|
122
121
|
activity_tasks_handle: Option<ActivitiesFromWFTsHandle>,
|
|
123
122
|
/// Ensures we stay at or below this worker's maximum concurrent workflow task limit
|
|
124
|
-
wft_semaphore:
|
|
123
|
+
wft_semaphore: MeteredPermitDealer<WorkflowSlotKind>,
|
|
125
124
|
local_act_mgr: Arc<LocalActivityManager>,
|
|
126
125
|
ever_polled: AtomicBool,
|
|
127
126
|
}
|
|
@@ -149,7 +148,7 @@ impl Workflows {
|
|
|
149
148
|
basics: WorkflowBasics,
|
|
150
149
|
sticky_attrs: Option<StickyExecutionAttributes>,
|
|
151
150
|
client: Arc<dyn WorkerClient>,
|
|
152
|
-
wft_semaphore:
|
|
151
|
+
wft_semaphore: MeteredPermitDealer<WorkflowSlotKind>,
|
|
153
152
|
wft_stream: impl Stream<Item = WFTStreamIn> + Send + 'static,
|
|
154
153
|
local_activity_request_sink: impl LocalActivityRequestSink,
|
|
155
154
|
local_act_mgr: Arc<LocalActivityManager>,
|
|
@@ -195,8 +194,8 @@ impl Workflows {
|
|
|
195
194
|
local_activity_request_sink,
|
|
196
195
|
);
|
|
197
196
|
|
|
198
|
-
// However, we want to avoid plowing ahead until we've been asked to poll at
|
|
199
|
-
// once. This supports activity-only workers.
|
|
197
|
+
// However, we want to avoid plowing ahead until we've been asked to poll at
|
|
198
|
+
// least once. This supports activity-only workers.
|
|
200
199
|
let do_poll = tokio::select! {
|
|
201
200
|
sp = start_polling_rx => {
|
|
202
201
|
sp.is_ok()
|
|
@@ -560,7 +559,7 @@ impl Workflows {
|
|
|
560
559
|
|
|
561
560
|
/// Must be called after every activation completion has finished
|
|
562
561
|
fn post_activation(&self, msg: PostActivationMsg) {
|
|
563
|
-
self.send_local(msg);
|
|
562
|
+
self.send_local(Box::new(msg));
|
|
564
563
|
}
|
|
565
564
|
|
|
566
565
|
/// Handle server errors from either completing or failing a workflow task. Un-handleable errors
|
|
@@ -715,6 +714,13 @@ impl Workflows {
|
|
|
715
714
|
}
|
|
716
715
|
}
|
|
717
716
|
}
|
|
717
|
+
|
|
718
|
+
pub(super) fn get_sticky_queue_name(&self) -> Option<String> {
|
|
719
|
+
self.sticky_attrs
|
|
720
|
+
.as_ref()
|
|
721
|
+
.and_then(|sa| sa.worker_task_queue.as_ref())
|
|
722
|
+
.map(|tq| tq.name.clone())
|
|
723
|
+
}
|
|
718
724
|
}
|
|
719
725
|
|
|
720
726
|
/// Returned when a cache miss happens and we need to fetch history from the beginning to
|
|
@@ -9,11 +9,10 @@ use crate::{
|
|
|
9
9
|
},
|
|
10
10
|
},
|
|
11
11
|
};
|
|
12
|
-
use
|
|
13
|
-
use futures_util::{stream, stream::PollNext, FutureExt, StreamExt};
|
|
12
|
+
use futures_util::{stream, stream::PollNext, FutureExt, Stream, StreamExt};
|
|
14
13
|
use std::{future, sync::Arc};
|
|
15
|
-
use temporal_sdk_core_api::worker::
|
|
16
|
-
use temporal_sdk_core_protos::TaskToken;
|
|
14
|
+
use temporal_sdk_core_api::worker::WorkflowSlotKind;
|
|
15
|
+
use temporal_sdk_core_protos::{coresdk::WorkflowSlotInfo, TaskToken};
|
|
17
16
|
use tracing::Span;
|
|
18
17
|
|
|
19
18
|
/// Transforms incoming validated WFTs and history fetching requests into [PermittedWFT]s ready
|
|
@@ -50,8 +49,8 @@ pub(crate) type WFTStreamIn = Result<
|
|
|
50
49
|
>;
|
|
51
50
|
#[derive(derive_more::From, Debug)]
|
|
52
51
|
pub(super) enum HistoryFetchReq {
|
|
53
|
-
Full(CacheMissFetchReq
|
|
54
|
-
NextPage(NextPageReq
|
|
52
|
+
Full(Box<CacheMissFetchReq>, Arc<HistfetchRC>),
|
|
53
|
+
NextPage(Box<NextPageReq>, Arc<HistfetchRC>),
|
|
55
54
|
}
|
|
56
55
|
/// Used inside of `Arc`s to ensure we don't shutdown while there are outstanding fetches.
|
|
57
56
|
#[derive(Debug)]
|
|
@@ -76,7 +75,8 @@ impl WFTExtractor {
|
|
|
76
75
|
Ok(match HistoryPaginator::from_poll(wft, client).await {
|
|
77
76
|
Ok((pag, prep)) => WFTExtractorOutput::NewWFT(PermittedWFT {
|
|
78
77
|
permit: permit.into_used(WorkflowSlotInfo {
|
|
79
|
-
workflow_type: prep.workflow_type.
|
|
78
|
+
workflow_type: prep.workflow_type.clone(),
|
|
79
|
+
is_sticky: prep.is_incremental(),
|
|
80
80
|
}),
|
|
81
81
|
work: prep,
|
|
82
82
|
paginator: pag,
|