@temporalio/core-bridge 1.6.0 → 1.7.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 (138) hide show
  1. package/Cargo.lock +520 -456
  2. package/lib/index.d.ts +8 -6
  3. package/lib/index.js.map +1 -1
  4. package/package.json +8 -3
  5. package/releases/aarch64-apple-darwin/index.node +0 -0
  6. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  7. package/releases/x86_64-apple-darwin/index.node +0 -0
  8. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  9. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  10. package/sdk-core/.buildkite/docker/Dockerfile +2 -2
  11. package/sdk-core/.buildkite/docker/docker-compose.yaml +1 -1
  12. package/sdk-core/.buildkite/pipeline.yml +1 -1
  13. package/sdk-core/.github/workflows/heavy.yml +1 -0
  14. package/sdk-core/README.md +13 -7
  15. package/sdk-core/client/src/lib.rs +27 -9
  16. package/sdk-core/client/src/metrics.rs +17 -8
  17. package/sdk-core/client/src/raw.rs +3 -3
  18. package/sdk-core/core/Cargo.toml +3 -4
  19. package/sdk-core/core/src/abstractions/take_cell.rs +28 -0
  20. package/sdk-core/core/src/abstractions.rs +197 -18
  21. package/sdk-core/core/src/core_tests/activity_tasks.rs +137 -45
  22. package/sdk-core/core/src/core_tests/child_workflows.rs +6 -5
  23. package/sdk-core/core/src/core_tests/determinism.rs +212 -2
  24. package/sdk-core/core/src/core_tests/local_activities.rs +183 -36
  25. package/sdk-core/core/src/core_tests/queries.rs +32 -14
  26. package/sdk-core/core/src/core_tests/workers.rs +8 -5
  27. package/sdk-core/core/src/core_tests/workflow_tasks.rs +340 -51
  28. package/sdk-core/core/src/ephemeral_server/mod.rs +110 -8
  29. package/sdk-core/core/src/internal_flags.rs +141 -0
  30. package/sdk-core/core/src/lib.rs +14 -9
  31. package/sdk-core/core/src/replay/mod.rs +16 -27
  32. package/sdk-core/core/src/telemetry/metrics.rs +69 -35
  33. package/sdk-core/core/src/telemetry/mod.rs +38 -14
  34. package/sdk-core/core/src/telemetry/prometheus_server.rs +19 -13
  35. package/sdk-core/core/src/test_help/mod.rs +65 -13
  36. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +119 -160
  37. package/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +89 -0
  38. package/sdk-core/core/src/worker/activities/local_activities.rs +122 -6
  39. package/sdk-core/core/src/worker/activities.rs +347 -173
  40. package/sdk-core/core/src/worker/client/mocks.rs +22 -2
  41. package/sdk-core/core/src/worker/client.rs +18 -2
  42. package/sdk-core/core/src/worker/mod.rs +137 -44
  43. package/sdk-core/core/src/worker/workflow/history_update.rs +132 -51
  44. package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +207 -166
  45. package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +6 -7
  46. package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +6 -7
  47. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +157 -82
  48. package/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +12 -12
  49. package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +6 -7
  50. package/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +13 -15
  51. package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +170 -60
  52. package/sdk-core/core/src/worker/workflow/machines/mod.rs +24 -16
  53. package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +6 -8
  54. package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +320 -204
  55. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +10 -13
  56. package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +15 -23
  57. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +187 -46
  58. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +237 -111
  59. package/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +13 -13
  60. package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +10 -6
  61. package/sdk-core/core/src/worker/workflow/managed_run.rs +81 -62
  62. package/sdk-core/core/src/worker/workflow/mod.rs +341 -79
  63. package/sdk-core/core/src/worker/workflow/run_cache.rs +18 -11
  64. package/sdk-core/core/src/worker/workflow/wft_extraction.rs +15 -3
  65. package/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +2 -0
  66. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +75 -52
  67. package/sdk-core/core-api/Cargo.toml +0 -1
  68. package/sdk-core/core-api/src/lib.rs +13 -7
  69. package/sdk-core/core-api/src/telemetry.rs +4 -6
  70. package/sdk-core/core-api/src/worker.rs +5 -0
  71. package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +80 -55
  72. package/sdk-core/fsm/rustfsm_trait/src/lib.rs +22 -68
  73. package/sdk-core/histories/ends_empty_wft_complete.bin +0 -0
  74. package/sdk-core/histories/old_change_marker_format.bin +0 -0
  75. package/sdk-core/protos/api_upstream/.github/CODEOWNERS +2 -1
  76. package/sdk-core/protos/api_upstream/Makefile +1 -1
  77. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +5 -17
  78. package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +11 -0
  79. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +1 -6
  80. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +6 -6
  81. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +5 -0
  82. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +22 -6
  83. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +48 -19
  84. package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +2 -0
  85. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +3 -0
  86. package/sdk-core/protos/api_upstream/temporal/api/{enums/v1/interaction_type.proto → protocol/v1/message.proto} +29 -11
  87. package/sdk-core/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +63 -0
  88. package/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +111 -0
  89. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +59 -28
  90. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +2 -2
  91. package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +7 -8
  92. package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +10 -7
  93. package/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +19 -30
  94. package/sdk-core/protos/local/temporal/sdk/core/common/common.proto +1 -0
  95. package/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +1 -0
  96. package/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +8 -0
  97. package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +65 -60
  98. package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +85 -84
  99. package/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +9 -3
  100. package/sdk-core/sdk/Cargo.toml +1 -1
  101. package/sdk-core/sdk/src/lib.rs +21 -5
  102. package/sdk-core/sdk/src/workflow_context/options.rs +7 -1
  103. package/sdk-core/sdk/src/workflow_context.rs +24 -17
  104. package/sdk-core/sdk/src/workflow_future.rs +9 -3
  105. package/sdk-core/sdk-core-protos/src/history_builder.rs +114 -89
  106. package/sdk-core/sdk-core-protos/src/history_info.rs +6 -1
  107. package/sdk-core/sdk-core-protos/src/lib.rs +205 -64
  108. package/sdk-core/test-utils/src/canned_histories.rs +106 -296
  109. package/sdk-core/test-utils/src/lib.rs +32 -5
  110. package/sdk-core/tests/heavy_tests.rs +10 -43
  111. package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +25 -3
  112. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +5 -3
  113. package/sdk-core/tests/integ_tests/metrics_tests.rs +218 -16
  114. package/sdk-core/tests/integ_tests/polling_tests.rs +3 -8
  115. package/sdk-core/tests/integ_tests/queries_tests.rs +4 -2
  116. package/sdk-core/tests/integ_tests/visibility_tests.rs +34 -23
  117. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +97 -81
  118. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -0
  119. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -0
  120. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +80 -3
  121. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +5 -1
  122. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +1 -0
  123. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +25 -3
  124. package/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +2 -4
  125. package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +30 -0
  126. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +64 -0
  127. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -0
  128. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +4 -0
  129. package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +3 -1
  130. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +7 -2
  131. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +6 -7
  132. package/sdk-core/tests/integ_tests/workflow_tests.rs +8 -8
  133. package/sdk-core/tests/main.rs +16 -25
  134. package/sdk-core/tests/runner.rs +11 -9
  135. package/src/conversions.rs +14 -8
  136. package/src/runtime.rs +9 -8
  137. package/ts/index.ts +8 -6
  138. package/sdk-core/protos/api_upstream/temporal/api/interaction/v1/message.proto +0 -87
