@temporalio/core-bridge 1.8.6 → 1.9.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 (213) hide show
  1. package/Cargo.lock +670 -594
  2. package/Cargo.toml +2 -1
  3. package/lib/errors.js +6 -6
  4. package/lib/errors.js.map +1 -1
  5. package/lib/index.d.ts +17 -44
  6. package/lib/index.js.map +1 -1
  7. package/package.json +5 -6
  8. package/releases/aarch64-apple-darwin/index.node +0 -0
  9. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  10. package/releases/x86_64-apple-darwin/index.node +0 -0
  11. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  12. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  13. package/sdk-core/.github/workflows/heavy.yml +4 -0
  14. package/sdk-core/.github/workflows/per-pr.yml +96 -0
  15. package/sdk-core/ARCHITECTURE.md +1 -1
  16. package/sdk-core/Cargo.toml +10 -0
  17. package/sdk-core/LICENSE.txt +0 -2
  18. package/sdk-core/README.md +37 -21
  19. package/sdk-core/client/Cargo.toml +7 -4
  20. package/sdk-core/client/src/lib.rs +274 -142
  21. package/sdk-core/client/src/metrics.rs +68 -57
  22. package/sdk-core/client/src/raw.rs +191 -45
  23. package/sdk-core/client/src/retry.rs +20 -0
  24. package/sdk-core/client/src/worker_registry/mod.rs +264 -0
  25. package/sdk-core/client/src/workflow_handle/mod.rs +2 -1
  26. package/sdk-core/core/Cargo.toml +17 -19
  27. package/sdk-core/core/src/core_tests/activity_tasks.rs +4 -2
  28. package/sdk-core/core/src/core_tests/child_workflows.rs +7 -7
  29. package/sdk-core/core/src/core_tests/mod.rs +1 -0
  30. package/sdk-core/core/src/core_tests/queries.rs +42 -1
  31. package/sdk-core/core/src/core_tests/replay_flag.rs +29 -39
  32. package/sdk-core/core/src/core_tests/updates.rs +73 -0
  33. package/sdk-core/core/src/core_tests/workflow_tasks.rs +52 -1
  34. package/sdk-core/core/src/ephemeral_server/mod.rs +34 -11
  35. package/sdk-core/core/src/internal_flags.rs +7 -1
  36. package/sdk-core/core/src/lib.rs +19 -36
  37. package/sdk-core/core/src/protosext/mod.rs +12 -4
  38. package/sdk-core/core/src/protosext/protocol_messages.rs +102 -0
  39. package/sdk-core/core/src/replay/mod.rs +99 -48
  40. package/sdk-core/core/src/telemetry/log_export.rs +161 -28
  41. package/sdk-core/core/src/telemetry/metrics.rs +869 -248
  42. package/sdk-core/core/src/telemetry/mod.rs +153 -257
  43. package/sdk-core/core/src/telemetry/prometheus_server.rs +36 -31
  44. package/sdk-core/core/src/test_help/mod.rs +64 -5
  45. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +12 -2
  46. package/sdk-core/core/src/worker/activities.rs +276 -10
  47. package/sdk-core/core/src/worker/client/mocks.rs +18 -0
  48. package/sdk-core/core/src/worker/client.rs +16 -3
  49. package/sdk-core/core/src/worker/mod.rs +45 -28
  50. package/sdk-core/core/src/worker/slot_provider.rs +175 -0
  51. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +27 -34
  52. package/sdk-core/core/src/worker/workflow/history_update.rs +5 -2
  53. package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +71 -95
  54. package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +34 -22
  55. package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +50 -34
  56. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +106 -92
  57. package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +22 -21
  58. package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +386 -499
  59. package/sdk-core/core/src/worker/workflow/machines/mod.rs +12 -2
  60. package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +33 -26
  61. package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +198 -215
  62. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +67 -63
  63. package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +88 -119
  64. package/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +3 -1
  65. package/sdk-core/core/src/worker/workflow/machines/update_state_machine.rs +411 -0
  66. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +27 -26
  67. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +319 -94
  68. package/sdk-core/core/src/worker/workflow/managed_run.rs +179 -132
  69. package/sdk-core/core/src/worker/workflow/mod.rs +129 -58
  70. package/sdk-core/core/src/worker/workflow/run_cache.rs +16 -26
  71. package/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +2 -2
  72. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +48 -43
  73. package/sdk-core/core-api/Cargo.toml +8 -7
  74. package/sdk-core/core-api/src/lib.rs +4 -12
  75. package/sdk-core/core-api/src/telemetry/metrics.rs +334 -0
  76. package/sdk-core/core-api/src/telemetry.rs +53 -42
  77. package/sdk-core/core-api/src/worker.rs +7 -0
  78. package/sdk-core/{.buildkite/docker → docker}/docker-compose.yaml +1 -1
  79. package/sdk-core/etc/dynamic-config.yaml +11 -1
  80. package/sdk-core/fsm/LICENSE.txt +0 -2
  81. package/sdk-core/fsm/rustfsm_procmacro/Cargo.toml +1 -1
  82. package/sdk-core/fsm/rustfsm_procmacro/LICENSE.txt +0 -2
  83. package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +1 -3
  84. package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +2 -2
  85. package/sdk-core/fsm/rustfsm_trait/LICENSE.txt +0 -2
  86. package/sdk-core/sdk/Cargo.toml +2 -2
  87. package/sdk-core/sdk/src/lib.rs +85 -7
  88. package/sdk-core/sdk/src/workflow_context/options.rs +4 -0
  89. package/sdk-core/sdk/src/workflow_context.rs +43 -15
  90. package/sdk-core/sdk/src/workflow_future.rs +334 -204
  91. package/sdk-core/sdk-core-protos/Cargo.toml +3 -3
  92. package/sdk-core/sdk-core-protos/build.rs +14 -14
  93. package/sdk-core/sdk-core-protos/protos/api_upstream/.buildkite/Dockerfile +2 -0
  94. package/sdk-core/sdk-core-protos/protos/api_upstream/Makefile +99 -0
  95. package/sdk-core/sdk-core-protos/protos/api_upstream/api-linter.yaml +56 -0
  96. package/sdk-core/sdk-core-protos/protos/api_upstream/buf.gen.yaml +20 -0
  97. package/sdk-core/sdk-core-protos/protos/api_upstream/buf.lock +11 -0
  98. package/sdk-core/sdk-core-protos/protos/api_upstream/buf.yaml +18 -0
  99. package/sdk-core/sdk-core-protos/protos/api_upstream/google/api/annotations.proto +31 -0
  100. package/sdk-core/sdk-core-protos/protos/api_upstream/google/api/http.proto +379 -0
  101. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/any.proto +162 -0
  102. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/descriptor.proto +1212 -0
  103. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/duration.proto +115 -0
  104. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/empty.proto +51 -0
  105. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/timestamp.proto +144 -0
  106. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/wrappers.proto +123 -0
  107. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/batch/v1/message.proto +12 -9
  108. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/command/v1/message.proto +11 -13
  109. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/common/v1/message.proto +33 -4
  110. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/failed_cause.proto +2 -0
  111. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/reset.proto +4 -4
  112. package/sdk-core/{protos/api_upstream/build/tools.go → sdk-core-protos/protos/api_upstream/temporal/api/export/v1/message.proto} +22 -6
  113. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/filter/v1/message.proto +2 -4
  114. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/history/v1/message.proto +21 -23
  115. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/namespace/v1/message.proto +2 -4
  116. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/operatorservice/v1/request_response.proto +2 -0
  117. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/operatorservice/v1/service.proto +4 -0
  118. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/replication/v1/message.proto +1 -3
  119. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/schedule/v1/message.proto +36 -20
  120. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +13 -0
  121. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/workflow_metadata.proto +66 -0
  122. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/taskqueue/v1/message.proto +2 -4
  123. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/update/v1/message.proto +1 -1
  124. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/version/v1/message.proto +2 -3
  125. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/workflow/v1/message.proto +24 -22
  126. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/workflowservice/v1/request_response.proto +84 -32
  127. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/workflowservice/v1/service.proto +205 -47
  128. package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +57 -0
  129. package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +27 -0
  130. package/sdk-core/sdk-core-protos/src/history_builder.rs +67 -2
  131. package/sdk-core/sdk-core-protos/src/history_info.rs +1 -1
  132. package/sdk-core/sdk-core-protos/src/lib.rs +76 -3
  133. package/sdk-core/sdk-core-protos/src/utilities.rs +14 -0
  134. package/sdk-core/test-utils/Cargo.toml +6 -1
  135. package/sdk-core/test-utils/src/canned_histories.rs +3 -57
  136. package/sdk-core/test-utils/src/interceptors.rs +46 -0
  137. package/sdk-core/test-utils/src/lib.rs +106 -38
  138. package/sdk-core/tests/integ_tests/metrics_tests.rs +110 -15
  139. package/sdk-core/tests/integ_tests/queries_tests.rs +174 -3
  140. package/sdk-core/tests/integ_tests/update_tests.rs +908 -0
  141. package/sdk-core/tests/integ_tests/visibility_tests.rs +4 -4
  142. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +44 -1
  143. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -1
  144. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -1
  145. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +4 -4
  146. package/sdk-core/tests/integ_tests/workflow_tests/eager.rs +61 -0
  147. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +27 -2
  148. package/sdk-core/tests/integ_tests/workflow_tests.rs +142 -3
  149. package/sdk-core/tests/main.rs +2 -1
  150. package/sdk-core/tests/runner.rs +15 -2
  151. package/src/conversions.rs +107 -96
  152. package/src/helpers.rs +74 -0
  153. package/src/runtime.rs +29 -15
  154. package/src/worker.rs +14 -61
  155. package/ts/index.ts +23 -54
  156. package/sdk-core/.buildkite/docker/Dockerfile +0 -9
  157. package/sdk-core/.buildkite/docker/build.sh +0 -5
  158. package/sdk-core/.buildkite/docker/docker-compose-ci.yaml +0 -27
  159. package/sdk-core/.buildkite/pipeline.yml +0 -57
  160. package/sdk-core/.github/workflows/semgrep.yml +0 -25
  161. package/sdk-core/client/LICENSE.txt +0 -23
  162. package/sdk-core/core/LICENSE.txt +0 -23
  163. package/sdk-core/core/src/worker/workflow/bridge.rs +0 -35
  164. package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +0 -215
  165. package/sdk-core/core-api/LICENSE.txt +0 -23
  166. package/sdk-core/protos/api_upstream/.buildkite/Dockerfile +0 -2
  167. package/sdk-core/protos/api_upstream/Makefile +0 -80
  168. package/sdk-core/protos/api_upstream/api-linter.yaml +0 -40
  169. package/sdk-core/protos/api_upstream/buf.yaml +0 -9
  170. package/sdk-core/protos/api_upstream/build/go.mod +0 -7
  171. package/sdk-core/protos/api_upstream/build/go.sum +0 -5
  172. package/sdk-core/protos/api_upstream/go.mod +0 -6
  173. package/sdk-core/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +0 -141
  174. package/sdk-core/sdk/LICENSE.txt +0 -23
  175. package/sdk-core/sdk-core-protos/LICENSE.txt +0 -23
  176. /package/sdk-core/{.buildkite/docker → docker}/docker-compose-telem.yaml +0 -0
  177. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.buildkite/docker-compose.yml +0 -0
  178. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.buildkite/pipeline.yml +0 -0
  179. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.github/CODEOWNERS +0 -0
  180. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  181. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.github/workflows/publish-docs.yml +0 -0
  182. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.github/workflows/trigger-api-go-update.yml +0 -0
  183. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/LICENSE +0 -0
  184. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/README.md +0 -0
  185. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/batch_operation.proto +0 -0
  186. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/command_type.proto +0 -0
  187. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/common.proto +0 -0
  188. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/event_type.proto +0 -0
  189. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/namespace.proto +0 -0
  190. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/query.proto +0 -0
  191. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/schedule.proto +0 -0
  192. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/task_queue.proto +0 -0
  193. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/update.proto +0 -0
  194. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/workflow.proto +0 -0
  195. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/errordetails/v1/message.proto +0 -0
  196. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/failure/v1/message.proto +0 -0
  197. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/protocol/v1/message.proto +0 -0
  198. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/query/v1/message.proto +0 -0
  199. /package/sdk-core/{protos → sdk-core-protos/protos}/google/rpc/status.proto +0 -0
  200. /package/sdk-core/{protos → sdk-core-protos/protos}/grpc/health/v1/health.proto +0 -0
  201. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/activity_result/activity_result.proto +0 -0
  202. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/activity_task/activity_task.proto +0 -0
  203. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/child_workflow/child_workflow.proto +0 -0
  204. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/common/common.proto +0 -0
  205. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/core_interface.proto +0 -0
  206. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/external_data/external_data.proto +0 -0
  207. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +0 -0
  208. /package/sdk-core/{protos → sdk-core-protos/protos}/testsrv_upstream/Makefile +0 -0
  209. /package/sdk-core/{protos → sdk-core-protos/protos}/testsrv_upstream/api-linter.yaml +0 -0
  210. /package/sdk-core/{protos → sdk-core-protos/protos}/testsrv_upstream/buf.yaml +0 -0
  211. /package/sdk-core/{protos/api_upstream → sdk-core-protos/protos/testsrv_upstream}/dependencies/gogoproto/gogo.proto +0 -0
  212. /package/sdk-core/{protos → sdk-core-protos/protos}/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +0 -0
  213. /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,14 +40,16 @@ 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,
