temporalio 0.0.2 → 0.1.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 (202) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +25 -23
  3. data/bridge/Cargo.lock +168 -59
  4. data/bridge/Cargo.toml +4 -2
  5. data/bridge/sdk-core/README.md +19 -6
  6. data/bridge/sdk-core/client/src/lib.rs +215 -39
  7. data/bridge/sdk-core/client/src/metrics.rs +17 -8
  8. data/bridge/sdk-core/client/src/raw.rs +4 -4
  9. data/bridge/sdk-core/client/src/retry.rs +32 -20
  10. data/bridge/sdk-core/core/Cargo.toml +22 -9
  11. data/bridge/sdk-core/core/src/abstractions.rs +203 -14
  12. data/bridge/sdk-core/core/src/core_tests/activity_tasks.rs +76 -41
  13. data/bridge/sdk-core/core/src/core_tests/determinism.rs +165 -2
  14. data/bridge/sdk-core/core/src/core_tests/local_activities.rs +204 -83
  15. data/bridge/sdk-core/core/src/core_tests/queries.rs +3 -4
  16. data/bridge/sdk-core/core/src/core_tests/workers.rs +1 -3
  17. data/bridge/sdk-core/core/src/core_tests/workflow_tasks.rs +397 -54
  18. data/bridge/sdk-core/core/src/ephemeral_server/mod.rs +106 -12
  19. data/bridge/sdk-core/core/src/internal_flags.rs +136 -0
  20. data/bridge/sdk-core/core/src/lib.rs +16 -9
  21. data/bridge/sdk-core/core/src/telemetry/log_export.rs +1 -1
  22. data/bridge/sdk-core/core/src/telemetry/metrics.rs +69 -35
  23. data/bridge/sdk-core/core/src/telemetry/mod.rs +29 -13
  24. data/bridge/sdk-core/core/src/telemetry/prometheus_server.rs +17 -12
  25. data/bridge/sdk-core/core/src/test_help/mod.rs +62 -12
  26. data/bridge/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +112 -156
  27. data/bridge/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +89 -0
  28. data/bridge/sdk-core/core/src/worker/activities/local_activities.rs +352 -122
  29. data/bridge/sdk-core/core/src/worker/activities.rs +233 -157
  30. data/bridge/sdk-core/core/src/worker/client/mocks.rs +22 -2
  31. data/bridge/sdk-core/core/src/worker/client.rs +18 -2
  32. data/bridge/sdk-core/core/src/worker/mod.rs +165 -58
  33. data/bridge/sdk-core/core/src/worker/workflow/bridge.rs +1 -3
  34. data/bridge/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -5
  35. data/bridge/sdk-core/core/src/worker/workflow/history_update.rs +856 -277
  36. data/bridge/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +100 -43
  37. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +7 -7
  38. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +5 -4
  39. data/bridge/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +87 -27
  40. data/bridge/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +5 -4
  41. data/bridge/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +5 -4
  42. data/bridge/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +5 -4
  43. data/bridge/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +137 -62
  44. data/bridge/sdk-core/core/src/worker/workflow/machines/mod.rs +25 -17
  45. data/bridge/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +7 -6
  46. data/bridge/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +103 -152
  47. data/bridge/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +7 -7
  48. data/bridge/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +9 -9
  49. data/bridge/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +2 -2
  50. data/bridge/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +14 -7
  51. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +5 -16
  52. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +201 -121
  53. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +11 -14
  54. data/bridge/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +30 -15
  55. data/bridge/sdk-core/core/src/worker/workflow/managed_run.rs +1026 -376
  56. data/bridge/sdk-core/core/src/worker/workflow/mod.rs +460 -384
  57. data/bridge/sdk-core/core/src/worker/workflow/run_cache.rs +40 -57
  58. data/bridge/sdk-core/core/src/worker/workflow/wft_extraction.rs +125 -0
  59. data/bridge/sdk-core/core/src/worker/workflow/wft_poller.rs +1 -4
  60. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +117 -0
  61. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream/tonic_status_serde.rs +24 -0
  62. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream.rs +448 -718
  63. data/bridge/sdk-core/core-api/Cargo.toml +2 -1
  64. data/bridge/sdk-core/core-api/src/errors.rs +1 -34
  65. data/bridge/sdk-core/core-api/src/lib.rs +6 -2
  66. data/bridge/sdk-core/core-api/src/telemetry.rs +0 -6
  67. data/bridge/sdk-core/core-api/src/worker.rs +14 -1
  68. data/bridge/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +18 -15
  69. data/bridge/sdk-core/fsm/rustfsm_trait/src/lib.rs +8 -3
  70. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-16_history.bin +0 -0
  71. data/bridge/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +5 -17
  72. data/bridge/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +11 -0
  73. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +1 -6
  74. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +6 -6
  75. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +5 -0
  76. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +22 -6
  77. data/bridge/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +48 -19
  78. data/bridge/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +2 -0
  79. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +3 -0
  80. data/bridge/sdk-core/protos/api_upstream/temporal/api/{enums/v1/interaction_type.proto → protocol/v1/message.proto} +29 -11
  81. data/bridge/sdk-core/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +63 -0
  82. data/bridge/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +111 -0
  83. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +59 -28
  84. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +2 -2
  85. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +1 -0
  86. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +1 -0
  87. data/bridge/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +1 -0
  88. data/bridge/sdk-core/protos/local/temporal/sdk/core/common/common.proto +1 -0
  89. data/bridge/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +1 -0
  90. data/bridge/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +1 -0
  91. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +7 -0
  92. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +1 -0
  93. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +6 -0
  94. data/bridge/sdk-core/sdk/Cargo.toml +3 -2
  95. data/bridge/sdk-core/sdk/src/lib.rs +87 -20
  96. data/bridge/sdk-core/sdk/src/workflow_future.rs +9 -8
  97. data/bridge/sdk-core/sdk-core-protos/Cargo.toml +5 -2
  98. data/bridge/sdk-core/sdk-core-protos/build.rs +36 -1
  99. data/bridge/sdk-core/sdk-core-protos/src/history_builder.rs +100 -87
  100. data/bridge/sdk-core/sdk-core-protos/src/history_info.rs +5 -1
  101. data/bridge/sdk-core/sdk-core-protos/src/lib.rs +175 -57
  102. data/bridge/sdk-core/sdk-core-protos/src/task_token.rs +12 -2
  103. data/bridge/sdk-core/test-utils/Cargo.toml +3 -1
  104. data/bridge/sdk-core/test-utils/src/canned_histories.rs +106 -296
  105. data/bridge/sdk-core/test-utils/src/histfetch.rs +1 -1
  106. data/bridge/sdk-core/test-utils/src/lib.rs +82 -23
  107. data/bridge/sdk-core/test-utils/src/wf_input_saver.rs +50 -0
  108. data/bridge/sdk-core/test-utils/src/workflows.rs +29 -0
  109. data/bridge/sdk-core/tests/fuzzy_workflow.rs +130 -0
  110. data/bridge/sdk-core/tests/{load_tests.rs → heavy_tests.rs} +125 -51
  111. data/bridge/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +25 -3
  112. data/bridge/sdk-core/tests/integ_tests/heartbeat_tests.rs +5 -3
  113. data/bridge/sdk-core/tests/integ_tests/metrics_tests.rs +218 -16
  114. data/bridge/sdk-core/tests/integ_tests/polling_tests.rs +4 -47
  115. data/bridge/sdk-core/tests/integ_tests/queries_tests.rs +5 -128
  116. data/bridge/sdk-core/tests/integ_tests/visibility_tests.rs +83 -25
  117. data/bridge/sdk-core/tests/integ_tests/workflow_tests/activities.rs +93 -69
  118. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -0
  119. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +6 -13
  120. data/bridge/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +1 -0
  121. data/bridge/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +6 -2
  122. data/bridge/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -10
  123. data/bridge/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +72 -191
  124. data/bridge/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +1 -0
  125. data/bridge/sdk-core/tests/integ_tests/workflow_tests/patches.rs +7 -28
  126. data/bridge/sdk-core/tests/integ_tests/workflow_tests/replay.rs +12 -7
  127. data/bridge/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -0
  128. data/bridge/sdk-core/tests/integ_tests/workflow_tests/signals.rs +18 -14
  129. data/bridge/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +6 -20
  130. data/bridge/sdk-core/tests/integ_tests/workflow_tests/timers.rs +10 -21
  131. data/bridge/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +6 -4
  132. data/bridge/sdk-core/tests/integ_tests/workflow_tests.rs +10 -11
  133. data/bridge/sdk-core/tests/main.rs +3 -13
  134. data/bridge/sdk-core/tests/runner.rs +75 -36
  135. data/bridge/sdk-core/tests/wf_input_replay.rs +32 -0
  136. data/bridge/src/connection.rs +41 -25
  137. data/bridge/src/lib.rs +269 -14
  138. data/bridge/src/runtime.rs +1 -1
  139. data/bridge/src/test_server.rs +153 -0
  140. data/bridge/src/worker.rs +89 -16
  141. data/lib/gen/temporal/api/command/v1/message_pb.rb +4 -18
  142. data/lib/gen/temporal/api/common/v1/message_pb.rb +4 -0
  143. data/lib/gen/temporal/api/enums/v1/command_type_pb.rb +1 -3
  144. data/lib/gen/temporal/api/enums/v1/event_type_pb.rb +3 -3
  145. data/lib/gen/temporal/api/enums/v1/failed_cause_pb.rb +2 -0
  146. data/lib/gen/temporal/api/enums/v1/update_pb.rb +6 -4
  147. data/lib/gen/temporal/api/history/v1/message_pb.rb +27 -19
  148. data/lib/gen/temporal/api/namespace/v1/message_pb.rb +1 -0
  149. data/lib/gen/temporal/api/operatorservice/v1/request_response_pb.rb +3 -0
  150. data/lib/gen/temporal/api/protocol/v1/message_pb.rb +30 -0
  151. data/lib/gen/temporal/api/sdk/v1/task_complete_metadata_pb.rb +23 -0
  152. data/lib/gen/temporal/api/testservice/v1/request_response_pb.rb +49 -0
  153. data/lib/gen/temporal/api/testservice/v1/service_pb.rb +21 -0
  154. data/lib/gen/temporal/api/update/v1/message_pb.rb +72 -0
  155. data/lib/gen/temporal/api/workflowservice/v1/request_response_pb.rb +26 -16
  156. data/lib/gen/temporal/sdk/core/activity_result/activity_result_pb.rb +13 -9
  157. data/lib/gen/temporal/sdk/core/activity_task/activity_task_pb.rb +10 -6
  158. data/lib/gen/temporal/sdk/core/child_workflow/child_workflow_pb.rb +13 -9
  159. data/lib/gen/temporal/sdk/core/common/common_pb.rb +7 -3
  160. data/lib/gen/temporal/sdk/core/core_interface_pb.rb +9 -3
  161. data/lib/gen/temporal/sdk/core/external_data/external_data_pb.rb +7 -3
  162. data/lib/gen/temporal/sdk/core/workflow_activation/workflow_activation_pb.rb +27 -21
  163. data/lib/gen/temporal/sdk/core/workflow_commands/workflow_commands_pb.rb +28 -24
  164. data/lib/gen/temporal/sdk/core/workflow_completion/workflow_completion_pb.rb +12 -5
  165. data/lib/temporalio/activity/context.rb +13 -8
  166. data/lib/temporalio/activity/info.rb +1 -1
  167. data/lib/temporalio/bridge/connect_options.rb +15 -0
  168. data/lib/temporalio/bridge/retry_config.rb +24 -0
  169. data/lib/temporalio/bridge/tls_options.rb +19 -0
  170. data/lib/temporalio/client/implementation.rb +8 -8
  171. data/lib/temporalio/connection/retry_config.rb +44 -0
  172. data/lib/temporalio/connection/service.rb +20 -0
  173. data/lib/temporalio/connection/test_service.rb +92 -0
  174. data/lib/temporalio/connection/tls_options.rb +51 -0
  175. data/lib/temporalio/connection/workflow_service.rb +731 -0
  176. data/lib/temporalio/connection.rb +55 -720
  177. data/lib/temporalio/interceptor/activity_inbound.rb +22 -0
  178. data/lib/temporalio/interceptor/activity_outbound.rb +24 -0
  179. data/lib/temporalio/interceptor/chain.rb +5 -5
  180. data/lib/temporalio/interceptor/client.rb +8 -4
  181. data/lib/temporalio/interceptor.rb +22 -0
  182. data/lib/temporalio/retry_policy.rb +13 -3
  183. data/lib/temporalio/testing/time_skipping_handle.rb +32 -0
  184. data/lib/temporalio/testing/time_skipping_interceptor.rb +23 -0
  185. data/lib/temporalio/testing/workflow_environment.rb +112 -0
  186. data/lib/temporalio/testing.rb +175 -0
  187. data/lib/temporalio/version.rb +1 -1
  188. data/lib/temporalio/worker/activity_runner.rb +26 -4
  189. data/lib/temporalio/worker/activity_worker.rb +44 -18
  190. data/lib/temporalio/worker/sync_worker.rb +47 -11
  191. data/lib/temporalio/worker.rb +27 -21
  192. data/lib/temporalio/workflow/async.rb +46 -0
  193. data/lib/temporalio/workflow/future.rb +138 -0
  194. data/lib/temporalio/workflow/info.rb +76 -0
  195. data/temporalio.gemspec +4 -3
  196. metadata +67 -17
  197. data/bridge/sdk-core/Cargo.lock +0 -2606
  198. data/bridge/sdk-core/protos/api_upstream/temporal/api/interaction/v1/message.proto +0 -87
  199. data/lib/bridge.so +0 -0
  200. data/lib/gen/temporal/api/enums/v1/interaction_type_pb.rb +0 -25
  201. data/lib/gen/temporal/api/interaction/v1/message_pb.rb +0 -49
  202. data/lib/gen/temporal/sdk/core/bridge/bridge_pb.rb +0 -222
