@temporalio/core-bridge 1.8.5 → 1.9.0-rc.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 (196) hide show
  1. package/Cargo.lock +189 -152
  2. package/Cargo.toml +1 -0
  3. package/lib/index.d.ts +17 -44
  4. package/lib/index.js.map +1 -1
  5. package/package.json +3 -4
  6. package/releases/aarch64-apple-darwin/index.node +0 -0
  7. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  8. package/releases/x86_64-apple-darwin/index.node +0 -0
  9. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  10. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  11. package/sdk-core/.github/workflows/heavy.yml +4 -0
  12. package/sdk-core/.github/workflows/per-pr.yml +96 -0
  13. package/sdk-core/ARCHITECTURE.md +1 -1
  14. package/sdk-core/Cargo.toml +6 -0
  15. package/sdk-core/README.md +37 -21
  16. package/sdk-core/client/Cargo.toml +6 -3
  17. package/sdk-core/client/src/lib.rs +272 -138
  18. package/sdk-core/client/src/metrics.rs +68 -57
  19. package/sdk-core/client/src/raw.rs +191 -45
  20. package/sdk-core/client/src/retry.rs +20 -0
  21. package/sdk-core/client/src/worker_registry/mod.rs +264 -0
  22. package/sdk-core/client/src/workflow_handle/mod.rs +2 -1
  23. package/sdk-core/core/Cargo.toml +16 -18
  24. package/sdk-core/core/src/core_tests/child_workflows.rs +7 -7
  25. package/sdk-core/core/src/core_tests/mod.rs +1 -0
  26. package/sdk-core/core/src/core_tests/replay_flag.rs +29 -39
  27. package/sdk-core/core/src/core_tests/updates.rs +73 -0
  28. package/sdk-core/core/src/core_tests/workflow_tasks.rs +52 -1
  29. package/sdk-core/core/src/ephemeral_server/mod.rs +34 -11
  30. package/sdk-core/core/src/internal_flags.rs +7 -1
  31. package/sdk-core/core/src/lib.rs +19 -36
  32. package/sdk-core/core/src/protosext/mod.rs +11 -3
  33. package/sdk-core/core/src/protosext/protocol_messages.rs +102 -0
  34. package/sdk-core/core/src/replay/mod.rs +100 -48
  35. package/sdk-core/core/src/telemetry/log_export.rs +161 -28
  36. package/sdk-core/core/src/telemetry/metrics.rs +869 -248
  37. package/sdk-core/core/src/telemetry/mod.rs +135 -239
  38. package/sdk-core/core/src/telemetry/prometheus_server.rs +36 -31
  39. package/sdk-core/core/src/test_help/mod.rs +63 -4
  40. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +12 -2
  41. package/sdk-core/core/src/worker/activities.rs +276 -10
  42. package/sdk-core/core/src/worker/client/mocks.rs +18 -0
  43. package/sdk-core/core/src/worker/client.rs +16 -3
  44. package/sdk-core/core/src/worker/mod.rs +50 -19
  45. package/sdk-core/core/src/worker/slot_provider.rs +175 -0
  46. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +27 -34
  47. package/sdk-core/core/src/worker/workflow/history_update.rs +4 -1
  48. package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +36 -94
  49. package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +34 -22
  50. package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +50 -34
  51. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +106 -92
  52. package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +22 -21
  53. package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +386 -499
  54. package/sdk-core/core/src/worker/workflow/machines/mod.rs +12 -2
  55. package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +33 -26
  56. package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +198 -215
  57. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +66 -62
  58. package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +88 -119
  59. package/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +3 -1
  60. package/sdk-core/core/src/worker/workflow/machines/update_state_machine.rs +411 -0
  61. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +26 -25
  62. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +302 -85
  63. package/sdk-core/core/src/worker/workflow/managed_run.rs +179 -132
  64. package/sdk-core/core/src/worker/workflow/mod.rs +121 -46
  65. package/sdk-core/core/src/worker/workflow/run_cache.rs +8 -12
  66. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +45 -38
  67. package/sdk-core/core-api/Cargo.toml +7 -6
  68. package/sdk-core/core-api/src/lib.rs +4 -12
  69. package/sdk-core/core-api/src/telemetry/metrics.rs +334 -0
  70. package/sdk-core/core-api/src/telemetry.rs +53 -42
  71. package/sdk-core/core-api/src/worker.rs +7 -0
  72. package/sdk-core/{.buildkite/docker → docker}/docker-compose.yaml +1 -1
  73. package/sdk-core/etc/dynamic-config.yaml +11 -1
  74. package/sdk-core/fsm/rustfsm_procmacro/Cargo.toml +1 -1
  75. package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +1 -3
  76. package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +2 -2
  77. package/sdk-core/sdk/Cargo.toml +1 -1
  78. package/sdk-core/sdk/src/lib.rs +85 -7
  79. package/sdk-core/sdk/src/workflow_context/options.rs +4 -0
  80. package/sdk-core/sdk/src/workflow_context.rs +43 -15
  81. package/sdk-core/sdk/src/workflow_future.rs +334 -204
  82. package/sdk-core/sdk-core-protos/Cargo.toml +2 -2
  83. package/sdk-core/sdk-core-protos/build.rs +14 -14
  84. package/sdk-core/sdk-core-protos/protos/api_upstream/.buildkite/Dockerfile +2 -0
  85. package/sdk-core/sdk-core-protos/protos/api_upstream/Makefile +99 -0
  86. package/sdk-core/sdk-core-protos/protos/api_upstream/api-linter.yaml +56 -0
  87. package/sdk-core/sdk-core-protos/protos/api_upstream/buf.gen.yaml +20 -0
  88. package/sdk-core/sdk-core-protos/protos/api_upstream/buf.lock +11 -0
  89. package/sdk-core/sdk-core-protos/protos/api_upstream/buf.yaml +18 -0
  90. package/sdk-core/sdk-core-protos/protos/api_upstream/google/api/annotations.proto +31 -0
  91. package/sdk-core/sdk-core-protos/protos/api_upstream/google/api/http.proto +379 -0
  92. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/any.proto +162 -0
  93. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/descriptor.proto +1212 -0
  94. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/duration.proto +115 -0
  95. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/empty.proto +51 -0
  96. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/timestamp.proto +144 -0
  97. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/wrappers.proto +123 -0
  98. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/batch/v1/message.proto +3 -5
  99. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/command/v1/message.proto +11 -13
  100. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/common/v1/message.proto +2 -4
  101. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/failed_cause.proto +2 -0
  102. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/reset.proto +1 -1
  103. package/sdk-core/{protos/api_upstream/build/tools.go → sdk-core-protos/protos/api_upstream/temporal/api/export/v1/message.proto} +22 -6
  104. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/filter/v1/message.proto +2 -4
  105. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/history/v1/message.proto +21 -23
  106. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/namespace/v1/message.proto +2 -4
  107. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/operatorservice/v1/request_response.proto +2 -0
  108. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/operatorservice/v1/service.proto +4 -0
  109. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/replication/v1/message.proto +1 -3
  110. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/schedule/v1/message.proto +36 -20
  111. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +13 -0
  112. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/taskqueue/v1/message.proto +2 -4
  113. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/update/v1/message.proto +1 -1
  114. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/version/v1/message.proto +2 -3
  115. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/workflow/v1/message.proto +18 -20
  116. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/workflowservice/v1/request_response.proto +84 -32
  117. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/workflowservice/v1/service.proto +205 -47
  118. package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +57 -0
  119. package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +27 -0
  120. package/sdk-core/sdk-core-protos/src/history_builder.rs +67 -2
  121. package/sdk-core/sdk-core-protos/src/lib.rs +75 -2
  122. package/sdk-core/sdk-core-protos/src/utilities.rs +14 -0
  123. package/sdk-core/test-utils/Cargo.toml +5 -1
  124. package/sdk-core/test-utils/src/canned_histories.rs +3 -57
  125. package/sdk-core/test-utils/src/interceptors.rs +46 -0
  126. package/sdk-core/test-utils/src/lib.rs +106 -38
  127. package/sdk-core/tests/integ_tests/metrics_tests.rs +110 -15
  128. package/sdk-core/tests/integ_tests/queries_tests.rs +174 -3
  129. package/sdk-core/tests/integ_tests/update_tests.rs +908 -0
  130. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +44 -1
  131. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -1
  132. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -1
  133. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +4 -4
  134. package/sdk-core/tests/integ_tests/workflow_tests/eager.rs +61 -0
  135. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +27 -2
  136. package/sdk-core/tests/integ_tests/workflow_tests.rs +1 -0
  137. package/sdk-core/tests/main.rs +2 -1
  138. package/sdk-core/tests/runner.rs +15 -2
  139. package/src/conversions.rs +75 -89
  140. package/src/helpers.rs +74 -0
  141. package/src/runtime.rs +17 -6
  142. package/src/worker.rs +14 -61
  143. package/ts/index.ts +21 -52
  144. package/sdk-core/.buildkite/docker/Dockerfile +0 -9
  145. package/sdk-core/.buildkite/docker/build.sh +0 -5
  146. package/sdk-core/.buildkite/docker/docker-compose-ci.yaml +0 -27
  147. package/sdk-core/.buildkite/pipeline.yml +0 -57
  148. package/sdk-core/.github/workflows/semgrep.yml +0 -25
  149. package/sdk-core/core/src/worker/workflow/bridge.rs +0 -35
  150. package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +0 -215
  151. package/sdk-core/protos/api_upstream/.buildkite/Dockerfile +0 -2
  152. package/sdk-core/protos/api_upstream/Makefile +0 -80
  153. package/sdk-core/protos/api_upstream/api-linter.yaml +0 -40
  154. package/sdk-core/protos/api_upstream/buf.yaml +0 -9
  155. package/sdk-core/protos/api_upstream/build/go.mod +0 -7
  156. package/sdk-core/protos/api_upstream/build/go.sum +0 -5
  157. package/sdk-core/protos/api_upstream/go.mod +0 -6
  158. package/sdk-core/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +0 -141
  159. /package/sdk-core/{.buildkite/docker → docker}/docker-compose-telem.yaml +0 -0
  160. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.buildkite/docker-compose.yml +0 -0
  161. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.buildkite/pipeline.yml +0 -0
  162. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.github/CODEOWNERS +0 -0
  163. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  164. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.github/workflows/publish-docs.yml +0 -0
  165. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.github/workflows/trigger-api-go-update.yml +0 -0
  166. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/LICENSE +0 -0
  167. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/README.md +0 -0
  168. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/batch_operation.proto +0 -0
  169. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/command_type.proto +0 -0
  170. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/common.proto +0 -0
  171. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/event_type.proto +0 -0
  172. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/namespace.proto +0 -0
  173. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/query.proto +0 -0
  174. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/schedule.proto +0 -0
  175. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/task_queue.proto +0 -0
  176. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/update.proto +0 -0
  177. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/workflow.proto +0 -0
  178. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/errordetails/v1/message.proto +0 -0
  179. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/failure/v1/message.proto +0 -0
  180. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/protocol/v1/message.proto +0 -0
  181. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/query/v1/message.proto +0 -0
  182. /package/sdk-core/{protos → sdk-core-protos/protos}/google/rpc/status.proto +0 -0
  183. /package/sdk-core/{protos → sdk-core-protos/protos}/grpc/health/v1/health.proto +0 -0
  184. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/activity_result/activity_result.proto +0 -0
  185. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/activity_task/activity_task.proto +0 -0
  186. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/child_workflow/child_workflow.proto +0 -0
  187. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/common/common.proto +0 -0
  188. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/core_interface.proto +0 -0
  189. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/external_data/external_data.proto +0 -0
  190. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +0 -0
  191. /package/sdk-core/{protos → sdk-core-protos/protos}/testsrv_upstream/Makefile +0 -0
  192. /package/sdk-core/{protos → sdk-core-protos/protos}/testsrv_upstream/api-linter.yaml +0 -0
  193. /package/sdk-core/{protos → sdk-core-protos/protos}/testsrv_upstream/buf.yaml +0 -0
  194. /package/sdk-core/{protos/api_upstream → sdk-core-protos/protos/testsrv_upstream}/dependencies/gogoproto/gogo.proto +0 -0
  195. /package/sdk-core/{protos → sdk-core-protos/protos}/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +0 -0
  196. /package/sdk-core/{protos → sdk-core-protos/protos}/testsrv_upstream/temporal/api/testservice/v1/service.proto +0 -0