49
+ sync::Arc,
46
50
  time::{Duration, Instant, SystemTime},
47
51
  };
52
+ use temporal_sdk_core_api::worker::WorkerConfig;
48
53
  use temporal_sdk_core_protos::{
49
54
  coresdk::{
50
55
  common::{NamespacedWorkflowExecution, VersioningIntent},
@@ -59,7 +64,8 @@ use temporal_sdk_core_protos::{
59
64
  temporal::api::{
60
65
  command::v1::{command::Attributes as ProtoCmdAttrs, Command as ProtoCommand},
61
66
  enums::v1::EventType,
62
- history::v1::{history_event, history_event::Attributes, HistoryEvent},
67
+ history::v1::{history_event, HistoryEvent},
68
+ protocol::v1::{message::SequencingId, Message as ProtocolMessage},
63
69
  sdk::v1::WorkflowTaskCompletedMetadata,
64
70
  },
65
71
  };
@@ -75,6 +81,8 @@ pub(crate) struct WorkflowMachines {
75
81
  /// kept because the lang side polls & completes for every workflow task, but we do not need
76
82
  /// to poll the server that often during replay.
77
83
  last_history_from_server: HistoryUpdate,
84
+ /// Protocol messages that have yet to be processed for the current WFT.
85
+ protocol_msgs: Vec<IncomingProtocolMessage>,
78
86
  /// EventId of the last handled WorkflowTaskStarted event
79
87
  current_started_event_id: i64,
80
88
  /// The event id of the next workflow task started event that the machines need to process.
@@ -87,16 +95,12 @@ pub(crate) struct WorkflowMachines {
87
95
  pub last_processed_event: i64,
88
96
  /// True if the workflow is replaying from history
89
97
  pub replaying: bool,
90
- /// Namespace this workflow exists in
91
- pub namespace: String,
92
98
  /// Workflow identifier
93
99
  pub workflow_id: String,
94
100
  /// Workflow type identifier. (Function name, class, etc)
95
101
  pub workflow_type: String,
96
102
  /// Identifies the current run
97
103
  pub run_id: String,
98
- /// The task queue this workflow is operating within
99
- pub task_queue: String,
100
104
  /// Is set to true once we've seen the final event in workflow history, to avoid accidentally
101
105
  /// re-applying the final workflow task.
102
106
  pub have_seen_terminal_event: bool,
@@ -117,16 +121,19 @@ pub(crate) struct WorkflowMachines {
117
121
  history_size_bytes: u64,
118
122
  /// Set on each WFT started event
119
123
  continue_as_new_suggested: bool,
124
+ /// Set if the current WFT is already complete and that completion event had a build id in it.
125
+ current_wft_build_id: Option<String>,
120
126
 
121
127
  all_machines: SlotMap<MachineKey, Machines>,
122
128
  /// If a machine key is in this map, that machine was created internally by core, not as a
123
129
  /// command from lang.
124
130
  machine_is_core_created: SparseSecondaryMap<MachineKey, ()>,
125
-
126
131
  /// A mapping for accessing machines associated to a particular event, where the key is the id
127
132
  /// of the initiating event for that machine.
128
133
  machines_by_event_id: HashMap<i64, MachineKey>,
129
-
134
+ /// A mapping for accessing machines that were created as a result of protocol messages. The
135
+ /// key is the protocol's instance id.
136
+ machines_by_protocol_instance_id: HashMap<String, MachineKey>,
130
137
  /// Maps command ids as created by workflow authors to their associated machines.
131
138
  id_to_machine: HashMap<CommandID, MachineKey>,
132
139
 
@@ -136,6 +143,9 @@ pub(crate) struct WorkflowMachines {
136
143
  /// Commands generated by the currently processing workflow task, which will eventually be
137
144
  /// transferred to `commands` (and hence eventually sent to the server)
138
145
  current_wf_task_commands: VecDeque<CommandAndMachine>,
146
+ /// Messages generated while processing the current workflow task, which will be sent in the
147
+ /// next WFT response. Keyed by message id.
148
+ message_outbox: VecDeque<ProtocolMessage>,
139
149
 
140
150
  /// Information about patch markers we have already seen while replaying history
141
151
  encountered_patch_markers: HashMap<String, ChangeInfo>,
@@ -148,6 +158,7 @@ pub(crate) struct WorkflowMachines {
148
158
 
149
159
  /// Metrics context
150
160
  pub metrics: MetricsContext,
161
+ worker_config: Arc<WorkerConfig>,
151
162
  }
152
163
 
153
164
  #[derive(Debug, derive_more::Display)]
@@ -180,6 +191,9 @@ pub(super) enum MachineResponse {
180
191
  /// Pushes a new command into the list that will be sent to server once we respond with the
181
192
  /// workflow task completion
182
193
  IssueNewCommand(ProtoCommand),
194
+ /// Pushes a new protocol message into the list that will be sent to server once we respond with
195
+ /// the workflow task completion
196
+ IssueNewMessage(ProtocolMessage),
183
197
  /// The machine requests the creation of another *different* machine. This acts as if lang
184
198
  /// had replied to the activation with a command, but we use a special set of IDs to avoid
185
199
  /// collisions.
@@ -230,11 +244,10 @@ impl WorkflowMachines {
230
244
  };
231
245
  Self {
232
246
  last_history_from_server: basics.history,
233
- namespace: basics.namespace,
247
+ protocol_msgs: vec![],
234
248
  workflow_id: basics.workflow_id,
235
249
  workflow_type: basics.workflow_type,
236
250
  run_id: basics.run_id,
237
- task_queue: basics.task_queue,
238
251
  drive_me: driven_wf,
239
252
  replaying,
240
253
  metrics: basics.metrics,
@@ -249,15 +262,19 @@ impl WorkflowMachines {
249
262
  observed_internal_flags: Rc::new(RefCell::new(observed_internal_flags)),
250
263
  history_size_bytes: 0,
251
264
  continue_as_new_suggested: false,
265
+ current_wft_build_id: None,
252
266
  all_machines: Default::default(),
253
267
  machine_is_core_created: Default::default(),
254
268
  machines_by_event_id: Default::default(),
269
+ machines_by_protocol_instance_id: Default::default(),
255
270
  id_to_machine: Default::default(),
256
271
  commands: Default::default(),
257
272
  current_wf_task_commands: Default::default(),
273
+ message_outbox: Default::default(),
258
274
  encountered_patch_markers: Default::default(),
259
275
  local_activity_data: LocalActivityData::default(),
260
276
  have_seen_terminal_event: false,
277
+ worker_config: basics.worker_config,
261
278
  }
262
279
  }
263
280
 
@@ -274,6 +291,20 @@ impl WorkflowMachines {
274
291
  .and_then(|(st, et)| et.duration_since(st).ok())
275
292
  }
276
293
 
294
+ /// Must be called every time a new WFT is received
295
+ pub(crate) fn new_work_from_server(
296
+ &mut self,
297
+ update: HistoryUpdate,
298
+ protocol_messages: Vec<IncomingProtocolMessage>,
299
+ ) -> Result<()> {
300
+ if !self.protocol_msgs.is_empty() {
301
+ dbg_panic!("There are unprocessed protocol messages while receiving new work");
302
+ }
303
+ self.protocol_msgs = protocol_messages;
304
+ self.new_history_from_server(update)?;
305
+ Ok(())
306
+ }
307
+
277
308
  pub(crate) fn new_history_from_server(&mut self, update: HistoryUpdate) -> Result<()> {
278
309
  self.last_history_from_server = update;
279
310
  self.replaying = self.last_history_from_server.previous_wft_started_id > 0;
@@ -335,28 +366,40 @@ impl WorkflowMachines {
335
366
  self.drive_me.get_started_info()
336
367
  }
337
368
 
369
+ pub(crate) fn get_last_wft_started_id(&self) -> i64 {
370
+ self.current_started_event_id
371
+ }
372
+
373
+ pub(crate) fn prepare_for_wft_response(&mut self) -> MachinesWFTResponseContent {
374
+ MachinesWFTResponseContent {
375
+ replaying: self.replaying,
376
+ has_pending_jobs: self.has_pending_jobs(),
377
+ have_seen_terminal_event: self.have_seen_terminal_event,
378
+ have_pending_la_resolutions: self.has_pending_la_resolutions(),
379
+ last_processed_event: self.last_processed_event,
380
+ me: self,
381
+ }
382
+ }
383
+
338
384
  /// Fetches commands which are ready for processing from the state machines, generally to be
339
385
  /// sent off to the server. They are not removed from the internal queue, that happens when
340
386
  /// corresponding history events from the server are being handled.
341
- pub(crate) fn get_commands(&self) -> Vec<ProtoCommand> {
387
+ pub(crate) fn get_commands(&self) -> impl Iterator<Item = ProtoCommand> + '_ {
342
388
  // Since we're about to write a WFT, record any internal flags we know about which aren't
343
389
  // already recorded.
344
390
  (*self.observed_internal_flags)
345
391
  .borrow_mut()
346
392
  .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
393
+ self.commands.iter().filter_map(|c| {
394
+ if !self.machine(c.machine).is_final_state() {
395
+ match &c.command {
396
+ MachineAssociatedCommand::Real(cmd) => Some((**cmd).clone()),
397
+ MachineAssociatedCommand::FakeLocalActivityMarker(_) => None,
357
398
  }
358
- })
359
- .collect()
399
+ } else {
400
+ None
401
+ }
402
+ })
360
403
  }
361
404
 
362
405
  /// Returns the next activation that needs to be performed by the lang sdk. Things like unblock
@@ -375,9 +418,16 @@ impl WorkflowMachines {
375
418
  Some(workflow_activation_job::Variant::QueryWorkflow(_))
376
419
  )
377
420
  });
421
+ let is_replaying = self.replaying || all_query;
422
+ let build_id_for_current_task = if is_replaying {
423
+ self.current_wft_build_id.clone().unwrap_or_default()
424
+ } else {
425
+ self.current_wft_build_id = Some(self.worker_config.worker_build_id.clone());
426
+ self.worker_config.worker_build_id.clone()
427
+ };
378
428
  WorkflowActivation {
379
429
  timestamp: self.current_wf_time.map(Into::into),
380
- is_replaying: self.replaying || all_query,
430
+ is_replaying,
381
431
  run_id: self.run_id.clone(),
382
432
  history_length: self.last_processed_event as u32,
383
433
  jobs,
@@ -387,6 +437,7 @@ impl WorkflowMachines {
387
437
  .collect(),
388
438
  history_size_bytes: self.history_size_bytes,
389
439
  continue_as_new_suggested: self.continue_as_new_suggested,
440
+ build_id_for_current_task,
390
441
  }
391
442
  }
392
443
 
@@ -401,7 +452,13 @@ impl WorkflowMachines {
401
452
  .any(|v| v.is_la_resolution)
402
453
  }
403
454
 
404
- pub(crate) fn get_metadata_for_wft_complete(&self) -> WorkflowTaskCompletedMetadata {
455
+ pub(crate) fn get_metadata_for_wft_complete(&mut self) -> WorkflowTaskCompletedMetadata {
456
+ // If this worker has a build ID and we're completing the task, we want to say our ID is the
457
+ // current build ID, so that if we get a query before any new history, we properly can
458
+ // report that our ID was the one used for the completion.
459
+ if !self.worker_config.worker_build_id.is_empty() {
460
+ self.current_wft_build_id = Some(self.worker_config.worker_build_id.clone());
461
+ }
405
462
  (*self.observed_internal_flags)
406
463
  .borrow_mut()
407
464
  .gather_for_wft_complete()
@@ -413,6 +470,30 @@ impl WorkflowMachines {
413
470
  .add_lang_used(flags);
414
471
  }
415
472
 
473
+ /// Undo a speculative workflow task by resetting to a certain WFT Started ID. This can happen
474
+ /// when an update request is rejected.
475
+ pub(crate) fn reset_last_started_id(&mut self, id: i64) {
476
+ debug!("Resetting back to event id {} due to speculative WFT", id);
477
+ self.current_started_event_id = id;
478
+ // This is pretty nasty to just + 1 like this, but, we know WFT complete always follows
479
+ // WFT started, which the id given to us to reset to must be, and we need to avoid
480
+ // re-applying the WFT Completed event, so we make sure to consider that processed
481
+ self.last_processed_event = id + 1;
482
+ // Then, we have to drop any state machines (which should only be one workflow task machine)
483
+ // we may have created when servicing the speculative task.
484
+ // Remove when https://github.com/rust-lang/rust/issues/59618 is stable
485
+ let remove_these: Vec<_> = self
486
+ .machines_by_event_id
487
+ .iter()
488
+ .filter(|(mid, _)| **mid > id)
489
+ .map(|(mid, mkey)| (*mid, *mkey))
490
+ .collect();
491
+ for (mid, mkey) in remove_these {
492
+ self.machines_by_event_id.remove(&mid);
493
+ self.all_machines.remove(mkey);
494
+ }
495
+ }
496
+
416
497
  /// Iterate the state machines, which consists of grabbing any pending outgoing commands from
417
498
  /// the workflow code, handling them, and preparing them to be sent off to the server.
418
499
  pub(crate) fn iterate_machines(&mut self) -> Result<()> {
@@ -446,22 +527,42 @@ impl WorkflowMachines {
446
527
  return Ok(0);
447
528
  }
448
529
 
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
- }
530
+ fn get_processable_messages(
531
+ me: &mut WorkflowMachines,
532
+ for_event_id: i64,
533
+ ) -> Vec<IncomingProtocolMessage> {
534
+ // Another thing to replace when `drain_filter` exists
535
+ let mut ret = vec![];
536
+ me.protocol_msgs = std::mem::take(&mut me.protocol_msgs)
537
+ .into_iter()
538
+ .filter_map(|x| {
539
+ if x.processable_after_event_id()
540
+ .is_some_and(|eid| eid <= for_event_id)
541
+ {
542
+ ret.push(x);
543
+ None
544
+ } else {
545
+ Some(x)
546
+ }
547
+ })
548
+ .collect();
549
+ ret
459
550
  }
460
551
 
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);
552
+ // Peek to the next WFT complete and update ourselves with data we might need in it.
553
+ if let Some(next_complete) = self
554
+ .last_history_from_server
555
+ .peek_next_wft_completed(self.last_processed_event)
556
+ {
557
+ // We update the internal flags before applying the current task
558
+ (*self.observed_internal_flags)
559
+ .borrow_mut()
560
+ .add_from_complete(next_complete);
561
+ // Save this tasks' Build ID if it had one
562
+ if let Some(bid) = next_complete.worker_version.as_ref().map(|wv| &wv.build_id) {
563
+ self.current_wft_build_id = Some(bid.to_string());
564
+ }
565
+ }
465
566
 
466
567
  let last_handled_wft_started_id = self.current_started_event_id;
467
568
  let (events, has_final_event) = match self
@@ -499,14 +600,14 @@ impl WorkflowMachines {
499
600
  let mut do_handle_event = true;
500
601
  let mut history = events.into_iter().peekable();
501
602
  while let Some(event) = history.next() {
502
- if event.event_id != self.last_processed_event + 1 {
603
+ let eid = event.event_id;
604
+ if eid != self.last_processed_event + 1 {
503
605
  return Err(WFMachinesError::Fatal(format!(
504
606
  "History is out of order. Last processed event: {}, event id: {}",
505
- self.last_processed_event, event.event_id
607
+ self.last_processed_event, eid
506
608
  )));
507
609
  }
508
610
  let next_event = history.peek();
509
- let eid = event.event_id;
510
611
 
511
612
  // This definition of replaying here is that we are no longer replaying as soon as we
512
613
  // see new events that have never been seen or produced by the SDK.
@@ -520,7 +621,7 @@ impl WorkflowMachines {
520
621
  // them.
521
622
  if self.replaying
522
623
  && has_final_event
523
- && event.event_id > self.last_history_from_server.previous_wft_started_id
624
+ && eid > self.last_history_from_server.previous_wft_started_id
524
625
  && event.event_type() != EventType::WorkflowTaskCompleted
525
626
  && !event.is_command_event()
526
627
  {
@@ -528,6 +629,12 @@ impl WorkflowMachines {
528
629
  self.replaying = false;
529
630
  }
530
631
 
632
+ // Process any messages that should be processed before the event we're about to handle
633
+ let processable_msgs = get_processable_messages(self, eid - 1);
634
+ for msg in processable_msgs {
635
+ self.handle_protocol_message(msg)?;
636
+ }
637
+
531
638
  if do_handle_event {
532
639
  let eho = self.handle_event(
533
640
  HistEventData {
@@ -551,9 +658,14 @@ impl WorkflowMachines {
551
658
  self.last_processed_event = eid;
552
659
  }
553
660
 
661
+ // Needed to delay mutation of self until after we've iterated over peeked events.
662
+ enum DelayedAction {
663
+ WakeLa(MachineKey, CompleteLocalActivityData),
664
+ ProtocolMessage(IncomingProtocolMessage),
665
+ }
666
+ let mut delayed_actions = vec![];
554
667
  // Scan through to the next WFT, searching for any patch / la markers, so that we can
555
668
  // pre-resolve them.
556
- let mut wake_las = vec![];
557
669
  for e in self
558
670
  .last_history_from_server
559
671
  .peek_next_wft_sequence(last_handled_wft_started_id)
@@ -575,7 +687,7 @@ impl WorkflowMachines {
575
687
  if let Ok(mk) =
576
688
  self.get_machine_key(CommandID::LocalActivity(la_dat.marker_dat.seq))
577
689
  {
578
- wake_las.push((mk, la_dat));
690
+ delayed_actions.push(DelayedAction::WakeLa(mk, la_dat));
579
691
  } else {
580
692
  self.local_activity_data.insert_peeked_marker(la_dat);
581
693
  }
@@ -584,22 +696,51 @@ impl WorkflowMachines {
584
696
  "Local activity marker was unparsable: {e:?}"
585
697
  )));
586
698
  }
699
+ } else if let Some(
700
+ history_event::Attributes::WorkflowExecutionUpdateAcceptedEventAttributes(ref atts),
701
+ ) = e.attributes
702
+ {
703
+ // If we see a workflow update accepted event, initialize the machine for it by
704
+ // pretending we received the message we would've under not-replay.
705
+ delayed_actions.push(DelayedAction::ProtocolMessage(IncomingProtocolMessage {
706
+ id: atts.accepted_request_message_id.clone(),
707
+ protocol_instance_id: atts.protocol_instance_id.clone(),
708
+ sequencing_id: Some(SequencingId::EventId(
709
+ atts.accepted_request_sequencing_event_id,
710
+ )),
711
+ body: IncomingProtocolMessageBody::UpdateRequest(
712
+ atts.accepted_request
713
+ .clone()
714
+ .ok_or_else(|| {
715
+ WFMachinesError::Fatal(
716
+ "Update accepted event must contain accepted request"
717
+ .to_string(),
718
+ )
719
+ })?
720
+ .try_into()?,
721
+ ),
722
+ }));
587
723
  }
588
724
  }
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);
725
+ for action in delayed_actions {
726
+ match action {
727
+ DelayedAction::WakeLa(mk, la_dat) => {
728
+ let mach = self.machine_mut(mk);
729
+ if let Machines::LocalActivityMachine(ref mut lam) = *mach {
730
+ if lam.will_accept_resolve_marker() {
731
+ let resps = lam.try_resolve_with_dat(la_dat.into())?;
732
+ self.process_machine_responses(mk, resps)?;
733
+ } else {
734
+ self.local_activity_data.insert_peeked_marker(la_dat);
735
+ }
736
+ }
737
+ }
738
+ DelayedAction::ProtocolMessage(pm) => {
739
+ self.handle_protocol_message(pm)?;
597
740
  }
598
741
  }
599
742
  }
600
743
 
601
- update_internal_flags(self);
602
-
603
744
  if !self.replaying {
604
745
  self.metrics.wf_task_replay_latency(replay_start.elapsed());
605
746
  }
@@ -665,27 +806,19 @@ impl WorkflowMachines {
665
806
  }
666
807
 
667
808
  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!(
809
+ let mkey = self
810
+ .machines_by_event_id
811
+ .get(&initial_cmd_id)
812
+ .ok_or_else(|| {
813
+ WFMachinesError::Nondeterminism(format!(
681
814
  "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
- }
815
+ could not find a matching command for it: {event:?}"
816
+ ))
817
+ })?;
818
+ self.submachine_handle_event(*mkey, event_dat)?;
686
819
  } else {
687
820
  self.handle_non_stateful_event(event_dat)?;
688
- }
821
+ };
689
822
 
690
823
  Ok(EventHandlingOutcome::Normal)
691
824
  }
@@ -769,10 +902,7 @@ impl WorkflowMachines {
769
902
  }
770
903
 
771
904
  fn handle_non_stateful_event(&mut self, event_dat: HistEventData) -> Result<()> {
772
- trace!(
773
- event = %event_dat.event,
774
- "handling non-stateful event"
775
- );
905
+ trace!(event = %event_dat.event, "handling non-stateful event");
776
906
  let event_id = event_dat.event.event_id;
777
907
  match EventType::from_i32(event_dat.event.event_type) {
778
908
  Some(EventType::WorkflowExecutionStarted) => {
@@ -855,6 +985,42 @@ impl WorkflowMachines {
855
985
  self.process_machine_responses(sm, machine_responses)?;
856
986
  Ok(())
857
987
  }
988
+ /// Handle a single protocol message delivered in a workflow task.
989
+ ///
990
+ /// This function will attempt to apply the message to a corresponding state machine for the
991
+ /// appropriate protocol type, creating it if it does not exist.
992
+ ///
993
+ /// On replay, protocol messages may be made up by looking ahead in history to see if there is
994
+ /// already an event corresponding to the result of some protocol message which would have
995
+ /// existed to create that result.
996
+ #[instrument(skip(self))]
997
+ fn handle_protocol_message(&mut self, message: IncomingProtocolMessage) -> Result<()> {
998
+ static SEQIDERR: &str = "Update request messages must contain an event sequencing id! \
999
+ This is a server bug.";
1000
+
1001
+ match message.body {
1002
+ IncomingProtocolMessageBody::UpdateRequest(ur) => {
1003
+ let seq_id = if let SequencingId::EventId(eid) = message
1004
+ .sequencing_id
1005
+ .ok_or_else(|| WFMachinesError::Fatal(SEQIDERR.to_string()))?
1006
+ {
1007
+ eid
1008
+ } else {
1009
+ return Err(WFMachinesError::Fatal(SEQIDERR.to_string()));
1010
+ };
1011
+ let um = UpdateMachine::init(
1012
+ message.id,
1013
+ message.protocol_instance_id.clone(),
1014
+ seq_id,
1015
+ ur,
1016
+ self.replaying,
1017
+ );
1018
+ let mk = self.add_new_protocol_machine(um.machine, message.protocol_instance_id);
1019
+ self.process_machine_responses(mk, vec![um.response])?;
1020
+ }
1021
+ }
1022
+ Ok(())
1023
+ }
858
1024
 
859
1025
  /// Transfer commands from `current_wf_task_commands` to `commands`, so they may be sent off
860
1026
  /// to the server. While doing so, [TemporalStateMachine::handle_command] is called on the
@@ -940,6 +1106,13 @@ impl WorkflowMachines {
940
1106
  machine: smk,
941
1107
  })
942
1108
  }
1109
+ MachineResponse::IssueNewMessage(pm) => {
1110
+ // Messages shouldn't be sent back when replaying. This is true for update,
1111
+ // currently the only user of protocol messages. May eventually change.
1112
+ if !self.replaying {
1113
+ self.message_outbox.push_back(pm);
1114
+ }
1115
+ }
943
1116
  MachineResponse::NewCoreOriginatedCommand(attrs) => match attrs {
944
1117
  ProtoCmdAttrs::RequestCancelExternalWorkflowExecutionCommandAttributes(
945
1118
  attrs,
@@ -1206,7 +1379,7 @@ impl WorkflowMachines {
1206
1379
  }
1207
1380
  Some(cancel_we::Target::ChildWorkflowId(wfid)) => (
1208
1381
  NamespacedWorkflowExecution {
1209
- namespace: self.namespace.clone(),
1382
+ namespace: self.worker_config.namespace.clone(),
1210
1383
  workflow_id: wfid,
1211
1384
  run_id: "".to_string(),
1212
1385
  },
@@ -1227,7 +1400,7 @@ impl WorkflowMachines {
1227
1400
  WFCommand::SignalExternalWorkflow(attrs) => {
1228
1401
  let seq = attrs.seq;
1229
1402
  self.add_cmd_to_wf_task(
1230
- new_external_signal(attrs, &self.namespace)?,
1403
+ new_external_signal(attrs, &self.worker_config.namespace)?,
1231
1404
  CommandID::SignalExternal(seq).into(),
1232
1405
  );
1233
1406
  }
@@ -1244,6 +1417,20 @@ impl WorkflowMachines {
1244
1417
  CommandIdKind::NeverResolves,
1245
1418
  );
1246
1419
  }
1420
+ WFCommand::UpdateResponse(ur) => {
1421
+ let m_key = self.get_machine_by_msg(&ur.protocol_instance_id)?;
1422
+ let mach = self.machine_mut(m_key);
1423
+ if let Machines::UpdateMachine(m) = mach {
1424
+ let resps = m.handle_response(ur)?;
1425
+ self.process_machine_responses(m_key, resps)?;
1426
+ } else {
1427
+ return Err(WFMachinesError::Nondeterminism(format!(
1428
+ "Tried to handle an update response for \
1429
+ update with instance id {} but it was not found!",
1430
+ &ur.protocol_instance_id
1431
+ )));
1432
+ }
1433
+ }
1247
1434
  WFCommand::NoCommandsFromLang => (),
1248
1435
  }
1249
1436
  }
@@ -1267,6 +1454,17 @@ impl WorkflowMachines {
1267
1454
  })?)
1268
1455
  }
1269
1456
 
1457
+ fn get_machine_by_msg(&self, protocol_instance_id: &str) -> Result<MachineKey> {
1458
+ Ok(*self
1459
+ .machines_by_protocol_instance_id
1460
+ .get(protocol_instance_id)
1461
+ .ok_or_else(|| {
1462
+ WFMachinesError::Fatal(format!(
1463
+ "Missing associated machine for protocol message {protocol_instance_id}"
1464
+ ))
1465
+ })?)
1466
+ }
1467
+
1270
1468
  fn add_terminal_command(&mut self, machine: NewMachineWithCommand) {
1271
1469
  let cwfm = self.add_new_command_machine(machine);
1272
1470
  self.workflow_end_time = Some(SystemTime::now());
@@ -1299,18 +1497,18 @@ impl WorkflowMachines {
1299
1497
  }
1300
1498
  }
1301
1499
 
1500
+ fn add_new_protocol_machine(&mut self, machine: Machines, instance_id: String) -> MachineKey {
1501
+ let k = self.all_machines.insert(machine);
1502
+ self.machines_by_protocol_instance_id.insert(instance_id, k);
1503
+ k
1504
+ }
1505
+
1302
1506
  fn machine(&self, m: MachineKey) -> &Machines {
1303
- self.all_machines
1304
- .get(m)
1305
- .expect("Machine must exist")
1306
- .borrow()
1507
+ self.all_machines.get(m).expect("Machine must exist")
1307
1508
  }
1308
1509
 
1309
1510
  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()
1511
+ self.all_machines.get_mut(m).expect("Machine must exist")
1314
1512
  }
1315
1513
 
1316
1514
  fn augment_continue_as_new_with_current_values(
@@ -1349,12 +1547,38 @@ impl WorkflowMachines {
1349
1547
  VersioningIntent::Unspecified => {
1350
1548
  // If the target TQ is empty, that means use same TQ.
1351
1549
  // When TQs match, use compat by default
1352
- target_tq.is_empty() || target_tq == self.task_queue
1550
+ target_tq.is_empty() || target_tq == self.worker_config.task_queue
1353
1551
  }
1354
1552
  }
1355
1553
  }
1356
1554
  }
1357
1555
 
1556
+ /// Contains everything workflow machine internals need to bubble up when we're getting ready to
1557
+ /// respond with a WFT completion. Allows for lazy mutation of the machine, since mutation is not
1558
+ /// desired unless we are actually going to respond to the WFT, which may not always happen.
1559
+ pub struct MachinesWFTResponseContent<'a> {
1560
+ me: &'a mut WorkflowMachines,
1561
+ pub replaying: bool,
1562
+ pub has_pending_jobs: bool,
1563
+ pub have_seen_terminal_event: bool,
1564
+ pub have_pending_la_resolutions: bool,
1565
+ pub last_processed_event: i64,
1566
+ }
1567
+ impl<'a> MachinesWFTResponseContent<'a> {
1568
+ pub fn commands(&self) -> Peekable<impl Iterator<Item = ProtoCommand> + '_> {
1569
+ self.me.get_commands().peekable()
1570
+ }
1571
+ pub fn has_messages(&self) -> bool {
1572
+ !self.me.message_outbox.is_empty()
1573
+ }
1574
+ pub fn messages(&mut self) -> Vec<ProtocolMessage> {
1575
+ self.me.message_outbox.drain(..).collect()
1576
+ }
1577
+ pub fn metadata_for_complete(&mut self) -> WorkflowTaskCompletedMetadata {
1578
+ self.me.get_metadata_for_wft_complete()
1579
+ }
1580
+ }
1581
+
1358
1582
  fn str_to_randomness_seed(run_id: &str) -> u64 {
1359
1583
  // This was originally `DefaultHasher` but that is potentially unstable across Rust releases.
1360
1584
  // This must forever be `SipHasher13` now or we risk breaking history compat.
@@ -1385,8 +1609,9 @@ fn patch_marker_handling(
1385
1609
  fn skip_one_or_two_events(next_event: Option<&HistoryEvent>) -> Result<EventHandlingOutcome> {
1386
1610
  // Also ignore the subsequent upsert event if present
1387
1611
  let mut skip_next_event = false;
1388
- if let Some(Attributes::UpsertWorkflowSearchAttributesEventAttributes(atts)) =
1389
- next_event.and_then(|ne| ne.attributes.as_ref())
1612
+ if let Some(history_event::Attributes::UpsertWorkflowSearchAttributesEventAttributes(
1613
+ atts,
1614
+ )) = next_event.and_then(|ne| ne.attributes.as_ref())
1390
1615
  {
1391
1616
  if let Some(ref sa) = atts.search_attributes {
1392
1617
  skip_next_event = sa.indexed_fields.contains_key(VERSION_SEARCH_ATTR_KEY);