@@ -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,21 @@ 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
+ HistEventData,
27
+ },
28
+ CommandID, DrivenWorkflow, HistoryUpdate, InternalFlagsRef, LocalResolution,
29
+ OutgoingJob, RunBasics, WFCommand, WFMachinesError, WorkflowFetcher,
30
+ WorkflowStartedInfo,
26
31
  },
27
32
  ExecutingLAId, LocalActRequest, LocalActivityExecutionResult, LocalActivityResolution,
28
33
  },
@@ -31,9 +36,11 @@ use siphasher::sip::SipHasher13;
31
36
  use slotmap::{SlotMap, SparseSecondaryMap};
32
37
  use std::{
33
38
  borrow::{Borrow, BorrowMut},
39
+ cell::RefCell,
34
40
  collections::{HashMap, VecDeque},
35
41
  convert::TryInto,
36
42
  hash::{Hash, Hasher},
43
+ rc::Rc,
37
44
  time::{Duration, Instant, SystemTime},
38
45
  };
39
46
  use temporal_sdk_core_protos::{
@@ -51,6 +58,7 @@ use temporal_sdk_core_protos::{
51
58
  command::v1::{command::Attributes as ProtoCmdAttrs, Command as ProtoCommand},
52
59
  enums::v1::EventType,
53
60
  history::v1::{history_event, HistoryEvent},
61
+ sdk::v1::WorkflowTaskCompletedMetadata,
54
62
  },
55
63
  };