@@ -19,15 +19,15 @@ use std::{
19
19
  };
20
20
  use temporal_sdk_core_protos::temporal::api::{
21
21
  enums::v1::EventType,
22
- history::v1::{History, HistoryEvent},
22
+ history::v1::{history_event, History, HistoryEvent, WorkflowTaskCompletedEventAttributes},
23
23
  };
24
24
  use tracing::Instrument;
25
25
 
26
26
  lazy_static::lazy_static! {
27
27
  static ref EMPTY_FETCH_ERR: tonic::Status
28
- = tonic::Status::data_loss("Fetched empty history page");
28
+ = tonic::Status::unknown("Fetched empty history page");
29
29
  static ref EMPTY_TASK_ERR: tonic::Status
30
- = tonic::Status::data_loss("Received an empty workflow task with no queries or history");
30
+ = tonic::Status::unknown("Received an empty workflow task with no queries or history");
31
31
  }
32
32
 
33
33
  /// Represents one or more complete WFT sequences. History events are expected to be consumed from
@@ -42,6 +42,9 @@ pub struct HistoryUpdate {
42
42
  /// extracted from. Hence, while processing multiple logical WFTs during replay which were part
43
43
  /// of one large history fetched from server, multiple updates may have the same value here.
44
44
  pub previous_wft_started_id: i64,
45
+ /// The `started_event_id` field from the WFT which this update is tied to. Multiple updates
46
+ /// may have the same value if they're associated with the same WFT.
47
+ pub wft_started_id: i64,
45
48
  /// True if this update contains the final WFT in history, and no more attempts to extract
46
49
  /// additional updates should be made.
47
50
  has_last_wft: bool,
@@ -61,28 +64,11 @@ impl Debug for HistoryUpdate {
61
64
  }
62
65
  }
