@temporalio/core-bridge 1.5.2 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/Cargo.lock +304 -112
  2. package/lib/index.d.ts +8 -6
  3. package/lib/index.js.map +1 -1
  4. package/package.json +9 -4
  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 +2 -4
  13. package/sdk-core/.cargo/config.toml +5 -2
  14. package/sdk-core/.github/workflows/heavy.yml +29 -0
  15. package/sdk-core/Cargo.toml +1 -1
  16. package/sdk-core/README.md +20 -10
  17. package/sdk-core/client/src/lib.rs +215 -39
  18. package/sdk-core/client/src/metrics.rs +17 -8
  19. package/sdk-core/client/src/raw.rs +4 -4
  20. package/sdk-core/client/src/retry.rs +32 -20
  21. package/sdk-core/core/Cargo.toml +25 -12
  22. package/sdk-core/core/src/abstractions/take_cell.rs +28 -0
  23. package/sdk-core/core/src/abstractions.rs +204 -14
  24. package/sdk-core/core/src/core_tests/activity_tasks.rs +143 -50
  25. package/sdk-core/core/src/core_tests/child_workflows.rs +6 -5
  26. package/sdk-core/core/src/core_tests/determinism.rs +165 -2
  27. package/sdk-core/core/src/core_tests/local_activities.rs +431 -43
  28. package/sdk-core/core/src/core_tests/queries.rs +34 -16
  29. package/sdk-core/core/src/core_tests/workers.rs +8 -5
  30. package/sdk-core/core/src/core_tests/workflow_tasks.rs +588 -55
  31. package/sdk-core/core/src/ephemeral_server/mod.rs +113 -12
  32. package/sdk-core/core/src/internal_flags.rs +155 -0
  33. package/sdk-core/core/src/lib.rs +16 -9
  34. package/sdk-core/core/src/protosext/mod.rs +1 -1
  35. package/sdk-core/core/src/replay/mod.rs +16 -27
  36. package/sdk-core/core/src/telemetry/log_export.rs +1 -1
  37. package/sdk-core/core/src/telemetry/metrics.rs +69 -35
  38. package/sdk-core/core/src/telemetry/mod.rs +60 -21
  39. package/sdk-core/core/src/telemetry/prometheus_server.rs +19 -13
  40. package/sdk-core/core/src/test_help/mod.rs +73 -14
  41. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +119 -160
  42. package/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +89 -0
  43. package/sdk-core/core/src/worker/activities/local_activities.rs +379 -129
  44. package/sdk-core/core/src/worker/activities.rs +350 -175
  45. package/sdk-core/core/src/worker/client/mocks.rs +22 -2
  46. package/sdk-core/core/src/worker/client.rs +18 -2
  47. package/sdk-core/core/src/worker/mod.rs +183 -64
  48. package/sdk-core/core/src/worker/workflow/bridge.rs +1 -3
  49. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -5
  50. package/sdk-core/core/src/worker/workflow/history_update.rs +916 -277
  51. package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +216 -183
  52. package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +9 -12
  53. package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +7 -9
  54. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +160 -87
  55. package/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +13 -14
  56. package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +7 -9
  57. package/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +14 -17
  58. package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +242 -110
  59. package/sdk-core/core/src/worker/workflow/machines/mod.rs +27 -19
  60. package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +9 -11
  61. package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +321 -206
  62. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +13 -18
  63. package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +20 -29
  64. package/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +2 -2
  65. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +257 -51
  66. package/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +6 -17
  67. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +310 -150
  68. package/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +17 -20
  69. package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +31 -15
  70. package/sdk-core/core/src/worker/workflow/managed_run.rs +1052 -380
  71. package/sdk-core/core/src/worker/workflow/mod.rs +598 -390
  72. package/sdk-core/core/src/worker/workflow/run_cache.rs +40 -57
  73. package/sdk-core/core/src/worker/workflow/wft_extraction.rs +137 -0
  74. package/sdk-core/core/src/worker/workflow/wft_poller.rs +1 -4
  75. package/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +117 -0
  76. package/sdk-core/core/src/worker/workflow/workflow_stream/tonic_status_serde.rs +24 -0
  77. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +469 -718
  78. package/sdk-core/core-api/Cargo.toml +2 -1
  79. package/sdk-core/core-api/src/errors.rs +1 -34
  80. package/sdk-core/core-api/src/lib.rs +19 -9
  81. package/sdk-core/core-api/src/telemetry.rs +4 -6
  82. package/sdk-core/core-api/src/worker.rs +19 -1
  83. package/sdk-core/etc/deps.svg +115 -140
  84. package/sdk-core/etc/regen-depgraph.sh +5 -0
  85. package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +86 -61
  86. package/sdk-core/fsm/rustfsm_trait/src/lib.rs +29 -71
  87. package/sdk-core/histories/ends_empty_wft_complete.bin +0 -0
  88. package/sdk-core/histories/evict_while_la_running_no_interference-16_history.bin +0 -0
  89. package/sdk-core/histories/old_change_marker_format.bin +0 -0
  90. package/sdk-core/protos/api_upstream/.github/CODEOWNERS +2 -1
  91. package/sdk-core/protos/api_upstream/Makefile +6 -6
  92. package/sdk-core/protos/api_upstream/build/go.mod +7 -0
  93. package/sdk-core/protos/api_upstream/build/go.sum +5 -0
  94. package/sdk-core/protos/api_upstream/build/tools.go +29 -0
  95. package/sdk-core/protos/api_upstream/go.mod +6 -0
  96. package/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +9 -2
  97. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +7 -26
  98. package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +13 -2
  99. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +3 -2
  100. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +3 -7
  101. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +3 -2
  102. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +8 -8
  103. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +25 -2
  104. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +2 -2
  105. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/query.proto +2 -2
  106. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +2 -2
  107. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/schedule.proto +2 -2
  108. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +2 -2
  109. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +24 -19
  110. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +2 -2
  111. package/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +2 -2
  112. package/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +2 -2
  113. package/sdk-core/protos/api_upstream/temporal/api/filter/v1/message.proto +2 -2
  114. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +49 -26
  115. package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +4 -2
  116. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +5 -2
  117. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +2 -2
  118. package/sdk-core/protos/api_upstream/temporal/api/protocol/v1/message.proto +57 -0
  119. package/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +2 -2
  120. package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +2 -2
  121. package/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +2 -2
  122. package/sdk-core/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +63 -0
  123. package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +2 -2
  124. package/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +71 -6
  125. package/sdk-core/protos/api_upstream/temporal/api/version/v1/message.proto +2 -2
  126. package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +2 -2
  127. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +64 -28
  128. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +4 -4
  129. package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +7 -8
  130. package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +10 -7
  131. package/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +19 -30
  132. package/sdk-core/protos/local/temporal/sdk/core/common/common.proto +1 -0
  133. package/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +1 -0
  134. package/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +8 -0
  135. package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +67 -60
  136. package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +85 -84
  137. package/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +9 -3
  138. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +2 -2
  139. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +2 -2
  140. package/sdk-core/sdk/Cargo.toml +5 -4
  141. package/sdk-core/sdk/src/lib.rs +108 -26
  142. package/sdk-core/sdk/src/workflow_context/options.rs +7 -1
  143. package/sdk-core/sdk/src/workflow_context.rs +24 -17
  144. package/sdk-core/sdk/src/workflow_future.rs +16 -15
  145. package/sdk-core/sdk-core-protos/Cargo.toml +5 -2
  146. package/sdk-core/sdk-core-protos/build.rs +36 -2
  147. package/sdk-core/sdk-core-protos/src/history_builder.rs +138 -106
  148. package/sdk-core/sdk-core-protos/src/history_info.rs +10 -1
  149. package/sdk-core/sdk-core-protos/src/lib.rs +272 -87
  150. package/sdk-core/sdk-core-protos/src/task_token.rs +12 -2
  151. package/sdk-core/test-utils/Cargo.toml +3 -1
  152. package/sdk-core/test-utils/src/canned_histories.rs +106 -296
  153. package/sdk-core/test-utils/src/histfetch.rs +1 -1
  154. package/sdk-core/test-utils/src/lib.rs +82 -23
  155. package/sdk-core/test-utils/src/wf_input_saver.rs +50 -0
  156. package/sdk-core/test-utils/src/workflows.rs +29 -0
  157. package/sdk-core/tests/fuzzy_workflow.rs +130 -0
  158. package/sdk-core/tests/{load_tests.rs → heavy_tests.rs} +125 -51
  159. package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +25 -3
  160. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +10 -5
  161. package/sdk-core/tests/integ_tests/metrics_tests.rs +218 -16
  162. package/sdk-core/tests/integ_tests/polling_tests.rs +4 -47
  163. package/sdk-core/tests/integ_tests/queries_tests.rs +5 -128
  164. package/sdk-core/tests/integ_tests/visibility_tests.rs +83 -25
  165. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +161 -72
  166. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -0
  167. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +6 -13
  168. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +80 -3
  169. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +6 -2
  170. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -10
  171. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +94 -200
  172. package/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +2 -4
  173. package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +34 -28
  174. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +76 -7
  175. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -0
  176. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +18 -14
  177. package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +6 -20
  178. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +10 -21
  179. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +7 -8
  180. package/sdk-core/tests/integ_tests/workflow_tests.rs +13 -14
  181. package/sdk-core/tests/main.rs +3 -13
  182. package/sdk-core/tests/runner.rs +75 -36
  183. package/sdk-core/tests/wf_input_replay.rs +32 -0
  184. package/src/conversions.rs +14 -8
  185. package/src/runtime.rs +9 -8
  186. package/ts/index.ts +8 -6
  187. package/sdk-core/bridge-ffi/Cargo.toml +0 -24
  188. package/sdk-core/bridge-ffi/LICENSE.txt +0 -23
  189. package/sdk-core/bridge-ffi/build.rs +0 -25
  190. package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +0 -224
  191. package/sdk-core/bridge-ffi/src/lib.rs +0 -746
  192. package/sdk-core/bridge-ffi/src/wrappers.rs +0 -221
  193. package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +0 -210
  194. package/sdk-core/sdk/src/conversions.rs +0 -8