@@ -13,8 +13,12 @@ use super::{
13
13
  TemporalStateMachine,
14
14
  };
15
15
  use crate::{
16
+ abstractions::dbg_panic,
16
17
  internal_flags::InternalFlags,
17
- protosext::{HistoryEventExt, ValidScheduleLA},
18
+ protosext::{
19
+ protocol_messages::{IncomingProtocolMessage, IncomingProtocolMessageBody},
20
+ CompleteLocalActivityData, HistoryEventExt, ValidScheduleLA,
21
+ },
18
22
  telemetry::{metrics::MetricsContext, VecDisplayer},
19
23
  worker::{
20
24
  workflow::{
@@ -23,13 +27,12 @@ use crate::{
23
27
  activity_state_machine::ActivityMachine,
24
28
  child_workflow_state_machine::ChildWorkflowMachine,
25
29
  modify_workflow_properties_state_machine::modify_workflow_properties,
26
- patch_state_machine::VERSION_SEARCH_ATTR_KEY,
30
+ patch_state_machine::VERSION_SEARCH_ATTR_KEY, update_state_machine::UpdateMachine,
27
31
  upsert_search_attributes_state_machine::upsert_search_attrs_internal,
28
32
  HistEventData,
29
33
  },
30
34
  CommandID, DrivenWorkflow, HistoryUpdate, InternalFlagsRef, LocalResolution,
31
- OutgoingJob, RunBasics, WFCommand, WFMachinesError, WorkflowFetcher,
32
- WorkflowStartedInfo,
35
+ OutgoingJob, RunBasics, WFCommand, WFMachinesError, WorkflowStartedInfo,
33
36
  },
34
37
  ExecutingLAId, LocalActRequest, LocalActivityExecutionResult, LocalActivityResolution,
35
38
  },
@@ -37,11 +40,11 @@ use crate::{
37
40
  use siphasher::sip::SipHasher13;
38
41
  use slotmap::{SlotMap, SparseSecondaryMap};
39
42
  use std::{
40
- borrow::{Borrow, BorrowMut},
41
43
  cell::RefCell,
42
44
  collections::{HashMap, VecDeque},
43
45
  convert::TryInto,
44
46
  hash::{Hash, Hasher},
47
+ iter::Peekable,
45
48
  rc::Rc,
46
49
  time::{Duration, Instant, SystemTime},
47
50
  };
@@ -59,7 +62,8 @@ use temporal_sdk_core_protos::{
59
62
  temporal::api::{
60
63
  command::v1::{command::Attributes as ProtoCmdAttrs, Command as ProtoCommand},
61
64
  enums::v1::EventType,
62
- history::v1::{history_event, history_event::Attributes, HistoryEvent},
65
+ history::v1::{history_event, HistoryEvent},
66
+ protocol::v1::{message::SequencingId, Message as ProtocolMessage},
63
67
  sdk::v1::WorkflowTaskCompletedMetadata,
64
68
  },
65
69
  };
@@ -75,6 +79,8 @@ pub(crate) struct WorkflowMachines {
75
79
  /// kept because the lang side polls & completes for every workflow task, but we do not need
76
80
  /// to poll the server that often during replay.
77
81
  last_history_from_server: HistoryUpdate,
82
+ /// Protocol messages that have yet to be processed for the current WFT.
83
+ protocol_msgs: Vec<IncomingProtocolMessage>,
78
84
  /// EventId of the last handled WorkflowTaskStarted event
79
85
  current_started_event_id: i64,
80
86
  /// The event id of the next workflow task started event that the machines need to process.
@@ -88,7 +94,7 @@ pub(crate) struct WorkflowMachines {
88
94
  /// True if the workflow is replaying from history
89
95
  pub replaying: bool,
90
96
  /// Namespace this workflow exists in
91
- pub namespace: String,
97
+ namespace: String,
92
98
  /// Workflow identifier
93
99
  pub workflow_id: String,
94
100
  /// Workflow type identifier. (Function name, class, etc)
@@ -96,7 +102,7 @@ pub(crate) struct WorkflowMachines {
96
102
  /// Identifies the current run
97
103
  pub run_id: String,
98
104
  /// The task queue this workflow is operating within
99
- pub task_queue: String,
105
+ task_queue: String,
100
106
  /// Is set to true once we've seen the final event in workflow history, to avoid accidentally
101
107
  /// re-applying the final workflow task.
102
108
  pub have_seen_terminal_event: bool,
@@ -117,16 +123,19 @@ pub(crate) struct WorkflowMachines {
117
123
  history_size_bytes: u64,
118
124
  /// Set on each WFT started event
119
125
  continue_as_new_suggested: bool,
126
+ /// Set if the current WFT is already complete and that completion event had a build id in it.
127
+ current_wft_build_id: Option<String>,
120
128
 
121
129
  all_machines: SlotMap<MachineKey, Machines>,
122
130
  /// If a machine key is in this map, that machine was created internally by core, not as a
123
131
  /// command from lang.
124
132
  machine_is_core_created: SparseSecondaryMap<MachineKey, ()>,
125
-
126
133
  /// A mapping for accessing machines associated to a particular event, where the key is the id
127
134
  /// of the initiating event for that machine.
128
135
  machines_by_event_id: HashMap<i64, MachineKey>,
129
-
136
+ /// A mapping for accessing machines that were created as a result of protocol messages. The
137
+ /// key is the protocol's instance id.
138
+ machines_by_protocol_instance_id: HashMap<String, MachineKey>,
130
139
  /// Maps command ids as created by workflow authors to their associated machines.
131
140
  id_to_machine: HashMap<CommandID, MachineKey>,
132
141
 
@@ -136,6 +145,9 @@ pub(crate) struct WorkflowMachines {
136
145
  /// Commands generated by the currently processing workflow task, which will eventually be
137
146
  /// transferred to `commands` (and hence eventually sent to the server)
138
147
  current_wf_task_commands: VecDeque<CommandAndMachine>,
148
+ /// Messages generated while processing the current workflow task, which will be sent in the
149
+ /// next WFT response. Keyed by message id.
150
+ message_outbox: VecDeque<ProtocolMessage>,
139
151
 
140
152
  /// Information about patch markers we have already seen while replaying history
141
153
  encountered_patch_markers: HashMap<String, ChangeInfo>,
@@ -180,6 +192,9 @@ pub(super) enum MachineResponse {
180
192
  /// Pushes a new command into the list that will be sent to server once we respond with the
181
193
  /// workflow task completion
182
194
  IssueNewCommand(ProtoCommand),
195
+ /// Pushes a new protocol message into the list that will be sent to server once we respond with
196
+ /// the workflow task completion
197
+ IssueNewMessage(ProtocolMessage),
183
198
  /// The machine requests the creation of another *different* machine. This acts as if lang
184
199
  /// had replied to the activation with a command, but we use a special set of IDs to avoid
185
200
  /// collisions.
@@ -230,6 +245,7 @@ impl WorkflowMachines {
230
245
  };
231
246
  Self {
232
247
  last_history_from_server: basics.history,
248
+ protocol_msgs: vec![],
233
249
  namespace: basics.namespace,
234
250
  workflow_id: basics.workflow_id,
235
251
  workflow_type: basics.workflow_type,
@@ -249,12 +265,15 @@ impl WorkflowMachines {
249
265
  observed_internal_flags: Rc::new(RefCell::new(observed_internal_flags)),
250
266
  history_size_bytes: 0,
251
267
  continue_as_new_suggested: false,
268
+ current_wft_build_id: None,
252
269
  all_machines: Default::default(),
253
270
  machine_is_core_created: Default::default(),
254
271
  machines_by_event_id: Default::default(),
272
+ machines_by_protocol_instance_id: Default::default(),
255
273
  id_to_machine: Default::default(),
256
274
  commands: Default::default(),
257
275
  current_wf_task_commands: Default::default(),
276
+ message_outbox: Default::default(),
258
277
  encountered_patch_markers: Default::default(),
259
278
  local_activity_data: LocalActivityData::default(),
260
279
  have_seen_terminal_event: false,
@@ -274,6 +293,20 @@ impl WorkflowMachines {
274
293
  .and_then(|(st, et)| et.duration_since(st).ok())
275
294
  }
276
295
 
296
+ /// Must be called every time a new WFT is received
297
+ pub(crate) fn new_work_from_server(
298
+ &mut self,
299
+ update: HistoryUpdate,
300
+ protocol_messages: Vec<IncomingProtocolMessage>,
301
+ ) -> Result<()> {
302
+ if !self.protocol_msgs.is_empty() {
303
+ dbg_panic!("There are unprocessed protocol messages while receiving new work");
304
+ }
305
+ self.protocol_msgs = protocol_messages;
306
+ self.new_history_from_server(update)?;
307
+ Ok(())
308
+ }
309
+
277
310
  pub(crate) fn new_history_from_server(&mut self, update: HistoryUpdate) -> Result<()> {
278
311
  self.last_history_from_server = update;
279
312
  self.replaying = self.last_history_from_server.previous_wft_started_id > 0;
@@ -335,28 +368,40 @@ impl WorkflowMachines {
335
368
  self.drive_me.get_started_info()
336
369
  }
337
370
 
371
+ pub(crate) fn get_last_wft_started_id(&self) -> i64 {
372
+ self.current_started_event_id
373
+ }
374
+
375
+ pub(crate) fn prepare_for_wft_response(&mut self) -> MachinesWFTResponseContent {
376
+ MachinesWFTResponseContent {
377
+ replaying: self.replaying,
378
+ has_pending_jobs: self.has_pending_jobs(),
379
+ have_seen_terminal_event: self.have_seen_terminal_event,
380
+ have_pending_la_resolutions: self.has_pending_la_resolutions(),
381
+ last_processed_event: self.last_processed_event,
382
+ me: self,
383
+ }
384
+ }
385
+
338
386
  /// Fetches commands which are ready for processing from the state machines, generally to be
339
387
  /// sent off to the server. They are not removed from the internal queue, that happens when
340
388
  /// corresponding history events from the server are being handled.
341
- pub(crate) fn get_commands(&self) -> Vec<ProtoCommand> {
389
+ pub(crate) fn get_commands(&self) -> impl Iterator<Item = ProtoCommand> + '_ {
342
390
  // Since we're about to write a WFT, record any internal flags we know about which aren't
343
391
  // already recorded.
344
392
  (*self.observed_internal_flags)
345
393
  .borrow_mut()
346
394
  .write_all_known();
347
- self.commands
348
- .iter()
349
- .filter_map(|c| {
350
- if !self.machine(c.machine).is_final_state() {
351
- match &c.command {
352
- MachineAssociatedCommand::Real(cmd) => Some((**cmd).clone()),
353
- MachineAssociatedCommand::FakeLocalActivityMarker(_) => None,
354
- }
355
- } else {
356
- None
395
+ self.commands.iter().filter_map(|c| {
396
+ if !self.machine(c.machine).is_final_state() {
397
+ match &c.command {
398
+ MachineAssociatedCommand::Real(cmd) => Some((**cmd).clone()),
399
+ MachineAssociatedCommand::FakeLocalActivityMarker(_) => None,
357
400
  }
358
- })
359
- .collect()
401
+ } else {
402
+ None
403
+ }
404
+ })
360
405
  }
361
406
 
362
407
  /// Returns the next activation that needs to be performed by the lang sdk. Things like unblock
@@ -387,6 +432,7 @@ impl WorkflowMachines {
387
432
  .collect(),
388
433
  history_size_bytes: self.history_size_bytes,
389
434
  continue_as_new_suggested: self.continue_as_new_suggested,
435
+ build_id_for_current_task: self.current_wft_build_id.clone().unwrap_or_default(),
390
436
  }
391
437
  }
392
438
 
@@ -413,6 +459,30 @@ impl WorkflowMachines {
413
459
  .add_lang_used(flags);
414
460
  }
415
461
 
462
+ /// Undo a speculative workflow task by resetting to a certain WFT Started ID. This can happen
463
+ /// when an update request is rejected.
464
+ pub(crate) fn reset_last_started_id(&mut self, id: i64) {
465
+ debug!("Resetting back to event id {} due to speculative WFT", id);
466
+ self.current_started_event_id = id;
467
+ // This is pretty nasty to just + 1 like this, but, we know WFT complete always follows
468
+ // WFT started, which the id given to us to reset to must be, and we need to avoid
469
+ // re-applying the WFT Completed event, so we make sure to consider that processed
470
+ self.last_processed_event = id + 1;
471
+ // Then, we have to drop any state machines (which should only be one workflow task machine)
472
+ // we may have created when servicing the speculative task.
473
+ // Remove when https://github.com/rust-lang/rust/issues/59618 is stable
474
+ let remove_these: Vec<_> = self
475
+ .machines_by_event_id
476
+ .iter()
477
+ .filter(|(mid, _)| **mid > id)
478
+ .map(|(mid, mkey)| (*mid, *mkey))
479
+ .collect();
480
+ for (mid, mkey) in remove_these {
481
+ self.machines_by_event_id.remove(&mid);
482
+ self.all_machines.remove(mkey);
483
+ }
484
+ }
485
+
416
486
  /// Iterate the state machines, which consists of grabbing any pending outgoing commands from
417
487
  /// the workflow code, handling them, and preparing them to be sent off to the server.
418
488
  pub(crate) fn iterate_machines(&mut self) -> Result<()> {
@@ -446,22 +516,45 @@ impl WorkflowMachines {
446
516
  return Ok(0);
447
517
  }
448
518
 
449
- fn update_internal_flags(me: &mut WorkflowMachines) {
450
- // Update observed patches with any that were used in the task
451
- if let Some(next_complete) = me
452
- .last_history_from_server
453
- .peek_next_wft_completed(me.last_processed_event)
454
- {
455
- (*me.observed_internal_flags)
456
- .borrow_mut()
457
- .add_from_complete(next_complete);
458
- }
519
+ fn get_processable_messages(
520
+ me: &mut WorkflowMachines,
521
+ for_event_id: i64,
522
+ ) -> Vec<IncomingProtocolMessage> {
523
+ // Another thing to replace when `drain_filter` exists
524
+ let mut ret = vec![];
525
+ me.protocol_msgs = std::mem::take(&mut me.protocol_msgs)
526
+ .into_iter()
527
+ .filter_map(|x| {
528
+ if x.processable_after_event_id()
529
+ .is_some_and(|eid| eid <= for_event_id)
530
+ {
531
+ ret.push(x);
532
+ None
533
+ } else {
534
+ Some(x)
535
+ }
536
+ })
537
+ .collect();
538
+ ret
459
539
  }
460
540
 
461
- // We update the internal flags before applying the current task (peeking to the completion
462
- // of this task), and also at the end (peeking to the completion of the task that lang is
463
- // about to generate commands for, and for which we will want those flags active).
464
- update_internal_flags(self);
541
+ // Peek to the next WFT complete and update ourselves with data we might need in it.
542
+ if let Some(next_complete) = self
543
+ .last_history_from_server
544
+ .peek_next_wft_completed(self.last_processed_event)
545
+ {
546
+ // We update the internal flags before applying the current task
547
+ (*self.observed_internal_flags)
548
+ .borrow_mut()
549
+ .add_from_complete(next_complete);
550
+ // Save this tasks' Build ID if it had one
551
+ if let Some(bid) = next_complete.worker_version.as_ref().map(|wv| &wv.build_id) {
552
+ self.current_wft_build_id = Some(bid.to_string());
553
+ } else {
554
+ // Otherwise we do not want to keep anything previously stored
555
+ self.current_wft_build_id = None;
556
+ }
557
+ }
465
558
 
466
559
  let last_handled_wft_started_id = self.current_started_event_id;
467
560
  let (events, has_final_event) = match self
@@ -499,14 +592,14 @@ impl WorkflowMachines {
499
592
  let mut do_handle_event = true;
500
593
  let mut history = events.into_iter().peekable();
501
594
  while let Some(event) = history.next() {
502
- if event.event_id != self.last_processed_event + 1 {
595
+ let eid = event.event_id;
596
+ if eid != self.last_processed_event + 1 {
503
597
  return Err(WFMachinesError::Fatal(format!(
504
598
  "History is out of order. Last processed event: {}, event id: {}",
505
- self.last_processed_event, event.event_id
599
+ self.last_processed_event, eid
506
600
  )));
507
601
  }
508
602
  let next_event = history.peek();
509
- let eid = event.event_id;
510
603
 
511
604
  // This definition of replaying here is that we are no longer replaying as soon as we
512
605
  // see new events that have never been seen or produced by the SDK.
@@ -520,7 +613,7 @@ impl WorkflowMachines {
520
613
  // them.
521
614
  if self.replaying
522
615
  && has_final_event
523
- && event.event_id > self.last_history_from_server.previous_wft_started_id
616
+ && eid > self.last_history_from_server.previous_wft_started_id
524
617
  && event.event_type() != EventType::WorkflowTaskCompleted
525
618
  && !event.is_command_event()
526
619
  {
@@ -528,6 +621,12 @@ impl WorkflowMachines {
528
621
  self.replaying = false;
529
622
  }
530
623
 
624
+ // Process any messages that should be processed before the event we're about to handle
625
+ let processable_msgs = get_processable_messages(self, eid - 1);
626
+ for msg in processable_msgs {
627
+ self.handle_protocol_message(msg)?;
628
+ }
629
+
531
630
  if do_handle_event {
532
631
  let eho = self.handle_event(
533
632
  HistEventData {
@@ -551,9 +650,14 @@ impl WorkflowMachines {
551
650
  self.last_processed_event = eid;
552
651
  }
553
652
 
653
+ // Needed to delay mutation of self until after we've iterated over peeked events.
654
+ enum DelayedAction {
655
+ WakeLa(MachineKey, CompleteLocalActivityData),
656
+ ProtocolMessage(IncomingProtocolMessage),
657
+ }
658
+ let mut delayed_actions = vec![];
554
659
  // Scan through to the next WFT, searching for any patch / la markers, so that we can
555
660
  // pre-resolve them.
556
- let mut wake_las = vec![];
557
661
  for e in self
558
662
  .last_history_from_server
559
663
  .peek_next_wft_sequence(last_handled_wft_started_id)
@@ -575,7 +679,7 @@ impl WorkflowMachines {
575
679
  if let Ok(mk) =
576
680
  self.get_machine_key(CommandID::LocalActivity(la_dat.marker_dat.seq))
577
681
  {
578
- wake_las.push((mk, la_dat));
682
+ delayed_actions.push(DelayedAction::WakeLa(mk, la_dat));
579
683
  } else {
580
684
  self.local_activity_data.insert_peeked_marker(la_dat);
581
685
  }
@@ -584,22 +688,51 @@ impl WorkflowMachines {
584
688
  "Local activity marker was unparsable: {e:?}"
585
689
  )));
586
690
  }
691
+ } else if let Some(
692
+ history_event::Attributes::WorkflowExecutionUpdateAcceptedEventAttributes(ref atts),
693
+ ) = e.attributes
694
+ {
695
+ // If we see a workflow update accepted event, initialize the machine for it by
696
+ // pretending we received the message we would've under not-replay.
697
+ delayed_actions.push(DelayedAction::ProtocolMessage(IncomingProtocolMessage {
698
+ id: atts.accepted_request_message_id.clone(),
699
+ protocol_instance_id: atts.protocol_instance_id.clone(),
700
+ sequencing_id: Some(SequencingId::EventId(
701
+ atts.accepted_request_sequencing_event_id,
702
+ )),
703
+ body: IncomingProtocolMessageBody::UpdateRequest(
704
+ atts.accepted_request
705
+ .clone()
706
+ .ok_or_else(|| {
707
+ WFMachinesError::Fatal(
708
+ "Update accepted event must contain accepted request"
709
+ .to_string(),
710
+ )
711
+ })?
712
+ .try_into()?,
713
+ ),
714
+ }));
587
715
  }
588
716
  }
589
- for (mk, la_dat) in wake_las {
590
- let mach = self.machine_mut(mk);
591
- if let Machines::LocalActivityMachine(ref mut lam) = *mach {
592
- if lam.will_accept_resolve_marker() {
593
- let resps = lam.try_resolve_with_dat(la_dat.into())?;
594
- self.process_machine_responses(mk, resps)?;
595
- } else {
596
- self.local_activity_data.insert_peeked_marker(la_dat);
717
+ for action in delayed_actions {
718
+ match action {
719
+ DelayedAction::WakeLa(mk, la_dat) => {
720
+ let mach = self.machine_mut(mk);
721
+ if let Machines::LocalActivityMachine(ref mut lam) = *mach {
722
+ if lam.will_accept_resolve_marker() {
723
+ let resps = lam.try_resolve_with_dat(la_dat.into())?;
724
+ self.process_machine_responses(mk, resps)?;
725
+ } else {
726
+ self.local_activity_data.insert_peeked_marker(la_dat);
727
+ }
728
+ }
729
+ }
730
+ DelayedAction::ProtocolMessage(pm) => {
731
+ self.handle_protocol_message(pm)?;
597
732
  }
598
733
  }
599
734
  }
600
735
 
601
- update_internal_flags(self);
602
-
603
736
  if !self.replaying {
604
737
  self.metrics.wf_task_replay_latency(replay_start.elapsed());
605
738
  }
@@ -665,27 +798,19 @@ impl WorkflowMachines {
665
798
  }
666
799
 
667
800
  if let Some(initial_cmd_id) = event.get_initial_command_event_id() {
668
- // We remove the machine while we it handles events, then return it, to avoid
669
- // borrowing from ourself mutably.
670
- let maybe_machine = self.machines_by_event_id.remove(&initial_cmd_id);
671
- match maybe_machine {
672
- Some(sm) => {
673
- self.submachine_handle_event(sm, event_dat)?;
674
- // Restore machine if not in it's final state
675
- if !self.machine(sm).is_final_state() {
676
- self.machines_by_event_id.insert(initial_cmd_id, sm);
677
- }
678
- }
679
- None => {
680
- return Err(WFMachinesError::Nondeterminism(format!(
801
+ let mkey = self
802
+ .machines_by_event_id
803
+ .get(&initial_cmd_id)
804
+ .ok_or_else(|| {
805
+ WFMachinesError::Nondeterminism(format!(
681
806
  "During event handling, this event had an initial command ID but we \
682
- could not find a matching command for it: {event:?}"
683
- )));
684
- }
685
- }
807
+ could not find a matching command for it: {event:?}"
808
+ ))
809
+ })?;
810
+ self.submachine_handle_event(*mkey, event_dat)?;
686
811
  } else {
687
812
  self.handle_non_stateful_event(event_dat)?;
688
- }
813
+ };
689
814
 
690
815
  Ok(EventHandlingOutcome::Normal)
691
816
  }
@@ -769,10 +894,7 @@ impl WorkflowMachines {
769
894
  }
770
895
 
771
896
  fn handle_non_stateful_event(&mut self, event_dat: HistEventData) -> Result<()> {
772
- trace!(
773
- event = %event_dat.event,
774
- "handling non-stateful event"
775
- );
897
+ trace!(event = %event_dat.event, "handling non-stateful event");
776
898
  let event_id = event_dat.event.event_id;
777
899
  match EventType::from_i32(event_dat.event.event_type) {
778
900
  Some(EventType::WorkflowExecutionStarted) => {
@@ -855,6 +977,42 @@ impl WorkflowMachines {
855
977
  self.process_machine_responses(sm, machine_responses)?;
856
978
  Ok(())
857
979
  }
980
+ /// Handle a single protocol message delivered in a workflow task.
981
+ ///
982
+ /// This function will attempt to apply the message to a corresponding state machine for the
983
+ /// appropriate protocol type, creating it if it does not exist.
984
+ ///
985
+ /// On replay, protocol messages may be made up by looking ahead in history to see if there is
986
+ /// already an event corresponding to the result of some protocol message which would have
987
+ /// existed to create that result.
988
+ #[instrument(skip(self))]
989
+ fn handle_protocol_message(&mut self, message: IncomingProtocolMessage) -> Result<()> {
990
+ static SEQIDERR: &str = "Update request messages must contain an event sequencing id! \
991
+ This is a server bug.";
992
+
993
+ match message.body {
994
+ IncomingProtocolMessageBody::UpdateRequest(ur) => {
995
+ let seq_id = if let SequencingId::EventId(eid) = message
996
+ .sequencing_id
997
+ .ok_or_else(|| WFMachinesError::Fatal(SEQIDERR.to_string()))?
998
+ {
999
+ eid
1000
+ } else {
1001
+ return Err(WFMachinesError::Fatal(SEQIDERR.to_string()));
1002
+ };
1003
+ let um = UpdateMachine::init(
1004
+ message.id,
1005
+ message.protocol_instance_id.clone(),
1006
+ seq_id,
1007
+ ur,
1008
+ self.replaying,
1009
+ );
1010
+ let mk = self.add_new_protocol_machine(um.machine, message.protocol_instance_id);
1011
+ self.process_machine_responses(mk, vec![um.response])?;
1012
+ }
1013
+ }
1014
+ Ok(())
1015
+ }
858
1016
 
859
1017
  /// Transfer commands from `current_wf_task_commands` to `commands`, so they may be sent off
860
1018
  /// to the server. While doing so, [TemporalStateMachine::handle_command] is called on the
@@ -940,6 +1098,13 @@ impl WorkflowMachines {
940
1098
  machine: smk,
941
1099
  })
942
1100
  }
1101
+ MachineResponse::IssueNewMessage(pm) => {
1102
+ // Messages shouldn't be sent back when replaying. This is true for update,
1103
+ // currently the only user of protocol messages. May eventually change.
1104
+ if !self.replaying {
1105
+ self.message_outbox.push_back(pm);
1106
+ }
1107
+ }
943
1108
  MachineResponse::NewCoreOriginatedCommand(attrs) => match attrs {
944
1109
  ProtoCmdAttrs::RequestCancelExternalWorkflowExecutionCommandAttributes(
945
1110
  attrs,
@@ -1244,6 +1409,20 @@ impl WorkflowMachines {
1244
1409
  CommandIdKind::NeverResolves,
1245
1410
  );
1246
1411
  }
1412
+ WFCommand::UpdateResponse(ur) => {
1413
+ let m_key = self.get_machine_by_msg(&ur.protocol_instance_id)?;
1414
+ let mach = self.machine_mut(m_key);
1415
+ if let Machines::UpdateMachine(m) = mach {
1416
+ let resps = m.handle_response(ur)?;
1417
+ self.process_machine_responses(m_key, resps)?;
1418
+ } else {
1419
+ return Err(WFMachinesError::Nondeterminism(format!(
1420
+ "Tried to handle an update response for \
1421
+ update with instance id {} but it was not found!",
1422
+ &ur.protocol_instance_id
1423
+ )));
1424
+ }
1425
+ }
1247
1426
  WFCommand::NoCommandsFromLang => (),
1248
1427
  }
1249
1428
  }
@@ -1267,6 +1446,17 @@ impl WorkflowMachines {
1267
1446
  })?)
1268
1447
  }
1269
1448
 
1449
+ fn get_machine_by_msg(&self, protocol_instance_id: &str) -> Result<MachineKey> {
1450
+ Ok(*self
1451
+ .machines_by_protocol_instance_id
1452
+ .get(protocol_instance_id)
1453
+ .ok_or_else(|| {
1454
+ WFMachinesError::Fatal(format!(
1455
+ "Missing associated machine for protocol message {protocol_instance_id}"
1456
+ ))
1457
+ })?)
1458
+ }
1459
+
1270
1460
  fn add_terminal_command(&mut self, machine: NewMachineWithCommand) {
1271
1461
  let cwfm = self.add_new_command_machine(machine);
1272
1462
  self.workflow_end_time = Some(SystemTime::now());
@@ -1299,18 +1489,18 @@ impl WorkflowMachines {
1299
1489
  }
1300
1490
  }
1301
1491
 
1492
+ fn add_new_protocol_machine(&mut self, machine: Machines, instance_id: String) -> MachineKey {
1493
+ let k = self.all_machines.insert(machine);
1494
+ self.machines_by_protocol_instance_id.insert(instance_id, k);
1495
+ k
1496
+ }
1497
+
1302
1498
  fn machine(&self, m: MachineKey) -> &Machines {
1303
- self.all_machines
1304
- .get(m)
1305
- .expect("Machine must exist")
1306
- .borrow()
1499
+ self.all_machines.get(m).expect("Machine must exist")
1307
1500
  }
1308
1501
 
1309
1502
  fn machine_mut(&mut self, m: MachineKey) -> &mut Machines {
1310
- self.all_machines
1311
- .get_mut(m)
1312
- .expect("Machine must exist")
1313
- .borrow_mut()
1503
+ self.all_machines.get_mut(m).expect("Machine must exist")
1314
1504
  }
1315
1505
 
1316
1506
  fn augment_continue_as_new_with_current_values(
@@ -1355,6 +1545,32 @@ impl WorkflowMachines {
1355
1545
  }
1356
1546
  }
1357
1547
 
1548
+ /// Contains everything workflow machine internals need to bubble up when we're getting ready to
1549
+ /// respond with a WFT completion. Allows for lazy mutation of the machine, since mutation is not
1550
+ /// desired unless we are actually going to respond to the WFT, which may not always happen.
1551
+ pub struct MachinesWFTResponseContent<'a> {
1552
+ me: &'a mut WorkflowMachines,
1553
+ pub replaying: bool,
1554
+ pub has_pending_jobs: bool,
1555
+ pub have_seen_terminal_event: bool,
1556
+ pub have_pending_la_resolutions: bool,
1557
+ pub last_processed_event: i64,
1558
+ }
1559
+ impl<'a> MachinesWFTResponseContent<'a> {
1560
+ pub fn commands(&self) -> Peekable<impl Iterator<Item = ProtoCommand> + '_> {
1561
+ self.me.get_commands().peekable()
1562
+ }
1563
+ pub fn has_messages(&self) -> bool {
1564
+ !self.me.message_outbox.is_empty()
1565
+ }
1566
+ pub fn messages(&mut self) -> Vec<ProtocolMessage> {
1567
+ self.me.message_outbox.drain(..).collect()
1568
+ }
1569
+ pub fn metadata_for_complete(&mut self) -> WorkflowTaskCompletedMetadata {
1570
+ self.me.get_metadata_for_wft_complete()
1571
+ }
1572
+ }
1573
+
1358
1574
  fn str_to_randomness_seed(run_id: &str) -> u64 {
1359
1575
  // This was originally `DefaultHasher` but that is potentially unstable across Rust releases.
1360
1576
  // This must forever be `SipHasher13` now or we risk breaking history compat.
@@ -1385,8 +1601,9 @@ fn patch_marker_handling(
1385
1601
  fn skip_one_or_two_events(next_event: Option<&HistoryEvent>) -> Result<EventHandlingOutcome> {
1386
1602
  // Also ignore the subsequent upsert event if present
1387
1603
  let mut skip_next_event = false;
1388
- if let Some(Attributes::UpsertWorkflowSearchAttributesEventAttributes(atts)) =
1389
- next_event.and_then(|ne| ne.attributes.as_ref())
1604
+ if let Some(history_event::Attributes::UpsertWorkflowSearchAttributesEventAttributes(
1605
+ atts,
1606
+ )) = next_event.and_then(|ne| ne.attributes.as_ref())
1390
1607
  {
1391
1608
  if let Some(ref sa) = atts.search_attributes {
1392
1609
  skip_next_event = sa.indexed_fields.contains_key(VERSION_SEARCH_ATTR_KEY);