63
66
  }
64
- impl HistoryUpdate {
65
- /// Sometimes it's useful to take an update out of something without needing to use an option
66
- /// field. Use this to replace the field with an empty update.
67
- pub fn dummy() -> Self {
68
- Self {
69
- events: vec![],
70
- previous_wft_started_id: -1,
71
- has_last_wft: false,
72
- }
73
- }
74
- pub fn is_real(&self) -> bool {
75
- self.previous_wft_started_id >= 0
76
- }
77
- pub fn first_event_id(&self) -> Option<i64> {
78
- self.events.get(0).map(|e| e.event_id)
79
- }
80
- }
81
67
 
82
68
  #[derive(Debug)]
83
69
  pub enum NextWFT {
84
70
  ReplayOver,
85
- WFT(Vec<HistoryEvent>),
71
+ WFT(Vec<HistoryEvent>, bool),
86
72
  NeedFetch,
87
73
  }
88
74
 
@@ -97,6 +83,7 @@ pub struct HistoryPaginator {
97
83
  pub(crate) wf_id: String,
98
84
  pub(crate) run_id: String,
99
85
  pub(crate) previous_wft_started_id: i64,
86
+ pub(crate) wft_started_event_id: i64,
100
87
 
101
88
  #[cfg_attr(feature = "save_wf_inputs", serde(skip))]
102
89
  client: Arc<dyn WorkerClient>,
@@ -147,6 +134,7 @@ impl HistoryPaginator {
147
134
  let mut paginator = HistoryPaginator::new(
148
135
  wft.history,
149
136
  wft.previous_started_event_id,
137
+ wft.started_event_id,
150
138
  wft.workflow_execution.workflow_id.clone(),
151
139
  wft.workflow_execution.run_id.clone(),
152
140
  npt,
@@ -156,7 +144,13 @@ impl HistoryPaginator {
156
144
  return Err(EMPTY_TASK_ERR.clone());
157
145
  }
158
146
  let update = if empty_hist {
159
- HistoryUpdate::from_events([], wft.previous_started_event_id, true).0
147
+ HistoryUpdate::from_events(
148
+ [],
149
+ wft.previous_started_event_id,
150
+ wft.started_event_id,
151
+ true,
152
+ )
153
+ .0
160
154
  } else {
161
155
  paginator.extract_next_update().await?
162
156
  };
@@ -180,6 +174,7 @@ impl HistoryPaginator {
180
174
  wf_id: req.original_wft.work.execution.workflow_id.clone(),
181
175
  run_id: req.original_wft.work.execution.run_id.clone(),
182
176
  previous_wft_started_id: req.original_wft.work.update.previous_wft_started_id,
177
+ wft_started_event_id: req.original_wft.work.update.wft_started_id,
183
178
  client,
184
179
  event_queue: Default::default(),
185
180
  next_page_token: NextPageToken::FetchFromStart,
@@ -194,6 +189,7 @@ impl HistoryPaginator {
194
189
  fn new(
195
190
  initial_history: History,
196
191
  previous_wft_started_id: i64,
192
+ wft_started_event_id: i64,
197
193
  wf_id: String,
198
194
  run_id: String,
199
195
  next_page_token: impl Into<NextPageToken>,
@@ -214,6 +210,7 @@ impl HistoryPaginator {
214
210
  next_page_token,
215
211
  final_events,
216
212
  previous_wft_started_id,
213
+ wft_started_event_id,
217
214
  }
218
215
  }
219
216
 
@@ -228,6 +225,7 @@ impl HistoryPaginator {
228
225
  next_page_token: NextPageToken::FetchFromStart,
229
226
  final_events: vec![],
230
227
  previous_wft_started_id: -2,
228
+ wft_started_event_id: -2,
231
229
  }
232
230
  }
233
231
 
@@ -242,31 +240,42 @@ impl HistoryPaginator {
242
240
  /// we have two, or until we are at the end of history.
243
241
  pub(crate) async fn extract_next_update(&mut self) -> Result<HistoryUpdate, tonic::Status> {
244
242
  loop {
245
- self.get_next_page().await?;
243
+ let no_next_page = !self.get_next_page().await?;
246
244
  let current_events = mem::take(&mut self.event_queue);
247
- if current_events.is_empty() {
248
- // If next page fetching happened, and we still ended up with no events, something
249
- // is wrong. We're expecting there to be more events to be able to extract this
250
- // update, but server isn't giving us any. We have no choice except to give up and
251
- // evict.
245
+ let seen_enough_events = current_events
246
+ .back()
247
+ .map(|e| e.event_id)
248
+ .unwrap_or_default()
249
+ >= self.wft_started_event_id;
250
+ if current_events.is_empty() || (no_next_page && !seen_enough_events) {
251
+ // If next page fetching happened, and we still ended up with no or insufficient
252
+ // events, something is wrong. We're expecting there to be more events to be able to
253
+ // extract this update, but server isn't giving us any. We have no choice except to
254
+ // give up and evict.
252
255
  error!(
253
256
  "We expected to be able to fetch more events but server says there are none"
254
257
  );
255
258
  return Err(EMPTY_FETCH_ERR.clone());
256
259
  }
257
260
  let first_event_id = current_events.front().unwrap().event_id;
258
- // If there are some events at the end of the fetched events which represent only a
259
- // portion of a complete WFT, retain them to be used in the next extraction.
260
- let no_more = matches!(self.next_page_token, NextPageToken::Done);
261
- let (update, extra) =
262
- HistoryUpdate::from_events(current_events, self.previous_wft_started_id, no_more);
261
+ // We only *really* have the last WFT if the events go all the way up to at least the
262
+ // WFT started event id. Otherwise we somehow still have partial history.
263
+ let no_more = matches!(self.next_page_token, NextPageToken::Done) && seen_enough_events;
264
+ let (update, extra) = HistoryUpdate::from_events(
265
+ current_events,
266
+ self.previous_wft_started_id,
267
+ self.wft_started_event_id,
268
+ no_more,
269
+ );
263
270
  let extra_eid_same = extra
264
271
  .first()
265
272
  .map(|e| e.event_id == first_event_id)
266
273
  .unwrap_or_default();
274
+ // If there are some events at the end of the fetched events which represent only a
275
+ // portion of a complete WFT, retain them to be used in the next extraction.
267
276
  self.event_queue = extra.into();
268
277
  if !no_more && extra_eid_same {
269
- // There was not a meaningful WFT in the whole page. We must fetch more
278
+ // There was not a meaningful WFT in the whole page. We must fetch more.
270
279
  continue;
271
280
  }
272
281
  return Ok(update);
@@ -278,7 +287,7 @@ impl HistoryPaginator {
278
287
  async fn get_next_page(&mut self) -> Result<bool, tonic::Status> {
279
288
  let history = loop {
280
289
  let npt = match mem::replace(&mut self.next_page_token, NextPageToken::Done) {
281
- // If there's no open request and the last page token we got was empty, we're done.
290
+ // If the last page token we got was empty, we're done.
282
291
  NextPageToken::Done => return Ok(false),
283
292
  NextPageToken::FetchFromStart => vec![],
284
293
  NextPageToken::Next(v) => v,
@@ -374,6 +383,23 @@ impl Stream for StreamingHistoryPaginator {
374
383
  }
375
384
 
376
385
  impl HistoryUpdate {
386
+ /// Sometimes it's useful to take an update out of something without needing to use an option
387
+ /// field. Use this to replace the field with an empty update.
388
+ pub fn dummy() -> Self {
389
+ Self {
390
+ events: vec![],
391
+ previous_wft_started_id: -1,
392
+ wft_started_id: -1,
393
+ has_last_wft: false,
394
+ }
395
+ }
396
+ pub fn is_real(&self) -> bool {
397
+ self.previous_wft_started_id >= 0
398
+ }
399
+ pub fn first_event_id(&self) -> Option<i64> {
400
+ self.events.get(0).map(|e| e.event_id)
401
+ }
402
+
377
403
  /// Create an instance of an update directly from events. If the passed in event iterator has a
378
404
  /// partial WFT sequence at the end, all events after the last complete WFT sequence (ending
379
405
  /// with WFT started) are returned back to the caller, since the history update only works in
@@ -381,6 +407,7 @@ impl HistoryUpdate {
381
407
  pub fn from_events<I: IntoIterator<Item = HistoryEvent>>(
382
408
  events: I,
383
409
  previous_wft_started_id: i64,
410
+ wft_started_id: i64,
384
411
  has_last_wft: bool,
385
412
  ) -> (Self, Vec<HistoryEvent>)
386
413
  where
@@ -395,6 +422,7 @@ impl HistoryUpdate {
395
422
  Self {
396
423
  events: all_events,
397
424
  previous_wft_started_id,
425
+ wft_started_id,
398
426
  has_last_wft,
399
427
  },
400
428
  vec![],
@@ -404,6 +432,7 @@ impl HistoryUpdate {
404
432
  Self {
405
433
  events: vec![],
406
434
  previous_wft_started_id,
435
+ wft_started_id,
407
436
  has_last_wft,
408
437
  },
409
438
  all_events,
@@ -431,6 +460,7 @@ impl HistoryUpdate {
431
460
  Self {
432
461
  events: all_events,
433
462
  previous_wft_started_id,
463
+ wft_started_id,
434
464
  has_last_wft,
435
465
  },
436
466
  remaining_events,
@@ -444,6 +474,7 @@ impl HistoryUpdate {
444
474
  pub fn new_from_events<I: IntoIterator<Item = HistoryEvent>>(
445
475
  events: I,
446
476
  previous_wft_started_id: i64,
477
+ wft_started_id: i64,
447
478
  ) -> Self
448
479
  where
449
480
  <I as IntoIterator>::IntoIter: Send + 'static,
@@ -451,6 +482,7 @@ impl HistoryUpdate {
451
482
  Self {
452
483
  events: events.into_iter().collect(),
453
484
  previous_wft_started_id,
485
+ wft_started_id,
454
486
  has_last_wft: true,
455
487
  }
456
488
  }
@@ -474,7 +506,7 @@ impl HistoryUpdate {
474
506
  if siz == 0 {
475
507
  NextWFT::ReplayOver
476
508
  } else {
477
- NextWFT::WFT(self.events.drain(0..=siz).collect())
509
+ self.build_next_wft(siz)
478
510
  }
479
511
  } else {
480
512
  if siz != 0 {
@@ -485,12 +517,17 @@ impl HistoryUpdate {
485
517
  NextWFT::NeedFetch
486
518
  }
487
519
  }
488
- NextWFTSeqEndIndex::Complete(next_wft_ix) => {
489
- NextWFT::WFT(self.events.drain(0..=next_wft_ix).collect())
490
- }
520
+ NextWFTSeqEndIndex::Complete(next_wft_ix) => self.build_next_wft(next_wft_ix),
491
521
  }
492
522
  }
493
523
 
524
+ fn build_next_wft(&mut self, drain_this_much: usize) -> NextWFT {
525
+ NextWFT::WFT(
526
+ self.events.drain(0..=drain_this_much).collect(),
527
+ self.events.is_empty() && self.has_last_wft,
528
+ )
529
+ }
530
+
494
531
  /// Lets the caller peek ahead at the next WFT sequence that will be returned by
495
532
  /// [take_next_wft_sequence]. Will always return the first available WFT sequence if that has
496
533
  /// not been called first. May also return an empty iterator or incomplete sequence if we are at
@@ -519,6 +556,23 @@ impl HistoryUpdate {
519
556
  true
520
557
  }
521
558
 
559
+ /// Returns the next WFT completed event attributes, if any, starting at (inclusive) the
560
+ /// `from_id`
561
+ pub fn peek_next_wft_completed(
562
+ &self,
563
+ from_id: i64,
564
+ ) -> Option<&WorkflowTaskCompletedEventAttributes> {
565
+ self.events
566
+ .iter()
567
+ .skip_while(|e| e.event_id < from_id)
568
+ .find_map(|e| match &e.attributes {
569
+ Some(history_event::Attributes::WorkflowTaskCompletedEventAttributes(ref a)) => {
570
+ Some(a)
571
+ }
572
+ _ => None,
573
+ })
574
+ }
575
+
522
576
  fn starting_index_after_skipping(&self, from_wft_started_id: i64) -> Option<usize> {
523
577
  self.events
524
578
  .iter()
@@ -632,7 +686,11 @@ pub mod tests {
632
686
 
633
687
  impl From<HistoryInfo> for HistoryUpdate {
634
688
  fn from(v: HistoryInfo) -> Self {
635
- Self::new_from_events(v.events().to_vec(), v.previous_started_event_id())
689
+ Self::new_from_events(
690
+ v.events().to_vec(),
691
+ v.previous_started_event_id(),
692
+ v.workflow_task_started_event_id(),
693
+ )
636
694
  }
637
695
  }
638
696
 
@@ -649,7 +707,7 @@ pub mod tests {
649
707
  impl NextWFT {
650
708
  fn unwrap_events(self) -> Vec<HistoryEvent> {
651
709
  match self {
652
- NextWFT::WFT(e) => e,
710
+ NextWFT::WFT(e, _) => e,
653
711
  o => panic!("Must be complete WFT: {o:?}"),
654
712
  }
655
713
  }
@@ -726,12 +784,12 @@ pub mod tests {
726
784
  t.add_by_type(EventType::WorkflowExecutionStarted);
727
785
  t.add_full_wf_task();
728
786
  t.add_full_wf_task(); // wft started 6
729
- t.add_get_event_id(EventType::TimerStarted, None);
787
+ t.add_by_type(EventType::TimerStarted);
730
788
  t.add_full_wf_task(); // wft started 10
731
789
  t.add_full_wf_task();
732
790
  t.add_full_wf_task();
733
791
  t.add_full_wf_task(); // wft started 19
734
- t.add_get_event_id(EventType::TimerStarted, None);
792
+ t.add_by_type(EventType::TimerStarted);
735
793
  t.add_full_wf_task(); // wft started 23
736
794
  t.add_we_signaled("whee", vec![]);
737
795
  t.add_full_wf_task();
@@ -768,7 +826,9 @@ pub mod tests {
768
826
  }
769
827
 
770
828
  fn paginator_setup(history: TestHistoryBuilder, chunk_size: usize) -> HistoryPaginator {
771
- let full_hist = history.get_full_history_info().unwrap().into_events();
829
+ let hinfo = history.get_full_history_info().unwrap();
830
+ let wft_started = hinfo.workflow_task_started_event_id();
831
+ let full_hist = hinfo.into_events();
772
832
  let initial_hist = full_hist.chunks(chunk_size).next().unwrap().to_vec();
773
833
  let mut mock_client = mock_workflow_client();
774
834
 
@@ -800,6 +860,7 @@ pub mod tests {
800
860
  events: initial_hist,
801
861
  },
802
862
  0,
863
+ wft_started,
803
864
  "wfid".to_string(),
804
865
  "runid".to_string(),
805
866
  vec![1],
@@ -825,7 +886,7 @@ pub mod tests {
825
886
  for i in 1..wft_count {
826
887
  let seq = {
827
888
  match update.take_next_wft_sequence(last_started_id) {
828
- NextWFT::WFT(seq) => seq,
889
+ NextWFT::WFT(seq, _) => seq,
829
890
  NextWFT::NeedFetch => {
830
891
  update = paginator.extract_next_update().await.unwrap();
831
892
  update
@@ -867,9 +928,9 @@ pub mod tests {
867
928
  // Start with two complete normal WFTs
868
929
  t.add_by_type(EventType::WorkflowExecutionStarted);
869
930
  t.add_full_wf_task(); // wft start - 3
870
- t.add_get_event_id(EventType::TimerStarted, None);
931
+ t.add_by_type(EventType::TimerStarted);
871
932
  t.add_full_wf_task(); // wft start - 7
872
- t.add_get_event_id(EventType::TimerStarted, None);
933
+ t.add_by_type(EventType::TimerStarted);
873
934
  t.add_full_wf_task(); // wft start - 11
874
935
  for _ in 1..50 {
875
936
  // Add a bunch of heartbeats with no commands, which count as one task
@@ -887,7 +948,14 @@ pub mod tests {
887
948
  // The update should contain the first two complete WFTs, ending on the 8th event which
888
949
  // is WFT started. The remaining events should be returned. False flags means the creator
889
950
  // knows there are more events, so we should return need fetch
890
- let (mut update, remaining) = HistoryUpdate::from_events(ends_in_middle_of_seq, 0, false);
951
+ let (mut update, remaining) = HistoryUpdate::from_events(
952
+ ends_in_middle_of_seq,
953
+ 0,
954
+ t.get_full_history_info()
955
+ .unwrap()
956
+ .workflow_task_started_event_id(),
957
+ false,
958
+ );
891
959
  assert_eq!(remaining[0].event_id, 8);
892
960
  assert_eq!(remaining.last().unwrap().event_id, 19);
893
961
  let seq = update.take_next_wft_sequence(0).unwrap_events();
@@ -906,7 +974,14 @@ pub mod tests {
906
974
  let t = three_wfts_then_heartbeats();
907
975
  let mut ends_in_middle_of_seq = t.as_history_update().events;
908
976
  ends_in_middle_of_seq.truncate(20);
909
- let (mut update, remaining) = HistoryUpdate::from_events(ends_in_middle_of_seq, 0, false);
977
+ let (mut update, remaining) = HistoryUpdate::from_events(
978
+ ends_in_middle_of_seq,
979
+ 0,
980
+ t.get_full_history_info()
981
+ .unwrap()
982
+ .workflow_task_started_event_id(),
983
+ false,
984
+ );
910
985
  assert!(remaining.is_empty());
911
986
  let seq = update.take_next_wft_sequence(0).unwrap_events();
912
987
  assert_eq!(seq.last().unwrap().event_id, 3);
@@ -930,7 +1005,7 @@ pub mod tests {
930
1005
  loop {
931
1006
  let seq = update.take_next_wft_sequence(last_id);
932
1007
  match seq {
933
- NextWFT::WFT(seq) => {
1008
+ NextWFT::WFT(seq, _) => {
934
1009
  last_id = seq.last().unwrap().event_id;
935
1010
  }
936
1011
  NextWFT::NeedFetch => {
@@ -965,6 +1040,7 @@ pub mod tests {
965
1040
  let timer_hist = canned_histories::single_timer("t");
966
1041
  let partial_task = timer_hist.get_one_wft(2).unwrap();
967
1042
  let prev_started_wft_id = partial_task.previous_started_event_id();
1043
+ let wft_started_id = partial_task.workflow_task_started_event_id();
968
1044
  let mut history_from_get: GetWorkflowExecutionHistoryResponse =
969
1045
  timer_hist.get_history_info(2).unwrap().into();
970
1046
  // Chop off the last event, which is WFT started, which server doesn't return in get
@@ -978,6 +1054,7 @@ pub mod tests {
978
1054
  let mut paginator = HistoryPaginator::new(
979
1055
  partial_task.into(),
980
1056
  prev_started_wft_id,
1057
+ wft_started_id,
981
1058
  "wfid".to_string(),
982
1059
  "runid".to_string(),
983
1060
  // A cache miss means we'll try to fetch from start
@@ -1026,6 +1103,7 @@ pub mod tests {
1026
1103
  let timer_hist = canned_histories::single_timer("t");
1027
1104
  let partial_task = timer_hist.get_one_wft(2).unwrap();
1028
1105
  let prev_started_wft_id = partial_task.previous_started_event_id();
1106
+ let wft_started_id = partial_task.workflow_task_started_event_id();
1029
1107
  let mut mock_client = mock_workflow_client();
1030
1108
  mock_client
1031
1109
  .expect_get_workflow_execution_history()
@@ -1034,6 +1112,7 @@ pub mod tests {
1034
1112
  let mut paginator = HistoryPaginator::new(
1035
1113
  partial_task.into(),
1036
1114
  prev_started_wft_id,
1115
+ wft_started_id,
1037
1116
  "wfid".to_string(),
1038
1117
  "runid".to_string(),
1039
1118
  // A cache miss means we'll try to fetch from start
@@ -1041,7 +1120,7 @@ pub mod tests {
1041
1120
  Arc::new(mock_client),
1042
1121
  );
1043
1122
  let err = paginator.extract_next_update().await.unwrap_err();
1044
- assert_matches!(err.code(), tonic::Code::DataLoss);
1123
+ assert_matches!(err.code(), tonic::Code::Unknown);
1045
1124
  }
1046
1125
 
1047
1126
  #[tokio::test]
@@ -1049,6 +1128,7 @@ pub mod tests {
1049
1128
  let timer_hist = canned_histories::single_timer("t");
1050
1129
  let partial_task = timer_hist.get_one_wft(2).unwrap();
1051
1130
  let prev_started_wft_id = partial_task.previous_started_event_id();
1131
+ let wft_started_id = partial_task.workflow_task_started_event_id();
1052
1132
  let full_resp: GetWorkflowExecutionHistoryResponse =
1053
1133
  timer_hist.get_full_history_info().unwrap().into();
1054
1134
  let mut mock_client = mock_workflow_client();
@@ -1071,6 +1151,7 @@ pub mod tests {
1071
1151
  let mut paginator = HistoryPaginator::new(
1072
1152
  partial_task.into(),
1073
1153
  prev_started_wft_id,
1154
+ wft_started_id,
1074
1155
  "wfid".to_string(),
1075
1156
  "runid".to_string(),
1076
1157
  // A cache miss means we'll try to fetch from start