@@ -1,11 +1,8 @@
1
1
  mod local_acts;
2
2
 
3
- pub(crate) use temporal_sdk_core_api::errors::WFMachinesError;
4
-
5
3
  use super::{
6
- activity_state_machine::new_activity, cancel_external_state_machine::new_external_cancel,
4
+ cancel_external_state_machine::new_external_cancel,
7
5
  cancel_workflow_state_machine::cancel_workflow,
8
- child_workflow_state_machine::new_child_workflow,
9
6
  complete_workflow_state_machine::complete_workflow,
10
7
  continue_as_new_workflow_state_machine::continue_as_new,
11
8
  fail_workflow_state_machine::fail_workflow, local_activity_state_machine::new_local_activity,
@@ -16,13 +13,23 @@ use super::{
16
13
  TemporalStateMachine,
17
14
  };
18
15
  use crate::{
16
+ internal_flags::InternalFlags,
19
17
  protosext::{HistoryEventExt, ValidScheduleLA},
20
18
  telemetry::{metrics::MetricsContext, VecDisplayer},
21
19
  worker::{
22
20
  workflow::{
23
- machines::modify_workflow_properties_state_machine::modify_workflow_properties,
24
- CommandID, DrivenWorkflow, HistoryUpdate, LocalResolution, OutgoingJob, WFCommand,
25
- WorkflowFetcher, WorkflowStartedInfo,
21
+ history_update::NextWFT,
22
+ machines::{
23
+ activity_state_machine::ActivityMachine,
24
+ child_workflow_state_machine::ChildWorkflowMachine,
25
+ modify_workflow_properties_state_machine::modify_workflow_properties,
26
+ patch_state_machine::VERSION_SEARCH_ATTR_KEY,
27
+ upsert_search_attributes_state_machine::upsert_search_attrs_internal,
28
+ HistEventData,
29
+ },
30
+ CommandID, DrivenWorkflow, HistoryUpdate, InternalFlagsRef, LocalResolution,
31
+ OutgoingJob, RunBasics, WFCommand, WFMachinesError, WorkflowFetcher,
32
+ WorkflowStartedInfo,
26
33
  },
27
34
  ExecutingLAId, LocalActRequest, LocalActivityExecutionResult, LocalActivityResolution,
28
35
  },
@@ -31,9 +38,11 @@ use siphasher::sip::SipHasher13;
31
38
  use slotmap::{SlotMap, SparseSecondaryMap};
32
39
  use std::{
33
40
  borrow::{Borrow, BorrowMut},
41
+ cell::RefCell,
34
42
  collections::{HashMap, VecDeque},
35
43
  convert::TryInto,
36
44
  hash::{Hash, Hasher},
45
+ rc::Rc,
37
46
  time::{Duration, Instant, SystemTime},
38
47
  };
39
48
  use temporal_sdk_core_protos::{
@@ -50,7 +59,8 @@ use temporal_sdk_core_protos::{
50
59
  temporal::api::{
51
60
  command::v1::{command::Attributes as ProtoCmdAttrs, Command as ProtoCommand},
52
61
  enums::v1::EventType,
53
- history::v1::{history_event, HistoryEvent},
62
+ history::v1::{history_event, history_event::Attributes, HistoryEvent},
63
+ sdk::v1::WorkflowTaskCompletedMetadata,
54
64
  },
55
65
  };
56
66
 
@@ -95,6 +105,9 @@ pub(crate) struct WorkflowMachines {
95
105
  /// The current workflow time if it has been established. This may differ from the WFT start
96
106
  /// time since local activities may advance the clock
97
107
  current_wf_time: Option<SystemTime>,
108
+ /// The internal flags which have been seen so far during this run's execution and thus are
109
+ /// usable during replay.
110
+ observed_internal_flags: InternalFlagsRef,
98
111
 
99
112
  all_machines: SlotMap<MachineKey, Machines>,
100
113
  /// If a machine key is in this map, that machine was created internally by core, not as a
@@ -133,7 +146,7 @@ pub(crate) struct WorkflowMachines {
133
146
  }
134
147
 
135
148
  #[derive(Debug, derive_more::Display)]
136
- #[display(fmt = "Cmd&Machine({})", "command")]
149
+ #[display(fmt = "Cmd&Machine({command})")]
137
150
  struct CommandAndMachine {
138
151
  command: MachineAssociatedCommand,
139
152
  machine: MachineKey,
@@ -142,7 +155,7 @@ struct CommandAndMachine {
142
155
  #[derive(Debug, derive_more::Display)]
143
156
  enum MachineAssociatedCommand {
144
157
  Real(Box<ProtoCommand>),
145
- #[display(fmt = "FakeLocalActivityMarker({})", "_0")]
158
+ #[display(fmt = "FakeLocalActivityMarker({_0})")]
146
159
  FakeLocalActivityMarker(u32),
147
160
  }
148
161
 
@@ -156,7 +169,7 @@ struct ChangeInfo {
156
169
  #[must_use]
157
170
  #[allow(clippy::large_enum_variant)]
158
171
  pub(super) enum MachineResponse {
159
- #[display(fmt = "PushWFJob({})", "_0")]
172
+ #[display(fmt = "PushWFJob({_0})")]
160
173
  PushWFJob(OutgoingJob),
161
174
 
162
175
  /// Pushes a new command into the list that will be sent to server once we respond with the
@@ -165,31 +178,31 @@ pub(super) enum MachineResponse {
165
178
  /// The machine requests the creation of another *different* machine. This acts as if lang
166
179
  /// had replied to the activation with a command, but we use a special set of IDs to avoid
167
180
  /// collisions.
168
- #[display(fmt = "NewCoreOriginatedCommand({:?})", "_0")]
181
+ #[display(fmt = "NewCoreOriginatedCommand({_0:?})")]
169
182
  NewCoreOriginatedCommand(ProtoCmdAttrs),
170
- #[display(fmt = "IssueFakeLocalActivityMarker({})", "_0")]
183
+ #[display(fmt = "IssueFakeLocalActivityMarker({_0})")]
171
184
  IssueFakeLocalActivityMarker(u32),
172
185
  #[display(fmt = "TriggerWFTaskStarted")]
173
186
  TriggerWFTaskStarted {
174
187
  task_started_event_id: i64,
175
188
  time: SystemTime,
176
189
  },
177
- #[display(fmt = "UpdateRunIdOnWorkflowReset({})", run_id)]
190
+ #[display(fmt = "UpdateRunIdOnWorkflowReset({run_id})")]
178
191
  UpdateRunIdOnWorkflowReset { run_id: String },
179
192
 
180
193
  /// Queue a local activity to be processed by the worker
181
194
  #[display(fmt = "QueueLocalActivity")]
182
195
  QueueLocalActivity(ValidScheduleLA),
183
196
  /// Request cancellation of an executing local activity
184
- #[display(fmt = "RequestCancelLocalActivity({})", "_0")]
197
+ #[display(fmt = "RequestCancelLocalActivity({_0})")]
185
198
  RequestCancelLocalActivity(u32),
186
199
  /// Indicates we are abandoning the indicated LA, so we can remove it from "outstanding" LAs
187
200
  /// and we will not try to WFT heartbeat because of it.
188
- #[display(fmt = "AbandonLocalActivity({:?})", "_0")]
201
+ #[display(fmt = "AbandonLocalActivity({_0:?})")]
189
202
  AbandonLocalActivity(u32),
190
203
 
191
204
  /// Set the workflow time to the provided time
192
- #[display(fmt = "UpdateWFTime({:?})", "_0")]
205
+ #[display(fmt = "UpdateWFTime({_0:?})")]
193
206
  UpdateWFTime(Option<SystemTime>),
194
207
  }
195
208
 
@@ -203,25 +216,22 @@ where
203
216
  }
204
217
 
205
218
  impl WorkflowMachines {
206
- pub(crate) fn new(
207
- namespace: String,
208
- workflow_id: String,
209
- workflow_type: String,
210
- run_id: String,
211
- history: HistoryUpdate,
212
- driven_wf: DrivenWorkflow,
213
- metrics: MetricsContext,
214
- ) -> Self {
215
- let replaying = history.previous_started_event_id > 0;
219
+ pub(crate) fn new(basics: RunBasics, driven_wf: DrivenWorkflow) -> Self {
220
+ let replaying = basics.history.previous_wft_started_id > 0;
221
+ let mut observed_internal_flags = InternalFlags::new(basics.capabilities);
222
+ // Peek ahead to determine used patches in the first WFT.
223
+ if let Some(attrs) = basics.history.peek_next_wft_completed(0) {
224
+ observed_internal_flags.add_from_complete(attrs);
225
+ };
216
226
  Self {
217
- last_history_from_server: history,
218
- namespace,
219
- workflow_id,
220
- workflow_type,
221
- run_id,
227
+ last_history_from_server: basics.history,
228
+ namespace: basics.namespace,
229
+ workflow_id: basics.workflow_id,
230
+ workflow_type: basics.workflow_type,
231
+ run_id: basics.run_id,
222
232
  drive_me: driven_wf,
223
233
  replaying,
224
- metrics,
234
+ metrics: basics.metrics,
225
235
  // In an ideal world one could say ..Default::default() here and it'd still work.
226
236
  current_started_event_id: 0,
227
237
  next_started_event_id: 0,
@@ -230,6 +240,7 @@ impl WorkflowMachines {
230
240
  workflow_end_time: None,
231
241
  wft_start_time: None,
232
242
  current_wf_time: None,
243
+ observed_internal_flags: Rc::new(RefCell::new(observed_internal_flags)),
233
244
  all_machines: Default::default(),
234
245
  machine_is_core_created: Default::default(),
235
246
  machines_by_event_id: Default::default(),
@@ -255,10 +266,10 @@ impl WorkflowMachines {
255
266
  .and_then(|(st, et)| et.duration_since(st).ok())
256
267
  }
257
268
 
258
- pub(crate) async fn new_history_from_server(&mut self, update: HistoryUpdate) -> Result<()> {
269
+ pub(crate) fn new_history_from_server(&mut self, update: HistoryUpdate) -> Result<()> {
259
270
  self.last_history_from_server = update;
260
- self.replaying = self.last_history_from_server.previous_started_event_id > 0;
261
- self.apply_next_wft_from_history().await?;
271
+ self.replaying = self.last_history_from_server.previous_wft_started_id > 0;
272
+ self.apply_next_wft_from_history()?;
262
273
  Ok(())
263
274
  }
264
275
 
@@ -290,9 +301,8 @@ impl WorkflowMachines {
290
301
  self.process_machine_responses(mk, resps)?;
291
302
  } else {
292
303
  return Err(WFMachinesError::Nondeterminism(format!(
293
- "Command matching activity with seq num {} existed but was not a \
294
- local activity!",
295
- seq
304
+ "Command matching activity with seq num {seq} existed but was not a \
305
+ local activity!"
296
306
  )));
297
307
  }
298
308
  self.local_activity_data.done_executing(seq);
@@ -350,6 +360,12 @@ impl WorkflowMachines {
350
360
  run_id: self.run_id.clone(),
351
361
  history_length: self.last_processed_event as u32,
352
362
  jobs,
363
+ available_internal_flags: (*self.observed_internal_flags)
364
+ .borrow()
365
+ .all_lang()
366
+ .iter()
367
+ .copied()
368
+ .collect(),
353
369
  }
354
370
  }
355
371
 
@@ -364,10 +380,22 @@ impl WorkflowMachines {
364
380
  .any(|v| v.is_la_resolution)
365
381
  }
366
382
 
383
+ pub(crate) fn get_metadata_for_wft_complete(&self) -> WorkflowTaskCompletedMetadata {
384
+ (*self.observed_internal_flags)
385
+ .borrow_mut()
386
+ .gather_for_wft_complete()
387
+ }
388
+
389
+ pub(crate) fn add_lang_used_flags(&self, flags: Vec<u32>) {
390
+ (*self.observed_internal_flags)
391
+ .borrow_mut()
392
+ .add_lang_used(flags);
393
+ }
394
+
367
395
  /// Iterate the state machines, which consists of grabbing any pending outgoing commands from
368
396
  /// the workflow code, handling them, and preparing them to be sent off to the server.
369
- pub(crate) async fn iterate_machines(&mut self) -> Result<()> {
370
- let results = self.drive_me.fetch_workflow_iteration_output().await;
397
+ pub(crate) fn iterate_machines(&mut self) -> Result<()> {
398
+ let results = self.drive_me.fetch_workflow_iteration_output();
371
399
  self.handle_driven_results(results)?;
372
400
  self.prepare_commands()?;
373
401
  if self.workflow_is_finished() {
@@ -378,27 +406,60 @@ impl WorkflowMachines {
378
406
  Ok(())
379
407
  }
380
408
 
409
+ /// Returns true if machines are ready to apply the next WFT sequence, false if events will need
410
+ /// to be fetched in order to create a complete update with the entire next WFT sequence.
411
+ pub(crate) fn ready_to_apply_next_wft(&self) -> bool {
412
+ self.last_history_from_server
413
+ .can_take_next_wft_sequence(self.current_started_event_id)
414
+ }
415
+
381
416
  /// Apply the next (unapplied) entire workflow task from history to these machines. Will replay
382
- /// any events that need to be replayed until caught up to the newest WFT. May also fetch
383
- /// history from server if needed.
384
- pub(crate) async fn apply_next_wft_from_history(&mut self) -> Result<usize> {
417
+ /// any events that need to be replayed until caught up to the newest WFT.
418
+ pub(crate) fn apply_next_wft_from_history(&mut self) -> Result<usize> {
385
419
  // If we have already seen the terminal event for the entire workflow in a previous WFT,
386
420
  // then we don't need to do anything here, and in fact we need to avoid re-applying the
387
421
  // final WFT.
388
422
  if self.have_seen_terminal_event {
423
+ // Replay clearly counts as done now, since we return here and never do anything else.
424
+ self.replaying = false;
389
425
  return Ok(0);
390
426
  }
391
427
 
392
- let last_handled_wft_started_id = self.current_started_event_id;
393
- let events = {
394
- let mut evts = self
428
+ fn update_internal_flags(me: &mut WorkflowMachines) {
429
+ // Update observed patches with any that were used in the task
430
+ if let Some(next_complete) = me
395
431
  .last_history_from_server
396
- .take_next_wft_sequence(last_handled_wft_started_id)
397
- .await
398
- .map_err(WFMachinesError::HistoryFetchingError)?;
399
- // Do not re-process events we have already processed
400
- evts.retain(|e| e.event_id > self.last_processed_event);
401
- evts
432
+ .peek_next_wft_completed(me.last_processed_event)
433
+ {
434
+ (*me.observed_internal_flags)
435
+ .borrow_mut()
436
+ .add_from_complete(next_complete);
437
+ }
438
+ }
439
+
440
+ // We update the internal flags before applying the current task (peeking to the completion
441
+ // of this task), and also at the end (peeking to the completion of the task that lang is
442
+ // about to generate commands for, and for which we will want those flags active).
443
+ update_internal_flags(self);
444
+
445
+ let last_handled_wft_started_id = self.current_started_event_id;
446
+ let (events, has_final_event) = match self
447
+ .last_history_from_server
448
+ .take_next_wft_sequence(last_handled_wft_started_id)
449
+ {
450
+ NextWFT::ReplayOver => (vec![], true),
451
+ NextWFT::WFT(mut evts, has_final_event) => {
452
+ // Do not re-process events we have already processed
453
+ evts.retain(|e| e.event_id > self.last_processed_event);
454
+ (evts, has_final_event)
455
+ }
456
+ NextWFT::NeedFetch => {
457
+ return Err(WFMachinesError::Fatal(
458
+ "Need to fetch history events to continue applying workflow task, but this \
459
+ should be prevented ahead of time! This is a Core SDK bug."
460
+ .to_string(),
461
+ ));
462
+ }
402
463
  };
403
464
  let num_events_to_process = events.len();
404
465
 
@@ -414,6 +475,8 @@ impl WorkflowMachines {
414
475
  }
415
476
  }
416
477
 
478
+ let mut saw_completed = false;
479
+ let mut do_handle_event = true;
417
480
  let mut history = events.into_iter().peekable();
418
481
  while let Some(event) = history.next() {
419
482
  if event.event_id != self.last_processed_event + 1 {
@@ -424,17 +487,52 @@ impl WorkflowMachines {
424
487
  }
425
488
  let next_event = history.peek();
426
489
  let eid = event.event_id;
427
- let etype = event.event_type();
428
- self.handle_event(event, next_event.is_some())?;
429
- self.last_processed_event = eid;
430
- if etype == EventType::WorkflowTaskStarted && next_event.is_none() {
431
- break;
490
+
491
+ // This definition of replaying here is that we are no longer replaying as soon as we
492
+ // see new events that have never been seen or produced by the SDK.
493
+ //
494
+ // Specifically, replay ends once we have seen the last command-event which was produced
495
+ // as a result of the last completed WFT. Thus, replay would be false for things like
496
+ // signals which were received and after the last completion, and thus generated the
497
+ // current WFT being handled.
498
+ if self.replaying && has_final_event && saw_completed && !event.is_command_event() {
499
+ // Replay is finished
500
+ self.replaying = false;
501
+ }
502
+ if event.event_type() == EventType::WorkflowTaskCompleted {
503
+ saw_completed = true;
432
504
  }
505
+
506
+ if do_handle_event {
507
+ let eho = self.handle_event(
508
+ HistEventData {
509
+ event,
510
+ replaying: self.replaying,
511
+ current_task_is_last_in_history: has_final_event,
512
+ },
513
+ next_event,
514
+ )?;
515
+ if matches!(
516
+ eho,
517
+ EventHandlingOutcome::SkipEvent {
518
+ skip_next_event: true
519
+ }
520
+ ) {
521
+ do_handle_event = false;
522
+ }
523
+ } else {
524
+ do_handle_event = true;
525
+ }
526
+ self.last_processed_event = eid;
433
527
  }
434
528
 
435
529
  // Scan through to the next WFT, searching for any patch / la markers, so that we can
436
530
  // pre-resolve them.
437
- for e in self.last_history_from_server.peek_next_wft_sequence() {
531
+ let mut wake_las = vec![];
532
+ for e in self
533
+ .last_history_from_server
534
+ .peek_next_wft_sequence(last_handled_wft_started_id)
535
+ {
438
536
  if let Some((patch_id, _)) = e.get_patch_marker_details() {
439
537
  self.encountered_change_markers.insert(
440
538
  patch_id.clone(),
@@ -448,10 +546,35 @@ impl WorkflowMachines {
448
546
  .into(),
449
547
  );
450
548
  } else if e.is_local_activity_marker() {
451
- self.local_activity_data.process_peekahead_marker(e)?;
549
+ if let Some(la_dat) = e.clone().into_local_activity_marker_details() {
550
+ if let Ok(mk) =
551
+ self.get_machine_key(CommandID::LocalActivity(la_dat.marker_dat.seq))
552
+ {
553
+ wake_las.push((mk, la_dat));
554
+ } else {
555
+ self.local_activity_data.insert_peeked_marker(la_dat);
556
+ }
557
+ } else {
558
+ return Err(WFMachinesError::Fatal(format!(
559
+ "Local activity marker was unparsable: {e:?}"
560
+ )));
561
+ }
562
+ }
563
+ }
564
+ for (mk, la_dat) in wake_las {
565
+ let mach = self.machine_mut(mk);
566
+ if let Machines::LocalActivityMachine(ref mut lam) = *mach {
567
+ if lam.will_accept_resolve_marker() {
568
+ let resps = lam.try_resolve_with_dat(la_dat.into())?;
569
+ self.process_machine_responses(mk, resps)?;
570
+ } else {
571
+ self.local_activity_data.insert_peeked_marker(la_dat);
572
+ }
452
573
  }
453
574
  }
454
575
 
576
+ update_internal_flags(self);
577
+
455
578
  if !self.replaying {
456
579
  self.metrics.wf_task_replay_latency(replay_start.elapsed());
457
580
  }
@@ -459,16 +582,20 @@ impl WorkflowMachines {
459
582
  Ok(num_events_to_process)
460
583
  }
461
584
 
462
- /// Handle a single event from the workflow history. `has_next_event` should be false if `event`
463
- /// is the last event in the history.
585
+ /// Handle a single event from the workflow history.
464
586
  ///
465
587
  /// This function will attempt to apply the event to the workflow state machines. If there is
466
588
  /// not a matching machine for the event, a nondeterminism error is returned. Otherwise, the
467
589
  /// event is applied to the machine, which may also return a nondeterminism error if the machine
468
590
  /// does not match the expected type. A fatal error may be returned if the machine is in an
469
591
  /// invalid state.
470
- #[instrument(skip(self, event), fields(event=%event))]
471
- fn handle_event(&mut self, event: HistoryEvent, has_next_event: bool) -> Result<()> {
592
+ #[instrument(skip(self, event_dat), fields(event=%event_dat))]
593
+ fn handle_event(
594
+ &mut self,
595
+ event_dat: HistEventData,
596
+ next_event: Option<&HistoryEvent>,
597
+ ) -> Result<EventHandlingOutcome> {
598
+ let event = &event_dat.event;
472
599
  if event.is_final_wf_execution_event() {
473
600
  self.have_seen_terminal_event = true;
474
601
  }
@@ -476,39 +603,33 @@ impl WorkflowMachines {
476
603
  event.event_type(),
477
604
  EventType::WorkflowExecutionTerminated | EventType::WorkflowExecutionTimedOut
478
605
  ) {
479
- return if has_next_event {
606
+ let are_more_events =
607
+ next_event.is_some() || !event_dat.current_task_is_last_in_history;
608
+ return if are_more_events {
480
609
  Err(WFMachinesError::Fatal(
481
610
  "Machines were fed a history which has an event after workflow execution was \
482
611
  terminated!"
483
612
  .to_string(),
484
613
  ))
485
614
  } else {
486
- Ok(())
615
+ Ok(EventHandlingOutcome::Normal)
487
616
  };
488
617
  }
489
- if self.replaying
490
- && self.current_started_event_id
491
- >= self.last_history_from_server.previous_started_event_id
492
- && event.event_type() != EventType::WorkflowTaskCompleted
493
- {
494
- // Replay is finished
495
- self.replaying = false;
496
- }
497
618
  if event.event_type() == EventType::Unspecified || event.attributes.is_none() {
498
619
  return if !event.worker_may_ignore {
499
620
  Err(WFMachinesError::Fatal(format!(
500
- "Event type is unspecified! This history is invalid. Event detail: {:?}",
501
- event
621
+ "Event type is unspecified! This history is invalid. Event detail: {event:?}"
502
622
  )))
503
623
  } else {
504
624
  debug!("Event is ignorable");
505
- Ok(())
625
+ Ok(EventHandlingOutcome::SkipEvent {
626
+ skip_next_event: false,
627
+ })
506
628
  };
507
629
  }
508
630
 
509
631
  if event.is_command_event() {
510
- self.handle_command_event(event)?;
511
- return Ok(());
632
+ return self.handle_command_event(event_dat, next_event);
512
633
  }
513
634
 
514
635
  if let Some(initial_cmd_id) = event.get_initial_command_event_id() {
@@ -517,7 +638,7 @@ impl WorkflowMachines {
517
638
  let maybe_machine = self.machines_by_event_id.remove(&initial_cmd_id);
518
639
  match maybe_machine {
519
640
  Some(sm) => {
520
- self.submachine_handle_event(sm, event, has_next_event)?;
641
+ self.submachine_handle_event(sm, event_dat)?;
521
642
  // Restore machine if not in it's final state
522
643
  if !self.machine(sm).is_final_state() {
523
644
  self.machines_by_event_id.insert(initial_cmd_id, sm);
@@ -526,16 +647,15 @@ impl WorkflowMachines {
526
647
  None => {
527
648
  return Err(WFMachinesError::Nondeterminism(format!(
528
649
  "During event handling, this event had an initial command ID but we \
529
- could not find a matching command for it: {:?}",
530
- event
650
+ could not find a matching command for it: {event:?}"
531
651
  )));
532
652
  }
533
653
  }
534
654
  } else {
535
- self.handle_non_stateful_event(event, has_next_event)?;
655
+ self.handle_non_stateful_event(event_dat)?;
536
656
  }
537
657
 
538
- Ok(())
658
+ Ok(EventHandlingOutcome::Normal)
539
659
  }
540
660
 
541
661
  /// A command event is an event which is generated from a command emitted as a result of
@@ -547,23 +667,28 @@ impl WorkflowMachines {
547
667
  /// The handling consists of verifying that the next command in the commands queue is associated
548
668
  /// with a state machine, which is then notified about the event and the command is removed from
549
669
  /// the commands queue.
550
- fn handle_command_event(&mut self, event: HistoryEvent) -> Result<()> {
670
+ fn handle_command_event(
671
+ &mut self,
672
+ event_dat: HistEventData,
673
+ next_event: Option<&HistoryEvent>,
674
+ ) -> Result<EventHandlingOutcome> {
675
+ let event = &event_dat.event;
676
+
551
677
  if event.is_local_activity_marker() {
552
678
  let deets = event.extract_local_activity_marker_data().ok_or_else(|| {
553
- WFMachinesError::Fatal(format!("Local activity marker was unparsable: {:?}", event))
679
+ WFMachinesError::Fatal(format!("Local activity marker was unparsable: {event:?}"))
554
680
  })?;
555
681
  let cmdid = CommandID::LocalActivity(deets.seq);
556
682
  let mkey = self.get_machine_key(cmdid)?;
557
683
  if let Machines::LocalActivityMachine(lam) = self.machine(mkey) {
558
684
  if lam.marker_should_get_special_handling()? {
559
- self.submachine_handle_event(mkey, event, false)?;
560
- return Ok(());
685
+ self.submachine_handle_event(mkey, event_dat)?;
686
+ return Ok(EventHandlingOutcome::Normal);
561
687
  }
562
688
  } else {
563
689
  return Err(WFMachinesError::Fatal(format!(
564
690
  "Encountered local activity marker but the associated machine was of the \
565
- wrong type! {:?}",
566
- event
691
+ wrong type! {event:?}"
567
692
  )));
568
693
  }
569
694
  }
@@ -573,13 +698,13 @@ impl WorkflowMachines {
573
698
  let consumed_cmd = loop {
574
699
  if let Some(peek_machine) = self.commands.front() {
575
700
  let mach = self.machine(peek_machine.machine);
576
- match change_marker_handling(&event, mach)? {
577
- ChangeMarkerOutcome::SkipEvent => return Ok(()),
578
- ChangeMarkerOutcome::SkipCommand => {
701
+ match change_marker_handling(event, mach, next_event)? {
702
+ EventHandlingOutcome::SkipCommand => {
579
703
  self.commands.pop_front();
580
704
  continue;
581
705
  }
582
- ChangeMarkerOutcome::Normal => {}
706
+ eho @ EventHandlingOutcome::SkipEvent { .. } => return Ok(eho),
707
+ EventHandlingOutcome::Normal => {}
583
708
  }
584
709
  }
585
710
 
@@ -588,8 +713,7 @@ impl WorkflowMachines {
588
713
  c
589
714
  } else {
590
715
  return Err(WFMachinesError::Nondeterminism(format!(
591
- "No command scheduled for event {}",
592
- event
716
+ "No command scheduled for event {event}"
593
717
  )));
594
718
  };
595
719
 
@@ -599,7 +723,7 @@ impl WorkflowMachines {
599
723
 
600
724
  if !canceled_before_sent {
601
725
  // Feed the machine the event
602
- self.submachine_handle_event(command.machine, event, true)?;
726
+ self.submachine_handle_event(command.machine, event_dat)?;
603
727
  break command;
604
728
  }
605
729
  };
@@ -609,26 +733,22 @@ impl WorkflowMachines {
609
733
  .insert(event_id, consumed_cmd.machine);
610
734
  }
611
735
 
612
- Ok(())
736
+ Ok(EventHandlingOutcome::Normal)
613
737
  }
614
738
 
615
- fn handle_non_stateful_event(
616
- &mut self,
617
- event: HistoryEvent,
618
- has_next_event: bool,
619
- ) -> Result<()> {
739
+ fn handle_non_stateful_event(&mut self, event_dat: HistEventData) -> Result<()> {
620
740
  trace!(
621
- event = %event,
741
+ event = %event_dat.event,
622
742
  "handling non-stateful event"
623
743
  );
624
- let event_id = event.event_id;
625
- match EventType::from_i32(event.event_type) {
744
+ let event_id = event_dat.event.event_id;
745
+ match EventType::from_i32(event_dat.event.event_type) {
626
746
  Some(EventType::WorkflowExecutionStarted) => {
627
747
  if let Some(history_event::Attributes::WorkflowExecutionStartedEventAttributes(
628
748
  attrs,
629
- )) = event.attributes
749
+ )) = event_dat.event.attributes
630
750
  {
631
- if let Some(st) = event.event_time.clone() {
751
+ if let Some(st) = event_dat.event.event_time.clone() {
632
752
  let as_systime: SystemTime = st.try_into()?;
633
753
  self.workflow_start_time = Some(as_systime);
634
754
  // Set the workflow time to be the event time of the first event, so that
@@ -640,26 +760,25 @@ impl WorkflowMachines {
640
760
  self.drive_me.start(
641
761
  self.workflow_id.clone(),
642
762
  str_to_randomness_seed(&attrs.original_execution_run_id),
643
- event.event_time.unwrap_or_default(),
763
+ event_dat.event.event_time.unwrap_or_default(),
644
764
  attrs,
645
765
  );
646
766
  } else {
647
767
  return Err(WFMachinesError::Fatal(format!(
648
- "WorkflowExecutionStarted event did not have appropriate attributes: {}",
649
- event
768
+ "WorkflowExecutionStarted event did not have appropriate attributes: {event_dat}"
650
769
  )));
651
770
  }
652
771
  }
653
772
  Some(EventType::WorkflowTaskScheduled) => {
654
773
  let wf_task_sm = WorkflowTaskMachine::new(self.next_started_event_id);
655
774
  let key = self.all_machines.insert(wf_task_sm.into());
656
- self.submachine_handle_event(key, event, has_next_event)?;
775
+ self.submachine_handle_event(key, event_dat)?;
657
776
  self.machines_by_event_id.insert(event_id, key);
658
777
  }
659
778
  Some(EventType::WorkflowExecutionSignaled) => {
660
779
  if let Some(history_event::Attributes::WorkflowExecutionSignaledEventAttributes(
661
780
  attrs,
662
- )) = event.attributes
781
+ )) = event_dat.event.attributes
663
782
  {
664
783
  self.drive_me
665
784
  .send_job(workflow_activation::SignalWorkflow::from(attrs).into());
@@ -672,7 +791,7 @@ impl WorkflowMachines {
672
791
  history_event::Attributes::WorkflowExecutionCancelRequestedEventAttributes(
673
792
  attrs,
674
793
  ),
675
- ) = event.attributes
794
+ ) = event_dat.event.attributes
676
795
  {
677
796
  self.drive_me
678
797
  .send_job(workflow_activation::CancelWorkflow::from(attrs).into());
@@ -682,8 +801,7 @@ impl WorkflowMachines {
682
801
  }
683
802
  _ => {
684
803
  return Err(WFMachinesError::Fatal(format!(
685
- "The event is not a non-stateful event, but we tried to handle it as one: {}",
686
- event
804
+ "The event is not a non-stateful event, but we tried to handle it as one: {event_dat}"
687
805
  )));
688
806
  }
689
807
  }
@@ -700,13 +818,8 @@ impl WorkflowMachines {
700
818
 
701
819
  /// Wrapper for calling [TemporalStateMachine::handle_event] which appropriately takes action
702
820
  /// on the returned machine responses
703
- fn submachine_handle_event(
704
- &mut self,
705
- sm: MachineKey,
706
- event: HistoryEvent,
707
- has_next_event: bool,
708
- ) -> Result<()> {
709
- let machine_responses = self.machine_mut(sm).handle_event(event, has_next_event)?;
821
+ fn submachine_handle_event(&mut self, sm: MachineKey, event: HistEventData) -> Result<()> {
822
+ let machine_responses = self.machine_mut(sm).handle_event(event)?;
710
823
  self.process_machine_responses(sm, machine_responses)?;
711
824
  Ok(())
712
825
  }
@@ -756,7 +869,7 @@ impl WorkflowMachines {
756
869
  ) -> Result<()> {
757
870
  let sm = self.machine(smk);
758
871
  if !machine_responses.is_empty() {
759
- debug!(responses = %machine_responses.display(), machine_name = %sm.name(),
872
+ trace!(responses = %machine_responses.display(), machine_name = %sm.name(),
760
873
  "Machine produced responses");
761
874
  }
762
875
  self.process_machine_resps_impl(smk, machine_responses)
@@ -809,10 +922,18 @@ impl WorkflowMachines {
809
922
  CommandIdKind::CoreInternal,
810
923
  );
811
924
  }
925
+ ProtoCmdAttrs::UpsertWorkflowSearchAttributesCommandAttributes(attrs) => {
926
+ self.add_cmd_to_wf_task(
927
+ upsert_search_attrs_internal(
928
+ attrs,
929
+ self.observed_internal_flags.clone(),
930
+ ),
931
+ CommandIdKind::NeverResolves,
932
+ );
933
+ }
812
934
  c => {
813
935
  return Err(WFMachinesError::Fatal(format!(
814
- "A machine requested to create a new command of an unsupported type: {:?}",
815
- c
936
+ "A machine requested to create a new command of an unsupported type: {c:?}"
816
937
  )))
817
938
  }
818
939
  },
@@ -849,7 +970,7 @@ impl WorkflowMachines {
849
970
  Duration::from_secs(0),
850
971
  removed_act.attempt,
851
972
  None,
852
- None,
973
+ removed_act.original_schedule_time,
853
974
  )?;
854
975
  self.process_machine_responses(smk, more_responses)?;
855
976
  } else {
@@ -916,7 +1037,11 @@ impl WorkflowMachines {
916
1037
  }
917
1038
  WFCommand::UpsertSearchAttributes(attrs) => {
918
1039
  self.add_cmd_to_wf_task(
919
- upsert_search_attrs(attrs),
1040
+ upsert_search_attrs(
1041
+ attrs,
1042
+ self.observed_internal_flags.clone(),
1043
+ self.replaying,
1044
+ ),
920
1045
  CommandIdKind::NeverResolves,
921
1046
  );
922
1047
  }
@@ -925,7 +1050,10 @@ impl WorkflowMachines {
925
1050
  }
926
1051
  WFCommand::AddActivity(attrs) => {
927
1052
  let seq = attrs.seq;
928
- self.add_cmd_to_wf_task(new_activity(attrs), CommandID::Activity(seq).into());
1053
+ self.add_cmd_to_wf_task(
1054
+ ActivityMachine::new_scheduled(attrs, self.observed_internal_flags.clone()),
1055
+ CommandID::Activity(seq).into(),
1056
+ );
929
1057
  }
930
1058
  WFCommand::AddLocalActivity(attrs) => {
931
1059
  let seq = attrs.seq;
@@ -937,8 +1065,7 @@ impl WorkflowMachines {
937
1065
  )
938
1066
  .map_err(|e| {
939
1067
  WFMachinesError::Fatal(format!(
940
- "Invalid schedule local activity request (seq {}): {}",
941
- seq, e
1068
+ "Invalid schedule local activity request (seq {seq}): {e}"
942
1069
  ))
943
1070
  })?;
944
1071
  let (la, mach_resp) = new_local_activity(
@@ -946,6 +1073,7 @@ impl WorkflowMachines {
946
1073
  self.replaying,
947
1074
  self.local_activity_data.take_preresolution(seq),
948
1075
  self.current_wf_time,
1076
+ self.observed_internal_flags.clone(),
949
1077
  )?;
950
1078
  let machkey = self.all_machines.insert(la.into());
951
1079
  self.id_to_machine
@@ -978,13 +1106,21 @@ impl WorkflowMachines {
978
1106
  WFCommand::SetPatchMarker(attrs) => {
979
1107
  // Do not create commands for change IDs that we have already created commands
980
1108
  // for.
981
- if !matches!(self.encountered_change_markers.get(&attrs.patch_id),
1109
+ let encountered_entry = self.encountered_change_markers.get(&attrs.patch_id);
1110
+ if !matches!(encountered_entry,
982
1111
  Some(ChangeInfo {created_command}) if *created_command)
983
1112
  {
984
- self.add_cmd_to_wf_task(
985
- has_change(attrs.patch_id.clone(), self.replaying, attrs.deprecated),
986
- CommandIdKind::NeverResolves,
987
- );
1113
+ let (patch_machine, other_cmds) = has_change(
1114
+ attrs.patch_id.clone(),
1115
+ self.replaying,
1116
+ attrs.deprecated,
1117
+ encountered_entry.is_some(),
1118
+ self.encountered_change_markers.keys().map(|s| s.as_str()),
1119
+ self.observed_internal_flags.clone(),
1120
+ )?;
1121
+ let mkey =
1122
+ self.add_cmd_to_wf_task(patch_machine, CommandIdKind::NeverResolves);
1123
+ self.process_machine_responses(mkey, other_cmds)?;
988
1124
 
989
1125
  if let Some(ci) = self.encountered_change_markers.get_mut(&attrs.patch_id) {
990
1126
  ci.created_command = true;
@@ -1001,7 +1137,10 @@ impl WorkflowMachines {
1001
1137
  WFCommand::AddChildWorkflow(attrs) => {
1002
1138
  let seq = attrs.seq;
1003
1139
  self.add_cmd_to_wf_task(
1004
- new_child_workflow(attrs),
1140
+ ChildWorkflowMachine::new_scheduled(
1141
+ attrs,
1142
+ self.observed_internal_flags.clone(),
1143
+ ),
1005
1144
  CommandID::ChildWorkflowStart(seq).into(),
1006
1145
  );
1007
1146
  }
@@ -1075,7 +1214,7 @@ impl WorkflowMachines {
1075
1214
 
1076
1215
  fn get_machine_key(&self, id: CommandID) -> Result<MachineKey> {
1077
1216
  Ok(*self.id_to_machine.get(&id).ok_or_else(|| {
1078
- WFMachinesError::Fatal(format!("Missing associated machine for {:?}", id))
1217
+ WFMachinesError::Fatal(format!("Missing associated machine for {id:?}"))
1079
1218
  })?)
1080
1219
  }
1081
1220
 
@@ -1086,15 +1225,21 @@ impl WorkflowMachines {
1086
1225
  }
1087
1226
 
1088
1227
  /// Add a new command/machines for that command to the current workflow task
1089
- fn add_cmd_to_wf_task(&mut self, machine: NewMachineWithCommand, id: CommandIdKind) {
1228
+ fn add_cmd_to_wf_task(
1229
+ &mut self,
1230
+ machine: NewMachineWithCommand,
1231
+ id: CommandIdKind,
1232
+ ) -> MachineKey {
1090
1233
  let mach = self.add_new_command_machine(machine);
1234
+ let key = mach.machine;
1091
1235
  if let CommandIdKind::LangIssued(id) = id {
1092
- self.id_to_machine.insert(id, mach.machine);
1236
+ self.id_to_machine.insert(id, key);
1093
1237
  }
1094
1238
  if matches!(id, CommandIdKind::CoreInternal) {
1095
- self.machine_is_core_created.insert(mach.machine, ());
1239
+ self.machine_is_core_created.insert(key, ());
1096
1240
  }
1097
1241
  self.current_wf_task_commands.push_back(mach);
1242
+ key
1098
1243
  }
1099
1244
 
1100
1245
  fn add_new_command_machine(&mut self, machine: NewMachineWithCommand) -> CommandAndMachine {
@@ -1154,15 +1299,20 @@ fn str_to_randomness_seed(run_id: &str) -> u64 {
1154
1299
  s.finish()
1155
1300
  }
1156
1301
 
1157
- enum ChangeMarkerOutcome {
1158
- SkipEvent,
1302
+ #[must_use]
1303
+ enum EventHandlingOutcome {
1304
+ SkipEvent { skip_next_event: bool },
1159
1305
  SkipCommand,
1160
1306
  Normal,
1161
1307
  }
1162
1308
 
1163
1309
  /// Special handling for patch markers, when handling command events as in
1164
1310
  /// [WorkflowMachines::handle_command_event]
1165
- fn change_marker_handling(event: &HistoryEvent, mach: &Machines) -> Result<ChangeMarkerOutcome> {
1311
+ fn change_marker_handling(
1312
+ event: &HistoryEvent,
1313
+ mach: &Machines,
1314
+ next_event: Option<&HistoryEvent>,
1315
+ ) -> Result<EventHandlingOutcome> {
1166
1316
  if !mach.matches_event(event) {
1167
1317
  // Version markers can be skipped in the event they are deprecated
1168
1318
  if let Some((patch_name, deprecated)) = event.get_patch_marker_details() {
@@ -1170,12 +1320,22 @@ fn change_marker_handling(event: &HistoryEvent, mach: &Machines) -> Result<Chang
1170
1320
  // markers are allowed without matching changed calls.
1171
1321
  if deprecated {
1172
1322
  debug!("Deprecated patch marker tried against wrong machine, skipping.");
1173
- return Ok(ChangeMarkerOutcome::SkipEvent);
1323
+
1324
+ // Also ignore the subsequent upsert event if present
1325
+ let mut skip_next_event = false;
1326
+ if let Some(Attributes::UpsertWorkflowSearchAttributesEventAttributes(atts)) =
1327
+ next_event.and_then(|ne| ne.attributes.as_ref())
1328
+ {
1329
+ if let Some(ref sa) = atts.search_attributes {
1330
+ skip_next_event = sa.indexed_fields.contains_key(VERSION_SEARCH_ATTR_KEY);
1331
+ }
1332
+ }
1333
+
1334
+ return Ok(EventHandlingOutcome::SkipEvent { skip_next_event });
1174
1335
  }
1175
1336
  return Err(WFMachinesError::Nondeterminism(format!(
1176
- "Non-deprecated patch marker encountered for change {}, \
1177
- but there is no corresponding change command!",
1178
- patch_name
1337
+ "Non-deprecated patch marker encountered for change {patch_name}, \
1338
+ but there is no corresponding change command!"
1179
1339
  )));
1180
1340
  }
1181
1341
  // Patch machines themselves may also not *have* matching markers, where non-deprecated
@@ -1183,10 +1343,10 @@ fn change_marker_handling(event: &HistoryEvent, mach: &Machines) -> Result<Chang
1183
1343
  // worker.
1184
1344
  if matches!(mach, Machines::PatchMachine(_)) {
1185
1345
  debug!("Skipping non-matching event against patch machine");
1186
- return Ok(ChangeMarkerOutcome::SkipCommand);
1346
+ return Ok(EventHandlingOutcome::SkipCommand);
1187
1347
  }
1188
1348
  }
1189
- Ok(ChangeMarkerOutcome::Normal)
1349
+ Ok(EventHandlingOutcome::Normal)
1190
1350
  }
1191
1351
 
1192
1352
  #[derive(derive_more::From)]