56
64
 
@@ -95,6 +103,9 @@ pub(crate) struct WorkflowMachines {
95
103
  /// The current workflow time if it has been established. This may differ from the WFT start
96
104
  /// time since local activities may advance the clock
97
105
  current_wf_time: Option<SystemTime>,
106
+ /// The internal flags which have been seen so far during this run's execution and thus are
107
+ /// usable during replay.
108
+ observed_internal_flags: InternalFlagsRef,
98
109
 
99
110
  all_machines: SlotMap<MachineKey, Machines>,
100
111
  /// If a machine key is in this map, that machine was created internally by core, not as a
@@ -133,7 +144,7 @@ pub(crate) struct WorkflowMachines {
133
144
  }
134
145
 
135
146
  #[derive(Debug, derive_more::Display)]
136
- #[display(fmt = "Cmd&Machine({})", "command")]
147
+ #[display(fmt = "Cmd&Machine({command})")]
137
148
  struct CommandAndMachine {
138
149
  command: MachineAssociatedCommand,
139
150
  machine: MachineKey,
@@ -142,7 +153,7 @@ struct CommandAndMachine {
142
153
  #[derive(Debug, derive_more::Display)]
143
154
  enum MachineAssociatedCommand {
144
155
  Real(Box<ProtoCommand>),
145
- #[display(fmt = "FakeLocalActivityMarker({})", "_0")]
156
+ #[display(fmt = "FakeLocalActivityMarker({_0})")]
146
157
  FakeLocalActivityMarker(u32),
147
158
  }
148
159
 
@@ -156,7 +167,7 @@ struct ChangeInfo {
156
167
  #[must_use]
157
168
  #[allow(clippy::large_enum_variant)]
158
169
  pub(super) enum MachineResponse {
159
- #[display(fmt = "PushWFJob({})", "_0")]
170
+ #[display(fmt = "PushWFJob({_0})")]
160
171
  PushWFJob(OutgoingJob),
161
172
 
162
173
  /// Pushes a new command into the list that will be sent to server once we respond with the
@@ -165,31 +176,31 @@ pub(super) enum MachineResponse {
165
176
  /// The machine requests the creation of another *different* machine. This acts as if lang
166
177
  /// had replied to the activation with a command, but we use a special set of IDs to avoid
167
178
  /// collisions.
168
- #[display(fmt = "NewCoreOriginatedCommand({:?})", "_0")]
179
+ #[display(fmt = "NewCoreOriginatedCommand({_0:?})")]
169
180
  NewCoreOriginatedCommand(ProtoCmdAttrs),
170
- #[display(fmt = "IssueFakeLocalActivityMarker({})", "_0")]
181
+ #[display(fmt = "IssueFakeLocalActivityMarker({_0})")]
171
182
  IssueFakeLocalActivityMarker(u32),
172
183
  #[display(fmt = "TriggerWFTaskStarted")]
173
184
  TriggerWFTaskStarted {
174
185
  task_started_event_id: i64,
175
186
  time: SystemTime,
176
187
  },
177
- #[display(fmt = "UpdateRunIdOnWorkflowReset({})", run_id)]
188
+ #[display(fmt = "UpdateRunIdOnWorkflowReset({run_id})")]
178
189
  UpdateRunIdOnWorkflowReset { run_id: String },
179
190
 
180
191
  /// Queue a local activity to be processed by the worker
181
192
  #[display(fmt = "QueueLocalActivity")]
182
193
  QueueLocalActivity(ValidScheduleLA),
183
194
  /// Request cancellation of an executing local activity
184
- #[display(fmt = "RequestCancelLocalActivity({})", "_0")]
195
+ #[display(fmt = "RequestCancelLocalActivity({_0})")]
185
196
  RequestCancelLocalActivity(u32),
186
197
  /// Indicates we are abandoning the indicated LA, so we can remove it from "outstanding" LAs
187
198
  /// and we will not try to WFT heartbeat because of it.
188
- #[display(fmt = "AbandonLocalActivity({:?})", "_0")]
199
+ #[display(fmt = "AbandonLocalActivity({_0:?})")]
189
200
  AbandonLocalActivity(u32),
190
201
 
191
202
  /// Set the workflow time to the provided time
192
- #[display(fmt = "UpdateWFTime({:?})", "_0")]
203
+ #[display(fmt = "UpdateWFTime({_0:?})")]
193
204
  UpdateWFTime(Option<SystemTime>),
194
205
  }
195
206
 
@@ -203,25 +214,22 @@ where
203
214
  }
204
215
 
205
216
  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;
217
+ pub(crate) fn new(basics: RunBasics, driven_wf: DrivenWorkflow) -> Self {
218
+ let replaying = basics.history.previous_wft_started_id > 0;
219
+ let mut observed_internal_flags = InternalFlags::new(basics.capabilities);
220
+ // Peek ahead to determine used patches in the first WFT.
221
+ if let Some(attrs) = basics.history.peek_next_wft_completed(0) {
222
+ observed_internal_flags.add_from_complete(attrs);
223
+ };
216
224
  Self {
217
- last_history_from_server: history,
218
- namespace,
219
- workflow_id,
220
- workflow_type,
221
- run_id,
225
+ last_history_from_server: basics.history,
226
+ namespace: basics.namespace,
227
+ workflow_id: basics.workflow_id,
228
+ workflow_type: basics.workflow_type,
229
+ run_id: basics.run_id,
222
230
  drive_me: driven_wf,
223
231
  replaying,
224
- metrics,
232
+ metrics: basics.metrics,
225
233
  // In an ideal world one could say ..Default::default() here and it'd still work.
226
234
  current_started_event_id: 0,
227
235
  next_started_event_id: 0,
@@ -230,6 +238,7 @@ impl WorkflowMachines {
230
238
  workflow_end_time: None,
231
239
  wft_start_time: None,
232
240
  current_wf_time: None,
241
+ observed_internal_flags: Rc::new(RefCell::new(observed_internal_flags)),
233
242
  all_machines: Default::default(),
234
243
  machine_is_core_created: Default::default(),
235
244
  machines_by_event_id: Default::default(),
@@ -255,10 +264,10 @@ impl WorkflowMachines {
255
264
  .and_then(|(st, et)| et.duration_since(st).ok())
256
265
  }
257
266
 
258
- pub(crate) async fn new_history_from_server(&mut self, update: HistoryUpdate) -> Result<()> {
267
+ pub(crate) fn new_history_from_server(&mut self, update: HistoryUpdate) -> Result<()> {
259
268
  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?;
269
+ self.replaying = self.last_history_from_server.previous_wft_started_id > 0;
270
+ self.apply_next_wft_from_history()?;
262
271
  Ok(())
263
272
  }
264
273
 
@@ -290,9 +299,8 @@ impl WorkflowMachines {
290
299
  self.process_machine_responses(mk, resps)?;
291
300
  } else {
292
301
  return Err(WFMachinesError::Nondeterminism(format!(
293
- "Command matching activity with seq num {} existed but was not a \
294
- local activity!",
295
- seq
302
+ "Command matching activity with seq num {seq} existed but was not a \
303
+ local activity!"
296
304
  )));
297
305
  }
298
306
  self.local_activity_data.done_executing(seq);
@@ -350,6 +358,12 @@ impl WorkflowMachines {
350
358
  run_id: self.run_id.clone(),
351
359
  history_length: self.last_processed_event as u32,
352
360
  jobs,
361
+ available_internal_flags: (*self.observed_internal_flags)
362
+ .borrow()
363
+ .all_lang()
364
+ .iter()
365
+ .copied()
366
+ .collect(),
353
367
  }
354
368
  }
355
369
 
@@ -364,10 +378,22 @@ impl WorkflowMachines {
364
378
  .any(|v| v.is_la_resolution)
365
379
  }
366
380
 
381
+ pub(crate) fn get_metadata_for_wft_complete(&self) -> WorkflowTaskCompletedMetadata {
382
+ (*self.observed_internal_flags)
383
+ .borrow_mut()
384
+ .gather_for_wft_complete()
385
+ }
386
+
387
+ pub(crate) fn add_lang_used_flags(&self, flags: Vec<u32>) {
388
+ (*self.observed_internal_flags)
389
+ .borrow_mut()
390
+ .add_lang_used(flags);
391
+ }
392
+
367
393
  /// Iterate the state machines, which consists of grabbing any pending outgoing commands from
368
394
  /// 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;
395
+ pub(crate) fn iterate_machines(&mut self) -> Result<()> {
396
+ let results = self.drive_me.fetch_workflow_iteration_output();
371
397
  self.handle_driven_results(results)?;
372
398
  self.prepare_commands()?;
373
399
  if self.workflow_is_finished() {
@@ -378,10 +404,16 @@ impl WorkflowMachines {
378
404
  Ok(())
379
405
  }
380
406
 
407
+ /// Returns true if machines are ready to apply the next WFT sequence, false if events will need
408
+ /// to be fetched in order to create a complete update with the entire next WFT sequence.
409
+ pub(crate) fn ready_to_apply_next_wft(&self) -> bool {
410
+ self.last_history_from_server
411
+ .can_take_next_wft_sequence(self.current_started_event_id)
412
+ }
413
+
381
414
  /// 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> {
415
+ /// any events that need to be replayed until caught up to the newest WFT.
416
+ pub(crate) fn apply_next_wft_from_history(&mut self) -> Result<usize> {
385
417
  // If we have already seen the terminal event for the entire workflow in a previous WFT,
386
418
  // then we don't need to do anything here, and in fact we need to avoid re-applying the
387
419
  // final WFT.
@@ -389,16 +421,34 @@ impl WorkflowMachines {
389
421
  return Ok(0);
390
422
  }
391
423
 
424
+ // Update observed patches with any that were used in the task
425
+ if let Some(next_complete) = self
426
+ .last_history_from_server
427
+ .peek_next_wft_completed(self.last_processed_event)
428
+ {
429
+ (*self.observed_internal_flags)
430
+ .borrow_mut()
431
+ .add_from_complete(next_complete);
432
+ }
433
+
392
434
  let last_handled_wft_started_id = self.current_started_event_id;
393
- let events = {
394
- let mut evts = self
395
- .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
435
+ let (events, has_final_event) = match self
436
+ .last_history_from_server
437
+ .take_next_wft_sequence(last_handled_wft_started_id)
438
+ {
439
+ NextWFT::ReplayOver => (vec![], true),
440
+ NextWFT::WFT(mut evts, has_final_event) => {
441
+ // Do not re-process events we have already processed
442
+ evts.retain(|e| e.event_id > self.last_processed_event);
443
+ (evts, has_final_event)
444
+ }
445
+ NextWFT::NeedFetch => {
446
+ return Err(WFMachinesError::Fatal(
447
+ "Need to fetch history events to continue applying workflow task, but this \
448
+ should be prevented ahead of time! This is a Core SDK bug."
449
+ .to_string(),
450
+ ));
451
+ }
402
452
  };
403
453
  let num_events_to_process = events.len();
404
454
 
@@ -414,6 +464,7 @@ impl WorkflowMachines {
414
464
  }
415
465
  }
416
466
 
467
+ let mut saw_completed = false;
417
468
  let mut history = events.into_iter().peekable();
418
469
  while let Some(event) = history.next() {
419
470
  if event.event_id != self.last_processed_event + 1 {
@@ -424,17 +475,40 @@ impl WorkflowMachines {
424
475
  }
425
476
  let next_event = history.peek();
426
477
  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;
478
+
479
+ // This definition of replaying here is that we are no longer replaying as soon as we
480
+ // see new events that have never been seen or produced by the SDK.
481
+ //
482
+ // Specifically, replay ends once we have seen the last command-event which was produced
483
+ // as a result of the last completed WFT. Thus, replay would be false for things like
484
+ // signals which were received and after the last completion, and thus generated the
485
+ // current WFT being handled.
486
+ if self.replaying && has_final_event && saw_completed && !event.is_command_event() {
487
+ // Replay is finished
488
+ self.replaying = false;
489
+ }
490
+ if event.event_type() == EventType::WorkflowTaskCompleted {
491
+ saw_completed = true;
432
492
  }
493
+
494
+ self.handle_event(
495
+ HistEventData {
496
+ event,
497
+ replaying: self.replaying,
498
+ current_task_is_last_in_history: has_final_event,
499
+ },
500
+ next_event.is_some() || !has_final_event,
501
+ )?;
502
+ self.last_processed_event = eid;
433
503
  }
434
504
 
435
505
  // Scan through to the next WFT, searching for any patch / la markers, so that we can
436
506
  // pre-resolve them.
437
- for e in self.last_history_from_server.peek_next_wft_sequence() {
507
+ let mut wake_las = vec![];
508
+ for e in self
509
+ .last_history_from_server
510
+ .peek_next_wft_sequence(last_handled_wft_started_id)
511
+ {
438
512
  if let Some((patch_id, _)) = e.get_patch_marker_details() {
439
513
  self.encountered_change_markers.insert(
440
514
  patch_id.clone(),
@@ -448,7 +522,30 @@ impl WorkflowMachines {
448
522
  .into(),
449
523
  );
450
524
  } else if e.is_local_activity_marker() {
451
- self.local_activity_data.process_peekahead_marker(e)?;
525
+ if let Some(la_dat) = e.clone().into_local_activity_marker_details() {
526
+ if let Ok(mk) =
527
+ self.get_machine_key(CommandID::LocalActivity(la_dat.marker_dat.seq))
528
+ {
529
+ wake_las.push((mk, la_dat));
530
+ } else {
531
+ self.local_activity_data.insert_peeked_marker(la_dat);
532
+ }
533
+ } else {
534
+ return Err(WFMachinesError::Fatal(format!(
535
+ "Local activity marker was unparsable: {e:?}"
536
+ )));
537
+ }
538
+ }
539
+ }
540
+ for (mk, la_dat) in wake_las {
541
+ let mach = self.machine_mut(mk);
542
+ if let Machines::LocalActivityMachine(ref mut lam) = *mach {
543
+ if lam.will_accept_resolve_marker() {
544
+ let resps = lam.try_resolve_with_dat(la_dat.into())?;
545
+ self.process_machine_responses(mk, resps)?;
546
+ } else {
547
+ self.local_activity_data.insert_peeked_marker(la_dat);
548
+ }
452
549
  }
453
550
  }
454
551
 
@@ -467,8 +564,9 @@ impl WorkflowMachines {
467
564
  /// event is applied to the machine, which may also return a nondeterminism error if the machine
468
565
  /// does not match the expected type. A fatal error may be returned if the machine is in an
469
566
  /// invalid state.
470
- #[instrument(skip(self, event), fields(event=%event))]
471
- fn handle_event(&mut self, event: HistoryEvent, has_next_event: bool) -> Result<()> {
567
+ #[instrument(skip(self, event_dat), fields(event=%event_dat))]
568
+ fn handle_event(&mut self, event_dat: HistEventData, has_next_event: bool) -> Result<()> {
569
+ let event = &event_dat.event;
472
570
  if event.is_final_wf_execution_event() {
473
571
  self.have_seen_terminal_event = true;
474
572
  }
@@ -486,19 +584,10 @@ impl WorkflowMachines {
486
584
  Ok(())
487
585
  };
488
586
  }
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
587
  if event.event_type() == EventType::Unspecified || event.attributes.is_none() {
498
588
  return if !event.worker_may_ignore {
499
589
  Err(WFMachinesError::Fatal(format!(
500
- "Event type is unspecified! This history is invalid. Event detail: {:?}",
501
- event
590
+ "Event type is unspecified! This history is invalid. Event detail: {event:?}"
502
591
  )))
503
592
  } else {
504
593
  debug!("Event is ignorable");
@@ -507,7 +596,7 @@ impl WorkflowMachines {
507
596
  }
508
597
 
509
598
  if event.is_command_event() {
510
- self.handle_command_event(event)?;
599
+ self.handle_command_event(event_dat)?;
511
600
  return Ok(());
512
601
  }
513
602
 
@@ -517,7 +606,7 @@ impl WorkflowMachines {
517
606
  let maybe_machine = self.machines_by_event_id.remove(&initial_cmd_id);
518
607
  match maybe_machine {
519
608
  Some(sm) => {
520
- self.submachine_handle_event(sm, event, has_next_event)?;
609
+ self.submachine_handle_event(sm, event_dat)?;
521
610
  // Restore machine if not in it's final state
522
611
  if !self.machine(sm).is_final_state() {
523
612
  self.machines_by_event_id.insert(initial_cmd_id, sm);
@@ -526,13 +615,12 @@ impl WorkflowMachines {
526
615
  None => {
527
616
  return Err(WFMachinesError::Nondeterminism(format!(
528
617
  "During event handling, this event had an initial command ID but we \
529
- could not find a matching command for it: {:?}",
530
- event
618
+ could not find a matching command for it: {event:?}"
531
619
  )));
532
620
  }
533
621
  }
534
622
  } else {
535
- self.handle_non_stateful_event(event, has_next_event)?;
623
+ self.handle_non_stateful_event(event_dat)?;
536
624
  }
537
625
 
538
626
  Ok(())
@@ -547,23 +635,23 @@ impl WorkflowMachines {
547
635
  /// The handling consists of verifying that the next command in the commands queue is associated
548
636
  /// with a state machine, which is then notified about the event and the command is removed from
549
637
  /// the commands queue.
550
- fn handle_command_event(&mut self, event: HistoryEvent) -> Result<()> {
638
+ fn handle_command_event(&mut self, event_dat: HistEventData) -> Result<()> {
639
+ let event = &event_dat.event;
551
640
  if event.is_local_activity_marker() {
552
641
  let deets = event.extract_local_activity_marker_data().ok_or_else(|| {
553
- WFMachinesError::Fatal(format!("Local activity marker was unparsable: {:?}", event))
642
+ WFMachinesError::Fatal(format!("Local activity marker was unparsable: {event:?}"))
554
643
  })?;
555
644
  let cmdid = CommandID::LocalActivity(deets.seq);
556
645
  let mkey = self.get_machine_key(cmdid)?;
557
646
  if let Machines::LocalActivityMachine(lam) = self.machine(mkey) {
558
647
  if lam.marker_should_get_special_handling()? {
559
- self.submachine_handle_event(mkey, event, false)?;
648
+ self.submachine_handle_event(mkey, event_dat)?;
560
649
  return Ok(());
561
650
  }
562
651
  } else {
563
652
  return Err(WFMachinesError::Fatal(format!(
564
653
  "Encountered local activity marker but the associated machine was of the \
565
- wrong type! {:?}",
566
- event
654
+ wrong type! {event:?}"
567
655
  )));
568
656
  }
569
657
  }
@@ -573,7 +661,7 @@ impl WorkflowMachines {
573
661
  let consumed_cmd = loop {
574
662
  if let Some(peek_machine) = self.commands.front() {
575
663
  let mach = self.machine(peek_machine.machine);
576
- match change_marker_handling(&event, mach)? {
664
+ match change_marker_handling(event, mach)? {
577
665
  ChangeMarkerOutcome::SkipEvent => return Ok(()),
578
666
  ChangeMarkerOutcome::SkipCommand => {
579
667
  self.commands.pop_front();
@@ -588,8 +676,7 @@ impl WorkflowMachines {
588
676
  c
589
677
  } else {
590
678
  return Err(WFMachinesError::Nondeterminism(format!(
591
- "No command scheduled for event {}",
592
- event
679
+ "No command scheduled for event {event}"
593
680
  )));
594
681
  };
595
682
 
@@ -599,7 +686,7 @@ impl WorkflowMachines {
599
686
 
600
687
  if !canceled_before_sent {
601
688
  // Feed the machine the event
602
- self.submachine_handle_event(command.machine, event, true)?;
689
+ self.submachine_handle_event(command.machine, event_dat)?;
603
690
  break command;
604
691
  }
605
692
  };
@@ -612,23 +699,19 @@ impl WorkflowMachines {
612
699
  Ok(())
613
700
  }
614
701
 
615
- fn handle_non_stateful_event(
616
- &mut self,
617
- event: HistoryEvent,
618
- has_next_event: bool,
619
- ) -> Result<()> {
702
+ fn handle_non_stateful_event(&mut self, event_dat: HistEventData) -> Result<()> {
620
703
  trace!(
621
- event = %event,
704
+ event = %event_dat.event,
622
705
  "handling non-stateful event"
623
706
  );
624
- let event_id = event.event_id;
625
- match EventType::from_i32(event.event_type) {
707
+ let event_id = event_dat.event.event_id;
708
+ match EventType::from_i32(event_dat.event.event_type) {
626
709
  Some(EventType::WorkflowExecutionStarted) => {
627
710
  if let Some(history_event::Attributes::WorkflowExecutionStartedEventAttributes(
628
711
  attrs,
629
- )) = event.attributes
712
+ )) = event_dat.event.attributes
630
713
  {
631
- if let Some(st) = event.event_time.clone() {
714
+ if let Some(st) = event_dat.event.event_time.clone() {
632
715
  let as_systime: SystemTime = st.try_into()?;
633
716
  self.workflow_start_time = Some(as_systime);
634
717
  // Set the workflow time to be the event time of the first event, so that
@@ -640,26 +723,25 @@ impl WorkflowMachines {
640
723
  self.drive_me.start(
641
724
  self.workflow_id.clone(),
642
725
  str_to_randomness_seed(&attrs.original_execution_run_id),
643
- event.event_time.unwrap_or_default(),
726
+ event_dat.event.event_time.unwrap_or_default(),
644
727
  attrs,
645
728
  );
646
729
  } else {
647
730
  return Err(WFMachinesError::Fatal(format!(
648
- "WorkflowExecutionStarted event did not have appropriate attributes: {}",
649
- event
731
+ "WorkflowExecutionStarted event did not have appropriate attributes: {event_dat}"
650
732
  )));
651
733
  }
652
734
  }
653
735
  Some(EventType::WorkflowTaskScheduled) => {
654
736
  let wf_task_sm = WorkflowTaskMachine::new(self.next_started_event_id);
655
737
  let key = self.all_machines.insert(wf_task_sm.into());
656
- self.submachine_handle_event(key, event, has_next_event)?;
738
+ self.submachine_handle_event(key, event_dat)?;
657
739
  self.machines_by_event_id.insert(event_id, key);
658
740
  }
659
741
  Some(EventType::WorkflowExecutionSignaled) => {
660
742
  if let Some(history_event::Attributes::WorkflowExecutionSignaledEventAttributes(
661
743
  attrs,
662
- )) = event.attributes
744
+ )) = event_dat.event.attributes
663
745
  {
664
746
  self.drive_me
665
747
  .send_job(workflow_activation::SignalWorkflow::from(attrs).into());
@@ -672,7 +754,7 @@ impl WorkflowMachines {
672
754
  history_event::Attributes::WorkflowExecutionCancelRequestedEventAttributes(
673
755
  attrs,
674
756
  ),
675
- ) = event.attributes
757
+ ) = event_dat.event.attributes
676
758
  {
677
759
  self.drive_me
678
760
  .send_job(workflow_activation::CancelWorkflow::from(attrs).into());
@@ -682,8 +764,7 @@ impl WorkflowMachines {
682
764
  }
683
765
  _ => {
684
766
  return Err(WFMachinesError::Fatal(format!(
685
- "The event is not a non-stateful event, but we tried to handle it as one: {}",
686
- event
767
+ "The event is not a non-stateful event, but we tried to handle it as one: {event_dat}"
687
768
  )));
688
769
  }
689
770
  }
@@ -700,13 +781,8 @@ impl WorkflowMachines {
700
781
 
701
782
  /// Wrapper for calling [TemporalStateMachine::handle_event] which appropriately takes action
702
783
  /// 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)?;
784
+ fn submachine_handle_event(&mut self, sm: MachineKey, event: HistEventData) -> Result<()> {
785
+ let machine_responses = self.machine_mut(sm).handle_event(event)?;
710
786
  self.process_machine_responses(sm, machine_responses)?;
711
787
  Ok(())
712
788
  }
@@ -756,7 +832,7 @@ impl WorkflowMachines {
756
832
  ) -> Result<()> {
757
833
  let sm = self.machine(smk);
758
834
  if !machine_responses.is_empty() {
759
- debug!(responses = %machine_responses.display(), machine_name = %sm.name(),
835
+ trace!(responses = %machine_responses.display(), machine_name = %sm.name(),
760
836
  "Machine produced responses");
761
837
  }
762
838
  self.process_machine_resps_impl(smk, machine_responses)
@@ -811,8 +887,7 @@ impl WorkflowMachines {
811
887
  }
812
888
  c => {
813
889
  return Err(WFMachinesError::Fatal(format!(
814
- "A machine requested to create a new command of an unsupported type: {:?}",
815
- c
890
+ "A machine requested to create a new command of an unsupported type: {c:?}"
816
891
  )))
817
892
  }
818
893
  },
@@ -925,7 +1000,10 @@ impl WorkflowMachines {
925
1000
  }
926
1001
  WFCommand::AddActivity(attrs) => {
927
1002
  let seq = attrs.seq;
928
- self.add_cmd_to_wf_task(new_activity(attrs), CommandID::Activity(seq).into());
1003
+ self.add_cmd_to_wf_task(
1004
+ ActivityMachine::new_scheduled(attrs, self.observed_internal_flags.clone()),
1005
+ CommandID::Activity(seq).into(),
1006
+ );
929
1007
  }
930
1008
  WFCommand::AddLocalActivity(attrs) => {
931
1009
  let seq = attrs.seq;
@@ -937,8 +1015,7 @@ impl WorkflowMachines {
937
1015
  )
938
1016
  .map_err(|e| {
939
1017
  WFMachinesError::Fatal(format!(
940
- "Invalid schedule local activity request (seq {}): {}",
941
- seq, e
1018
+ "Invalid schedule local activity request (seq {seq}): {e}"
942
1019
  ))
943
1020
  })?;
944
1021
  let (la, mach_resp) = new_local_activity(
@@ -946,6 +1023,7 @@ impl WorkflowMachines {
946
1023
  self.replaying,
947
1024
  self.local_activity_data.take_preresolution(seq),
948
1025
  self.current_wf_time,
1026
+ self.observed_internal_flags.clone(),
949
1027
  )?;
950
1028
  let machkey = self.all_machines.insert(la.into());
951
1029
  self.id_to_machine
@@ -1001,7 +1079,10 @@ impl WorkflowMachines {
1001
1079
  WFCommand::AddChildWorkflow(attrs) => {
1002
1080
  let seq = attrs.seq;
1003
1081
  self.add_cmd_to_wf_task(
1004
- new_child_workflow(attrs),
1082
+ ChildWorkflowMachine::new_scheduled(
1083
+ attrs,
1084
+ self.observed_internal_flags.clone(),
1085
+ ),
1005
1086
  CommandID::ChildWorkflowStart(seq).into(),
1006
1087
  );
1007
1088
  }
@@ -1075,7 +1156,7 @@ impl WorkflowMachines {
1075
1156
 
1076
1157
  fn get_machine_key(&self, id: CommandID) -> Result<MachineKey> {
1077
1158
  Ok(*self.id_to_machine.get(&id).ok_or_else(|| {
1078
- WFMachinesError::Fatal(format!("Missing associated machine for {:?}", id))
1159
+ WFMachinesError::Fatal(format!("Missing associated machine for {id:?}"))
1079
1160
  })?)
1080
1161
  }
1081
1162
 
@@ -1173,9 +1254,8 @@ fn change_marker_handling(event: &HistoryEvent, mach: &Machines) -> Result<Chang
1173
1254
  return Ok(ChangeMarkerOutcome::SkipEvent);
1174
1255
  }
1175
1256
  return Err(WFMachinesError::Nondeterminism(format!(
1176
- "Non-deprecated patch marker encountered for change {}, \
1177
- but there is no corresponding change command!",
1178
- patch_name
1257
+ "Non-deprecated patch marker encountered for change {patch_name}, \
1258
+ but there is no corresponding change command!"
1179
1259
  )));
1180
1260
  }
1181
1261
  // Patch machines themselves may also not *have* matching markers, where non-deprecated