@temporalio/core-bridge 1.10.3 → 1.11.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 (126) hide show
  1. package/Cargo.lock +563 -676
  2. package/Cargo.toml +3 -3
  3. package/lib/index.d.ts +16 -5
  4. package/lib/index.js.map +1 -1
  5. package/lib/worker-tuner.d.ts +57 -0
  6. package/lib/worker-tuner.js +3 -0
  7. package/lib/worker-tuner.js.map +1 -0
  8. package/package.json +3 -3
  9. package/releases/aarch64-apple-darwin/index.node +0 -0
  10. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  11. package/releases/x86_64-apple-darwin/index.node +0 -0
  12. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  13. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  14. package/sdk-core/.github/workflows/heavy.yml +6 -11
  15. package/sdk-core/.github/workflows/per-pr.yml +23 -41
  16. package/sdk-core/Cargo.toml +5 -5
  17. package/sdk-core/README.md +2 -0
  18. package/sdk-core/client/Cargo.toml +4 -2
  19. package/sdk-core/client/src/lib.rs +60 -17
  20. package/sdk-core/client/src/metrics.rs +1 -1
  21. package/sdk-core/client/src/proxy.rs +17 -12
  22. package/sdk-core/client/src/raw.rs +218 -69
  23. package/sdk-core/client/src/retry.rs +19 -9
  24. package/sdk-core/core/Cargo.toml +12 -12
  25. package/sdk-core/core/src/abstractions.rs +3 -3
  26. package/sdk-core/core/src/core_tests/activity_tasks.rs +2 -1
  27. package/sdk-core/core/src/core_tests/determinism.rs +1 -1
  28. package/sdk-core/core/src/core_tests/local_activities.rs +73 -10
  29. package/sdk-core/core/src/core_tests/queries.rs +2 -1
  30. package/sdk-core/core/src/core_tests/updates.rs +162 -4
  31. package/sdk-core/core/src/core_tests/workers.rs +38 -2
  32. package/sdk-core/core/src/core_tests/workflow_tasks.rs +158 -27
  33. package/sdk-core/core/src/internal_flags.rs +17 -7
  34. package/sdk-core/core/src/lib.rs +9 -3
  35. package/sdk-core/core/src/pollers/poll_buffer.rs +1 -10
  36. package/sdk-core/core/src/protosext/mod.rs +0 -1
  37. package/sdk-core/core/src/protosext/protocol_messages.rs +105 -16
  38. package/sdk-core/core/src/retry_logic.rs +22 -2
  39. package/sdk-core/core/src/telemetry/otel.rs +44 -12
  40. package/sdk-core/core/src/test_help/mod.rs +65 -12
  41. package/sdk-core/core/src/worker/activities/local_activities.rs +1 -4
  42. package/sdk-core/core/src/worker/activities.rs +3 -4
  43. package/sdk-core/core/src/worker/client/mocks.rs +7 -6
  44. package/sdk-core/core/src/worker/client.rs +11 -2
  45. package/sdk-core/core/src/worker/mod.rs +49 -24
  46. package/sdk-core/core/src/worker/tuner/resource_based.rs +48 -48
  47. package/sdk-core/core/src/worker/tuner.rs +124 -4
  48. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +1 -1
  49. package/sdk-core/core/src/worker/workflow/history_update.rs +11 -2
  50. package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +18 -3
  51. package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +1 -0
  52. package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +1 -0
  53. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +1 -0
  54. package/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +1 -0
  55. package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +1 -0
  56. package/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +1 -0
  57. package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +4 -4
  58. package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +1 -0
  59. package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +1 -0
  60. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +1 -0
  61. package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +3 -1
  62. package/sdk-core/core/src/worker/workflow/machines/update_state_machine.rs +38 -28
  63. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +4 -2
  64. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +95 -71
  65. package/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +1 -1
  66. package/sdk-core/core/src/worker/workflow/managed_run.rs +214 -14
  67. package/sdk-core/core/src/worker/workflow/mod.rs +49 -36
  68. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +1 -2
  69. package/sdk-core/core-api/src/errors.rs +13 -7
  70. package/sdk-core/core-api/src/lib.rs +9 -1
  71. package/sdk-core/sdk/Cargo.toml +1 -1
  72. package/sdk-core/sdk/src/activity_context.rs +3 -4
  73. package/sdk-core/sdk/src/lib.rs +96 -49
  74. package/sdk-core/sdk/src/workflow_context/options.rs +8 -4
  75. package/sdk-core/sdk/src/workflow_context.rs +53 -49
  76. package/sdk-core/sdk/src/workflow_future.rs +10 -4
  77. package/sdk-core/sdk-core-protos/Cargo.toml +4 -3
  78. package/sdk-core/sdk-core-protos/build.rs +2 -0
  79. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/.github/workflows/build.yaml +18 -0
  80. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/LICENSE +21 -0
  81. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/Makefile +59 -0
  82. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/README.md +25 -0
  83. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/VERSION +1 -0
  84. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/buf.gen.yaml +14 -0
  85. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/buf.lock +8 -0
  86. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/buf.yaml +9 -0
  87. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/cloudservice/v1/request_response.proto +520 -0
  88. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/cloudservice/v1/service.proto +263 -0
  89. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/identity/v1/message.proto +173 -0
  90. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/namespace/v1/message.proto +164 -0
  91. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/operation/v1/message.proto +36 -0
  92. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/region/v1/message.proto +22 -0
  93. package/sdk-core/sdk-core-protos/protos/api_upstream/.github/workflows/trigger-api-go-update.yml +50 -8
  94. package/sdk-core/sdk-core-protos/protos/api_upstream/.gitmodules +3 -0
  95. package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv2.json +132 -54
  96. package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv3.yaml +177 -81
  97. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/command/v1/message.proto +13 -0
  98. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +2 -0
  99. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +8 -3
  100. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/history/v1/message.proto +10 -0
  101. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/nexus/v1/message.proto +3 -3
  102. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +6 -6
  103. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/enhanced_stack_trace.proto +96 -0
  104. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/user_metadata.proto +49 -0
  105. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/workflow_metadata.proto +6 -7
  106. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +55 -24
  107. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflow/v1/message.proto +7 -0
  108. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +21 -4
  109. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +64 -45
  110. package/sdk-core/sdk-core-protos/src/history_builder.rs +8 -1
  111. package/sdk-core/sdk-core-protos/src/lib.rs +40 -10
  112. package/sdk-core/test-utils/src/canned_histories.rs +1 -1
  113. package/sdk-core/tests/fuzzy_workflow.rs +4 -2
  114. package/sdk-core/tests/heavy_tests.rs +3 -3
  115. package/sdk-core/tests/integ_tests/activity_functions.rs +2 -2
  116. package/sdk-core/tests/integ_tests/client_tests.rs +234 -6
  117. package/sdk-core/tests/integ_tests/update_tests.rs +180 -47
  118. package/sdk-core/tests/integ_tests/worker_tests.rs +32 -0
  119. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +47 -3
  120. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +10 -10
  121. package/sdk-core/tests/main.rs +1 -0
  122. package/src/conversions.rs +84 -11
  123. package/src/runtime.rs +5 -17
  124. package/src/worker.rs +27 -6
  125. package/ts/index.ts +24 -5
  126. package/ts/worker-tuner.ts +71 -0
@@ -6,7 +6,7 @@ use crate::{
6
6
  use itertools::Itertools;
7
7
  use prost::EncodeError;
8
8
  use rustfsm::{fsm, MachineError, StateMachine, TransitionResult};
9
- use std::{convert::TryFrom, mem};
9
+ use std::convert::TryFrom;
10
10
  use temporal_sdk_core_protos::{
11
11
  coresdk::{
12
12
  workflow_activation::DoUpdate,
@@ -18,6 +18,7 @@ use temporal_sdk_core_protos::{
18
18
  enums::v1::{CommandType, EventType},
19
19
  failure::v1::Failure,
20
20
  protocol::v1::Message as ProtocolMessage,
21
+ update,
21
22
  update::v1::{outcome, Acceptance, Outcome, Rejection, Response},
22
23
  },
23
24
  utilities::pack_any,
@@ -59,9 +60,9 @@ fsm! {
59
60
  #[derive(Debug, derive_more::Display)]
60
61
  pub(super) enum UpdateMachineCommand {
61
62
  #[display(fmt = "Accept")]
62
- Accept,
63
+ Accept(update::v1::Request),
63
64
  #[display(fmt = "Reject")]
64
- Reject(Failure),
65
+ Reject(update::v1::Request, Failure),
65
66
  #[display(fmt = "Complete")]
66
67
  Complete(Payload),
67
68
  #[display(fmt = "Fail")]
@@ -73,7 +74,7 @@ pub(super) struct SharedState {
73
74
  message_id: String,
74
75
  instance_id: String,
75
76
  event_seq_id: i64,
76
- request: UpdateRequest,
77
+ meta: update::v1::Meta,
77
78
  }
78
79
 
79
80
  impl UpdateMachine {
@@ -81,27 +82,31 @@ impl UpdateMachine {
81
82
  message_id: String,
82
83
  instance_id: String,
83
84
  event_seq_id: i64,
84
- mut request: UpdateRequest,
85
+ request: UpdateRequest,
85
86
  replaying: bool,
86
87
  ) -> NewMachineWithResponse {
88
+ let meta = request.meta().clone();
89
+ let do_update = DoUpdate {
90
+ id: meta.update_id.clone(),
91
+ protocol_instance_id: instance_id.clone(),
92
+ name: request.name().to_string(),
93
+ input: request.input(),
94
+ headers: request.headers(),
95
+ meta: Some(meta.clone()),
96
+ run_validator: !replaying,
97
+ };
87
98
  let me = Self::from_parts(
88
- RequestInitiated {}.into(),
99
+ RequestInitiated {
100
+ original_request: request.original,
101
+ }
102
+ .into(),
89
103
  SharedState {
90
104
  message_id,
91
- instance_id: instance_id.clone(),
105
+ instance_id,
92
106
  event_seq_id,
93
- request: request.clone(),
107
+ meta,
94
108
  },
95
109
  );
96
- let do_update = DoUpdate {
97
- id: mem::replace(&mut request.meta.update_id, "".to_string()),
98
- protocol_instance_id: instance_id,
99
- name: request.name,
100
- input: request.input,
101
- headers: request.headers,
102
- meta: Some(request.meta),
103
- run_validator: !replaying,
104
- };
105
110
  NewMachineWithResponse {
106
111
  machine: me.into(),
107
112
  response: MachineResponse::PushWFJob(do_update.into()),
@@ -116,7 +121,7 @@ impl UpdateMachine {
116
121
  None => {
117
122
  return Err(WFMachinesError::Fatal(format!(
118
123
  "Update response for update {} had an empty result, this is a lang layer bug.",
119
- &self.shared_state.request.meta.update_id
124
+ &self.shared_state.meta.update_id
120
125
  )))
121
126
  }
122
127
  Some(update_response::Response::Accepted(_)) => {
@@ -132,7 +137,7 @@ impl UpdateMachine {
132
137
  .map_err(|e| match e {
133
138
  MachineError::InvalidTransition => WFMachinesError::Nondeterminism(format!(
134
139
  "Invalid transition while handling update response (id {}) in state {}",
135
- &self.shared_state.request.meta.update_id,
140
+ &self.shared_state.meta.update_id,
136
141
  self.state(),
137
142
  )),
138
143
  MachineError::Underlying(e) => e,
@@ -231,29 +236,29 @@ impl WFMachinesAdapter for UpdateMachine {
231
236
  _event_info: Option<EventInfo>,
232
237
  ) -> Result<Vec<MachineResponse>, WFMachinesError> {
233
238
  Ok(match my_command {
234
- UpdateMachineCommand::Accept => self.build_command_msg(
239
+ UpdateMachineCommand::Accept(orig) => self.build_command_msg(
235
240
  format!("{}/accept", self.shared_state.message_id),
236
241
  UpdateMsg::Accept(Acceptance {
237
242
  accepted_request_message_id: self.shared_state.message_id.clone(),
238
243
  accepted_request_sequencing_event_id: self.shared_state.event_seq_id,
239
- ..Default::default()
244
+ accepted_request: Some(orig),
240
245
  }),
241
246
  )?,
242
- UpdateMachineCommand::Reject(fail) => {
247
+ UpdateMachineCommand::Reject(orig, fail) => {
243
248
  vec![self.build_msg(
244
249
  format!("{}/reject", self.shared_state.message_id),
245
250
  UpdateMsg::Reject(Rejection {
246
251
  rejected_request_message_id: self.shared_state.message_id.clone(),
247
252
  rejected_request_sequencing_event_id: self.shared_state.event_seq_id,
253
+ rejected_request: Some(orig),
248
254
  failure: Some(fail),
249
- ..Default::default()
250
255
  }),
251
256
  )?]
252
257
  }
253
258
  UpdateMachineCommand::Complete(p) => self.build_command_msg(
254
259
  format!("{}/complete", self.shared_state.message_id),
255
260
  UpdateMsg::Response(Response {
256
- meta: Some(self.shared_state.request.meta.clone()),
261
+ meta: Some(self.shared_state.meta.clone()),
257
262
  outcome: Some(Outcome {
258
263
  value: Some(outcome::Value::Success(p.into())),
259
264
  }),
@@ -262,7 +267,7 @@ impl WFMachinesAdapter for UpdateMachine {
262
267
  UpdateMachineCommand::Fail(f) => self.build_command_msg(
263
268
  format!("{}/complete", self.shared_state.message_id),
264
269
  UpdateMsg::Response(Response {
265
- meta: Some(self.shared_state.request.meta.clone()),
270
+ meta: Some(self.shared_state.meta.clone()),
266
271
  outcome: Some(Outcome {
267
272
  value: Some(outcome::Value::Failure(f)),
268
273
  }),
@@ -284,13 +289,18 @@ impl TryFrom<CommandType> for UpdateMachineEvents {
284
289
  }
285
290
 
286
291
  #[derive(Default, Clone)]
287
- pub(super) struct RequestInitiated {}
292
+ pub(super) struct RequestInitiated {
293
+ original_request: update::v1::Request,
294
+ }
288
295
  impl RequestInitiated {
289
296
  fn on_accept(self) -> UpdateMachineTransition<Accepted> {
290
- UpdateMachineTransition::commands([UpdateMachineCommand::Accept])
297
+ UpdateMachineTransition::commands([UpdateMachineCommand::Accept(self.original_request)])
291
298
  }
292
299
  fn on_reject(self, fail: Failure) -> UpdateMachineTransition<Rejected> {
293
- UpdateMachineTransition::commands([UpdateMachineCommand::Reject(fail)])
300
+ UpdateMachineTransition::commands([UpdateMachineCommand::Reject(
301
+ self.original_request,
302
+ fail,
303
+ )])
294
304
  }
295
305
  }
296
306
 
@@ -86,6 +86,7 @@ fn create_new(sa_map: SearchAttributes) -> NewMachineWithCommand {
86
86
  },
87
87
  ),
88
88
  ),
89
+ user_metadata: Default::default(),
89
90
  };
90
91
  NewMachineWithCommand {
91
92
  command: cmd,
@@ -339,7 +340,7 @@ mod tests {
339
340
  );
340
341
  // Ensure the upsert command has an empty map when not using the patched command
341
342
  if !with_patched_cmd {
342
- mp.completion_asserts = Some(Box::new(|wftc| {
343
+ mp.completion_mock_fn = Some(Box::new(|wftc| {
343
344
  let cmd_attrs = wftc
344
345
  .commands
345
346
  .first()
@@ -349,11 +350,12 @@ mod tests {
349
350
  cmd_attrs,
350
351
  Attributes::CompleteWorkflowExecutionCommandAttributes(_)
351
352
  ) {
352
- return;
353
+ return Ok(Default::default());
353
354
  }
354
355
  assert_matches!(cmd_attrs,
355
356
  Attributes::UpsertWorkflowSearchAttributesCommandAttributes(attrs)
356
357
  if attrs.search_attributes.clone().unwrap_or_default().indexed_fields.is_empty());
358
+ Ok(Default::default())
357
359
  }));
358
360
  }
359
361
  let mut mock = build_mock_pollers(mp);
@@ -14,7 +14,7 @@ use super::{
14
14
  };
15
15
  use crate::{
16
16
  abstractions::dbg_panic,
17
- internal_flags::InternalFlags,
17
+ internal_flags::{CoreInternalFlags, InternalFlags},
18
18
  protosext::{
19
19
  protocol_messages::{IncomingProtocolMessage, IncomingProtocolMessageBody},
20
20
  CompleteLocalActivityData, HistoryEventExt, ValidScheduleLA,
@@ -37,6 +37,7 @@ use crate::{
37
37
  ExecutingLAId, LocalActRequest, LocalActivityExecutionResult, LocalActivityResolution,
38
38
  },
39
39
  };
40
+ use anyhow::Context;
40
41
  use siphasher::sip::SipHasher13;
41
42
  use slotmap::{SlotMap, SparseSecondaryMap};
42
43
  use std::{
@@ -238,7 +239,7 @@ impl WorkflowMachines {
238
239
  pub(crate) fn new(basics: RunBasics, driven_wf: DrivenWorkflow) -> Self {
239
240
  let replaying = basics.history.previous_wft_started_id > 0;
240
241
  let mut observed_internal_flags = InternalFlags::new(basics.capabilities);
241
- // Peek ahead to determine used patches in the first WFT.
242
+ // Peek ahead to determine used flags in the first WFT.
242
243
  if let Some(attrs) = basics.history.peek_next_wft_completed(0) {
243
244
  observed_internal_flags.add_from_complete(attrs);
244
245
  };
@@ -469,22 +470,30 @@ impl WorkflowMachines {
469
470
  .add_lang_used(flags);
470
471
  }
471
472
 
473
+ pub(crate) fn try_use_flag(&self, flag: CoreInternalFlags, should_record: bool) -> bool {
474
+ self.observed_internal_flags
475
+ .borrow_mut()
476
+ .try_use(flag, should_record)
477
+ }
478
+
472
479
  /// Undo a speculative workflow task by resetting to a certain WFT Started ID. This can happen
473
480
  /// when an update request is rejected.
474
481
  pub(crate) fn reset_last_started_id(&mut self, id: i64) {
475
482
  debug!("Resetting back to event id {} due to speculative WFT", id);
476
483
  self.current_started_event_id = id;
477
- // This is pretty nasty to just + 1 like this, but, we know WFT complete always follows
478
- // WFT started, which the id given to us to reset to must be, and we need to avoid
479
- // re-applying the WFT Completed event, so we make sure to consider that processed
480
- self.last_processed_event = id + 1;
484
+ // We must reset the last event we "processed" to be after the last WFT we really completed
485
+ // + any command events (since the SDK "processed" those when it emitted the commands). This
486
+ // is also equal to what we just processed in the speculative task, minus two, since we
487
+ // would've just handled the most recent WFT started event, and we need to drop that & the
488
+ // schedule event just before it.
489
+ self.last_processed_event -= 2;
481
490
  // Then, we have to drop any state machines (which should only be one workflow task machine)
482
491
  // we may have created when servicing the speculative task.
483
492
  // Remove when https://github.com/rust-lang/rust/issues/59618 is stable
484
493
  let remove_these: Vec<_> = self
485
494
  .machines_by_event_id
486
495
  .iter()
487
- .filter(|(mid, _)| **mid > id)
496
+ .filter(|(mid, _)| **mid > self.last_processed_event)
488
497
  .map(|(mid, mkey)| (*mid, *mkey))
489
498
  .collect();
490
499
  for (mid, mkey) in remove_these {
@@ -526,43 +535,6 @@ impl WorkflowMachines {
526
535
  return Ok(0);
527
536
  }
528
537
 
529
- fn get_processable_messages(
530
- me: &mut WorkflowMachines,
531
- for_event_id: i64,
532
- ) -> Vec<IncomingProtocolMessage> {
533
- // Another thing to replace when `drain_filter` exists
534
- let mut ret = vec![];
535
- me.protocol_msgs = std::mem::take(&mut me.protocol_msgs)
536
- .into_iter()
537
- .filter_map(|x| {
538
- if x.processable_after_event_id()
539
- .is_some_and(|eid| eid <= for_event_id)
540
- {
541
- ret.push(x);
542
- None
543
- } else {
544
- Some(x)
545
- }
546
- })
547
- .collect();
548
- ret
549
- }
550
-
551
- // Peek to the next WFT complete and update ourselves with data we might need in it.
552
- if let Some(next_complete) = self
553
- .last_history_from_server
554
- .peek_next_wft_completed(self.last_processed_event)
555
- {
556
- // We update the internal flags before applying the current task
557
- (*self.observed_internal_flags)
558
- .borrow_mut()
559
- .add_from_complete(next_complete);
560
- // Save this tasks' Build ID if it had one
561
- if let Some(bid) = next_complete.worker_version.as_ref().map(|wv| &wv.build_id) {
562
- self.current_wft_build_id = Some(bid.to_string());
563
- }
564
- }
565
-
566
538
  let last_handled_wft_started_id = self.current_started_event_id;
567
539
  let (events, has_final_event) = match self
568
540
  .last_history_from_server
@@ -584,7 +556,37 @@ impl WorkflowMachines {
584
556
  };
585
557
  let num_events_to_process = events.len();
586
558
 
587
- // We're caught up on reply if there are no new events to process
559
+ // Process any WFT completed events in the next sequence, as well as peek ahead to the
560
+ // subsequent one to properly apply flags & any other data. Macro used to avoid self
561
+ // double-borrow.
562
+ macro_rules! apply_wft_complete_data {
563
+ ($me:expr, $wtc:expr) => {{
564
+ (*$me.observed_internal_flags)
565
+ .borrow_mut()
566
+ .add_from_complete($wtc);
567
+ if let Some(bid) = $wtc.worker_version.as_ref().map(|wv| &wv.build_id) {
568
+ $me.current_wft_build_id = Some(bid.to_string());
569
+ }
570
+ }};
571
+ }
572
+ let mut peeked_events = events.iter().peekable();
573
+ while let Some(event) = peeked_events.next() {
574
+ if let Some(history_event::Attributes::WorkflowTaskCompletedEventAttributes(ref wtc)) =
575
+ event.attributes
576
+ {
577
+ apply_wft_complete_data!(self, wtc);
578
+ }
579
+ if peeked_events.peek().is_none() {
580
+ if let Some(wtc) = self
581
+ .last_history_from_server
582
+ .peek_next_wft_completed(event.event_id)
583
+ {
584
+ apply_wft_complete_data!(self, wtc);
585
+ }
586
+ }
587
+ }
588
+
589
+ // We're caught up on replay if there are no new events to process
588
590
  if events.is_empty() {
589
591
  self.replaying = false;
590
592
  }
@@ -596,6 +598,7 @@ impl WorkflowMachines {
596
598
  }
597
599
  }
598
600
 
601
+ let mut update_admitted_event_messages = HashMap::<String, IncomingProtocolMessage>::new();
599
602
  let mut do_handle_event = true;
600
603
  let mut history = events.into_iter().peekable();
601
604
  while let Some(event) = history.next() {
@@ -628,8 +631,42 @@ impl WorkflowMachines {
628
631
  self.replaying = false;
629
632
  }
630
633
 
634
+ if matches!(
635
+ event.attributes,
636
+ Some(history_event::Attributes::WorkflowExecutionUpdateAdmittedEventAttributes(_)),
637
+ ) {
638
+ // The server has sent a durable update admitted event: create the message that would have been sent
639
+ // for a non-durable update request message.
640
+ let msg = IncomingProtocolMessage::try_from(&event).context(
641
+ "Failed to create protocol message from WorkflowExecutionUpdateAdmittedEvent",
642
+ )?;
643
+ if self.replaying {
644
+ // Stash the message for use if the update request is accepted.
645
+ update_admitted_event_messages.insert(msg.protocol_instance_id.clone(), msg);
646
+ } else {
647
+ // Use the message now.
648
+ self.protocol_msgs.push(msg);
649
+ }
650
+ do_handle_event = false;
651
+ }
652
+
631
653
  // Process any messages that should be processed before the event we're about to handle
632
- let processable_msgs = get_processable_messages(self, eid - 1);
654
+ let for_event_id = eid - 1;
655
+ // Another thing to replace when `drain_filter` exists
656
+ let mut processable_msgs = vec![];
657
+ self.protocol_msgs = std::mem::take(&mut self.protocol_msgs)
658
+ .into_iter()
659
+ .filter_map(|x| {
660
+ if x.processable_after_event_id()
661
+ .is_some_and(|eid| eid <= for_event_id)
662
+ {
663
+ processable_msgs.push(x);
664
+ None
665
+ } else {
666
+ Some(x)
667
+ }
668
+ })
669
+ .collect();
633
670
  for msg in processable_msgs {
634
671
  self.handle_protocol_message(msg)?;
635
672
  }
@@ -699,26 +736,16 @@ impl WorkflowMachines {
699
736
  history_event::Attributes::WorkflowExecutionUpdateAcceptedEventAttributes(ref atts),
700
737
  ) = e.attributes
701
738
  {
702
- // If we see a workflow update accepted event, initialize the machine for it by
703
- // pretending we received the message we would've under not-replay.
704
- delayed_actions.push(DelayedAction::ProtocolMessage(IncomingProtocolMessage {
705
- id: atts.accepted_request_message_id.clone(),
706
- protocol_instance_id: atts.protocol_instance_id.clone(),
707
- sequencing_id: Some(SequencingId::EventId(
708
- atts.accepted_request_sequencing_event_id,
709
- )),
710
- body: IncomingProtocolMessageBody::UpdateRequest(
711
- atts.accepted_request
712
- .clone()
713
- .ok_or_else(|| {
714
- WFMachinesError::Fatal(
715
- "Update accepted event must contain accepted request"
716
- .to_string(),
717
- )
718
- })?
719
- .try_into()?,
720
- ),
721
- }));
739
+ // We've encountered an UpdateAccepted event during replay: pretend that we received the message we
740
+ // would have when receiving an update request under not-replay. If this event was preceded by an
741
+ // UpdateAdmitted event, then use the message that we created when we encountered that.
742
+ delayed_actions.push(DelayedAction::ProtocolMessage(
743
+ update_admitted_event_messages
744
+ .remove(&atts.protocol_instance_id)
745
+ .map_or_else(|| e.try_into().context(
746
+ "Failed to create protocol message from WorkflowExecutionUpdateAcceptedEvent",
747
+ ), Ok)?,
748
+ ));
722
749
  }
723
750
  }
724
751
  for action in delayed_actions {
@@ -780,10 +807,7 @@ impl WorkflowMachines {
780
807
  Ok(EventHandlingOutcome::Normal)
781
808
  };
782
809
  }
783
- if event.event_type() == EventType::Unspecified
784
- || event.event_type() == EventType::WorkflowExecutionUpdateAdmitted
785
- || event.attributes.is_none()
786
- {
810
+ if event.event_type() == EventType::Unspecified || event.attributes.is_none() {
787
811
  return if !event.worker_may_ignore {
788
812
  Err(WFMachinesError::Fatal(format!(
789
813
  "Event type is unspecified! This history is invalid. Event detail: {event:?}"
@@ -910,7 +934,7 @@ impl WorkflowMachines {
910
934
  attrs,
911
935
  )) = event_dat.event.attributes
912
936
  {
913
- if let Some(st) = event_dat.event.event_time.clone() {
937
+ if let Some(st) = event_dat.event.event_time {
914
938
  let as_systime: SystemTime = st.try_into()?;
915
939
  self.workflow_start_time = Some(as_systime);
916
940
  // Set the workflow time to be the event time of the first event, so that
@@ -98,7 +98,7 @@ impl TryFrom<HistEventData> for WorkflowTaskMachineEvents {
98
98
  Ok(match e.event_type() {
99
99
  EventType::WorkflowTaskScheduled => Self::WorkflowTaskScheduled,
100
100
  EventType::WorkflowTaskStarted => Self::WorkflowTaskStarted({
101
- let time = if let Some(time) = e.event_time.clone() {
101
+ let time = if let Some(time) = e.event_time {
102
102
  match time.try_into() {
103
103
  Ok(t) => t,
104
104
  Err(_) => {