@temporalio/core-bridge 1.5.2 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/Cargo.lock +304 -112
  2. package/lib/index.d.ts +8 -6
  3. package/lib/index.js.map +1 -1
  4. package/package.json +9 -4
  5. package/releases/aarch64-apple-darwin/index.node +0 -0
  6. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  7. package/releases/x86_64-apple-darwin/index.node +0 -0
  8. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  9. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  10. package/sdk-core/.buildkite/docker/Dockerfile +2 -2
  11. package/sdk-core/.buildkite/docker/docker-compose.yaml +1 -1
  12. package/sdk-core/.buildkite/pipeline.yml +2 -4
  13. package/sdk-core/.cargo/config.toml +5 -2
  14. package/sdk-core/.github/workflows/heavy.yml +29 -0
  15. package/sdk-core/Cargo.toml +1 -1
  16. package/sdk-core/README.md +20 -10
  17. package/sdk-core/client/src/lib.rs +215 -39
  18. package/sdk-core/client/src/metrics.rs +17 -8
  19. package/sdk-core/client/src/raw.rs +4 -4
  20. package/sdk-core/client/src/retry.rs +32 -20
  21. package/sdk-core/core/Cargo.toml +25 -12
  22. package/sdk-core/core/src/abstractions/take_cell.rs +28 -0
  23. package/sdk-core/core/src/abstractions.rs +204 -14
  24. package/sdk-core/core/src/core_tests/activity_tasks.rs +143 -50
  25. package/sdk-core/core/src/core_tests/child_workflows.rs +6 -5
  26. package/sdk-core/core/src/core_tests/determinism.rs +165 -2
  27. package/sdk-core/core/src/core_tests/local_activities.rs +431 -43
  28. package/sdk-core/core/src/core_tests/queries.rs +34 -16
  29. package/sdk-core/core/src/core_tests/workers.rs +8 -5
  30. package/sdk-core/core/src/core_tests/workflow_tasks.rs +588 -55
  31. package/sdk-core/core/src/ephemeral_server/mod.rs +113 -12
  32. package/sdk-core/core/src/internal_flags.rs +155 -0
  33. package/sdk-core/core/src/lib.rs +16 -9
  34. package/sdk-core/core/src/protosext/mod.rs +1 -1
  35. package/sdk-core/core/src/replay/mod.rs +16 -27
  36. package/sdk-core/core/src/telemetry/log_export.rs +1 -1
  37. package/sdk-core/core/src/telemetry/metrics.rs +69 -35
  38. package/sdk-core/core/src/telemetry/mod.rs +60 -21
  39. package/sdk-core/core/src/telemetry/prometheus_server.rs +19 -13
  40. package/sdk-core/core/src/test_help/mod.rs +73 -14
  41. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +119 -160
  42. package/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +89 -0
  43. package/sdk-core/core/src/worker/activities/local_activities.rs +379 -129
  44. package/sdk-core/core/src/worker/activities.rs +350 -175
  45. package/sdk-core/core/src/worker/client/mocks.rs +22 -2
  46. package/sdk-core/core/src/worker/client.rs +18 -2
  47. package/sdk-core/core/src/worker/mod.rs +183 -64
  48. package/sdk-core/core/src/worker/workflow/bridge.rs +1 -3
  49. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -5
  50. package/sdk-core/core/src/worker/workflow/history_update.rs +916 -277
  51. package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +216 -183
  52. package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +9 -12
  53. package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +7 -9
  54. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +160 -87
  55. package/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +13 -14
  56. package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +7 -9
  57. package/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +14 -17
  58. package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +242 -110
  59. package/sdk-core/core/src/worker/workflow/machines/mod.rs +27 -19
  60. package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +9 -11
  61. package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +321 -206
  62. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +13 -18
  63. package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +20 -29
  64. package/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +2 -2
  65. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +257 -51
  66. package/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +6 -17
  67. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +310 -150
  68. package/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +17 -20
  69. package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +31 -15
  70. package/sdk-core/core/src/worker/workflow/managed_run.rs +1052 -380
  71. package/sdk-core/core/src/worker/workflow/mod.rs +598 -390
  72. package/sdk-core/core/src/worker/workflow/run_cache.rs +40 -57
  73. package/sdk-core/core/src/worker/workflow/wft_extraction.rs +137 -0
  74. package/sdk-core/core/src/worker/workflow/wft_poller.rs +1 -4
  75. package/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +117 -0
  76. package/sdk-core/core/src/worker/workflow/workflow_stream/tonic_status_serde.rs +24 -0
  77. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +469 -718
  78. package/sdk-core/core-api/Cargo.toml +2 -1
  79. package/sdk-core/core-api/src/errors.rs +1 -34
  80. package/sdk-core/core-api/src/lib.rs +19 -9
  81. package/sdk-core/core-api/src/telemetry.rs +4 -6
  82. package/sdk-core/core-api/src/worker.rs +19 -1
  83. package/sdk-core/etc/deps.svg +115 -140
  84. package/sdk-core/etc/regen-depgraph.sh +5 -0
  85. package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +86 -61
  86. package/sdk-core/fsm/rustfsm_trait/src/lib.rs +29 -71
  87. package/sdk-core/histories/ends_empty_wft_complete.bin +0 -0
  88. package/sdk-core/histories/evict_while_la_running_no_interference-16_history.bin +0 -0
  89. package/sdk-core/histories/old_change_marker_format.bin +0 -0
  90. package/sdk-core/protos/api_upstream/.github/CODEOWNERS +2 -1
  91. package/sdk-core/protos/api_upstream/Makefile +6 -6
  92. package/sdk-core/protos/api_upstream/build/go.mod +7 -0
  93. package/sdk-core/protos/api_upstream/build/go.sum +5 -0
  94. package/sdk-core/protos/api_upstream/build/tools.go +29 -0
  95. package/sdk-core/protos/api_upstream/go.mod +6 -0
  96. package/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +9 -2
  97. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +7 -26
  98. package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +13 -2
  99. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +3 -2
  100. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +3 -7
  101. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +3 -2
  102. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +8 -8
  103. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +25 -2
  104. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +2 -2
  105. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/query.proto +2 -2
  106. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +2 -2
  107. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/schedule.proto +2 -2
  108. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +2 -2
  109. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +24 -19
  110. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +2 -2
  111. package/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +2 -2
  112. package/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +2 -2
  113. package/sdk-core/protos/api_upstream/temporal/api/filter/v1/message.proto +2 -2
  114. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +49 -26
  115. package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +4 -2
  116. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +5 -2
  117. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +2 -2
  118. package/sdk-core/protos/api_upstream/temporal/api/protocol/v1/message.proto +57 -0
  119. package/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +2 -2
  120. package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +2 -2
  121. package/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +2 -2
  122. package/sdk-core/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +63 -0
  123. package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +2 -2
  124. package/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +71 -6
  125. package/sdk-core/protos/api_upstream/temporal/api/version/v1/message.proto +2 -2
  126. package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +2 -2
  127. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +64 -28
  128. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +4 -4
  129. package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +7 -8
  130. package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +10 -7
  131. package/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +19 -30
  132. package/sdk-core/protos/local/temporal/sdk/core/common/common.proto +1 -0
  133. package/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +1 -0
  134. package/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +8 -0
  135. package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +67 -60
  136. package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +85 -84
  137. package/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +9 -3
  138. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +2 -2
  139. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +2 -2
  140. package/sdk-core/sdk/Cargo.toml +5 -4
  141. package/sdk-core/sdk/src/lib.rs +108 -26
  142. package/sdk-core/sdk/src/workflow_context/options.rs +7 -1
  143. package/sdk-core/sdk/src/workflow_context.rs +24 -17
  144. package/sdk-core/sdk/src/workflow_future.rs +16 -15
  145. package/sdk-core/sdk-core-protos/Cargo.toml +5 -2
  146. package/sdk-core/sdk-core-protos/build.rs +36 -2
  147. package/sdk-core/sdk-core-protos/src/history_builder.rs +138 -106
  148. package/sdk-core/sdk-core-protos/src/history_info.rs +10 -1
  149. package/sdk-core/sdk-core-protos/src/lib.rs +272 -87
  150. package/sdk-core/sdk-core-protos/src/task_token.rs +12 -2
  151. package/sdk-core/test-utils/Cargo.toml +3 -1
  152. package/sdk-core/test-utils/src/canned_histories.rs +106 -296
  153. package/sdk-core/test-utils/src/histfetch.rs +1 -1
  154. package/sdk-core/test-utils/src/lib.rs +82 -23
  155. package/sdk-core/test-utils/src/wf_input_saver.rs +50 -0
  156. package/sdk-core/test-utils/src/workflows.rs +29 -0
  157. package/sdk-core/tests/fuzzy_workflow.rs +130 -0
  158. package/sdk-core/tests/{load_tests.rs → heavy_tests.rs} +125 -51
  159. package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +25 -3
  160. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +10 -5
  161. package/sdk-core/tests/integ_tests/metrics_tests.rs +218 -16
  162. package/sdk-core/tests/integ_tests/polling_tests.rs +4 -47
  163. package/sdk-core/tests/integ_tests/queries_tests.rs +5 -128
  164. package/sdk-core/tests/integ_tests/visibility_tests.rs +83 -25
  165. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +161 -72
  166. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -0
  167. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +6 -13
  168. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +80 -3
  169. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +6 -2
  170. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -10
  171. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +94 -200
  172. package/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +2 -4
  173. package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +34 -28
  174. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +76 -7
  175. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -0
  176. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +18 -14
  177. package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +6 -20
  178. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +10 -21
  179. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +7 -8
  180. package/sdk-core/tests/integ_tests/workflow_tests.rs +13 -14
  181. package/sdk-core/tests/main.rs +3 -13
  182. package/sdk-core/tests/runner.rs +75 -36
  183. package/sdk-core/tests/wf_input_replay.rs +32 -0
  184. package/src/conversions.rs +14 -8
  185. package/src/runtime.rs +9 -8
  186. package/ts/index.ts +8 -6
  187. package/sdk-core/bridge-ffi/Cargo.toml +0 -24
  188. package/sdk-core/bridge-ffi/LICENSE.txt +0 -23
  189. package/sdk-core/bridge-ffi/build.rs +0 -25
  190. package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +0 -224
  191. package/sdk-core/bridge-ffi/src/lib.rs +0 -746
  192. package/sdk-core/bridge-ffi/src/wrappers.rs +0 -221
  193. package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +0 -210
  194. package/sdk-core/sdk/src/conversions.rs +0 -8
@@ -21,19 +21,37 @@ use super::{
21
21
  workflow_machines::MachineResponse, Cancellable, EventInfo, NewMachineWithCommand,
22
22
  OnEventWrapper, WFMachinesAdapter, WFMachinesError,
23
23
  };
24
- use crate::protosext::HistoryEventExt;
25
- use rustfsm::{fsm, TransitionResult};
26
- use std::convert::TryFrom;
24
+ use crate::{
25
+ internal_flags::CoreInternalFlags,
26
+ protosext::HistoryEventExt,
27
+ worker::workflow::{
28
+ machines::{
29
+ upsert_search_attributes_state_machine::MAX_SEARCH_ATTR_PAYLOAD_SIZE, HistEventData,
30
+ },
31
+ InternalFlagsRef,
32
+ },
33
+ };
34
+ use anyhow::Context;
35
+ use rustfsm::{fsm, StateMachine, TransitionResult};
36
+ use std::{
37
+ collections::{BTreeSet, HashMap},
38
+ convert::TryFrom,
39
+ };
27
40
  use temporal_sdk_core_protos::{
28
41
  constants::PATCH_MARKER_NAME,
29
- coresdk::common::build_has_change_marker_details,
42
+ coresdk::{common::build_has_change_marker_details, AsJsonPayloadExt},
30
43
  temporal::api::{
31
- command::v1::{Command, RecordMarkerCommandAttributes},
44
+ command::v1::{
45
+ Command, RecordMarkerCommandAttributes, UpsertWorkflowSearchAttributesCommandAttributes,
46
+ },
47
+ common::v1::SearchAttributes,
32
48
  enums::v1::CommandType,
33
49
  history::v1::HistoryEvent,
34
50
  },
35
51
  };
36
52
 
53
+ pub(crate) const VERSION_SEARCH_ATTR_KEY: &str = "TemporalChangeVersion";
54
+
37
55
  fsm! {
38
56
  pub(super) name PatchMachine;
39
57
  command PatchCommand;
@@ -71,53 +89,90 @@ pub(super) enum PatchCommand {}
71
89
  /// are guaranteed to return the same value.
72
90
  /// `replaying_when_invoked`: If the workflow is replaying when this invocation occurs, this needs
73
91
  /// to be set to true.
74
- pub(super) fn has_change(
92
+ pub(super) fn has_change<'a>(
75
93
  patch_id: String,
76
94
  replaying_when_invoked: bool,
77
95
  deprecated: bool,
78
- ) -> NewMachineWithCommand {
79
- let (machine, command) =
80
- PatchMachine::new_scheduled(SharedState { patch_id }, replaying_when_invoked, deprecated);
81
- NewMachineWithCommand {
82
- command,
83
- machine: machine.into(),
84
- }
85
- }
86
-
87
- impl PatchMachine {
88
- fn new_scheduled(
89
- state: SharedState,
90
- replaying_when_invoked: bool,
91
- deprecated: bool,
92
- ) -> (Self, Command) {
93
- let initial_state = if replaying_when_invoked {
94
- Replaying {}.into()
96
+ seen_in_peekahead: bool,
97
+ existing_patch_ids: impl Iterator<Item = &'a str>,
98
+ internal_flags: InternalFlagsRef,
99
+ ) -> Result<(NewMachineWithCommand, Vec<MachineResponse>), WFMachinesError> {
100
+ let shared_state = SharedState { patch_id };
101
+ let initial_state = if replaying_when_invoked {
102
+ Replaying {}.into()
103
+ } else {
104
+ Executing {}.into()
105
+ };
106
+ let command = Command {
107
+ command_type: CommandType::RecordMarker as i32,
108
+ attributes: Some(
109
+ RecordMarkerCommandAttributes {
110
+ marker_name: PATCH_MARKER_NAME.to_string(),
111
+ details: build_has_change_marker_details(&shared_state.patch_id, deprecated)
112
+ .context("While encoding patch marker details")?,
113
+ header: None,
114
+ failure: None,
115
+ }
116
+ .into(),
117
+ ),
118
+ };
119
+ let mut machine = PatchMachine::from_parts(initial_state, shared_state);
120
+
121
+ OnEventWrapper::on_event_mut(&mut machine, PatchMachineEvents::Schedule)
122
+ .expect("Patch machine scheduling doesn't fail");
123
+
124
+ // If we're replaying but this patch isn't in the peekahead, then we wouldn't have
125
+ // upserted either, and thus should not create the machine
126
+ let replaying_and_not_in_history = replaying_when_invoked && !seen_in_peekahead;
127
+ let cannot_use_flag = !internal_flags.borrow_mut().try_use(
128
+ CoreInternalFlags::UpsertSearchAttributeOnPatch,
129
+ !replaying_when_invoked,
130
+ );
131
+ let maybe_upsert_cmd = if replaying_and_not_in_history || cannot_use_flag {
132
+ vec![]
133
+ } else {
134
+ // Produce an upsert SA command for this patch.
135
+ let mut all_ids = BTreeSet::from_iter(existing_patch_ids);
136
+ all_ids.insert(machine.shared_state.patch_id.as_str());
137
+ let serialized = all_ids
138
+ .as_json_payload()
139
+ .context("Could not serialize search attribute value for patch machine")
140
+ .map_err(|e| WFMachinesError::Fatal(e.to_string()))?;
141
+
142
+ if serialized.data.len() >= MAX_SEARCH_ATTR_PAYLOAD_SIZE {
143
+ warn!(
144
+ "Serialized size of {VERSION_SEARCH_ATTR_KEY} search attribute update would \
145
+ exceed the maximum value size. Skipping this upsert. Be aware that your \
146
+ visibility records will not include the following patch: {}",
147
+ machine.shared_state.patch_id
148
+ );
149
+ vec![]
95
150
  } else {
96
- Executing {}.into()
97
- };
98
- let cmd = Command {
99
- command_type: CommandType::RecordMarker as i32,
100
- attributes: Some(
101
- RecordMarkerCommandAttributes {
102
- marker_name: PATCH_MARKER_NAME.to_string(),
103
- details: build_has_change_marker_details(&state.patch_id, deprecated),
104
- header: None,
105
- failure: None,
151
+ let indexed_fields = {
152
+ let mut m = HashMap::new();
153
+ m.insert(VERSION_SEARCH_ATTR_KEY.to_string(), serialized);
154
+ m
155
+ };
156
+ vec![MachineResponse::NewCoreOriginatedCommand(
157
+ UpsertWorkflowSearchAttributesCommandAttributes {
158
+ search_attributes: Some(SearchAttributes { indexed_fields }),
106
159
  }
107
160
  .into(),
108
- ),
109
- };
110
- let mut machine = Self {
111
- state: initial_state,
112
- shared_state: state,
113
- };
114
- OnEventWrapper::on_event_mut(&mut machine, PatchMachineEvents::Schedule)
115
- .expect("Patch machine scheduling doesn't fail");
161
+ )]
162
+ }
163
+ };
116
164
 
117
- (machine, cmd)
118
- }
165
+ Ok((
166
+ NewMachineWithCommand {
167
+ command,
168
+ machine: machine.into(),
169
+ },
170
+ maybe_upsert_cmd,
171
+ ))
119
172
  }
120
173
 
174
+ impl PatchMachine {}
175
+
121
176
  #[derive(Default, Clone)]
122
177
  pub(super) struct Executing {}
123
178
 
@@ -132,7 +187,7 @@ pub(super) struct MarkerCommandCreated {}
132
187
 
133
188
  impl MarkerCommandCreated {
134
189
  pub(super) fn on_command_record_marker(self) -> PatchMachineTransition<Notified> {
135
- TransitionResult::commands(vec![])
190
+ TransitionResult::default()
136
191
  }
137
192
  }
138
193
 
@@ -161,7 +216,7 @@ impl From<MarkerCommandCreatedReplaying> for Notified {
161
216
  impl Notified {
162
217
  pub(super) fn on_marker_recorded(
163
218
  self,
164
- dat: SharedState,
219
+ dat: &mut SharedState,
165
220
  id: String,
166
221
  ) -> PatchMachineTransition<MarkerCommandRecorded> {
167
222
  if id != dat.patch_id {
@@ -201,15 +256,15 @@ impl TryFrom<CommandType> for PatchMachineEvents {
201
256
  }
202
257
  }
203
258
 
204
- impl TryFrom<HistoryEvent> for PatchMachineEvents {
259
+ impl TryFrom<HistEventData> for PatchMachineEvents {
205
260
  type Error = WFMachinesError;
206
261
 
207
- fn try_from(e: HistoryEvent) -> Result<Self, Self::Error> {
262
+ fn try_from(e: HistEventData) -> Result<Self, Self::Error> {
263
+ let e = e.event;
208
264
  match e.get_patch_marker_details() {
209
265
  Some((id, _)) => Ok(Self::MarkerRecorded(id)),
210
266
  _ => Err(WFMachinesError::Nondeterminism(format!(
211
- "Change machine cannot handle this event: {}",
212
- e
267
+ "Change machine cannot handle this event: {e}"
213
268
  ))),
214
269
  }
215
270
  }
@@ -218,35 +273,43 @@ impl TryFrom<HistoryEvent> for PatchMachineEvents {
218
273
  #[cfg(test)]
219
274
  mod tests {
220
275
  use crate::{
276
+ internal_flags::CoreInternalFlags,
221
277
  replay::TestHistoryBuilder,
222
- worker::workflow::{machines::WFMachinesError, ManagedWFFunc},
278
+ worker::workflow::{
279
+ machines::{patch_state_machine::VERSION_SEARCH_ATTR_KEY, WFMachinesError},
280
+ ManagedWFFunc,
281
+ },
223
282
  };
224
283
  use rstest::rstest;
225
- use std::time::Duration;
284
+ use std::{
285
+ collections::{hash_map::RandomState, HashSet, VecDeque},
286
+ time::Duration,
287
+ };
226
288
  use temporal_sdk::{ActivityOptions, WfContext, WorkflowFunction};
227
289
  use temporal_sdk_core_protos::{
228
290
  constants::PATCH_MARKER_NAME,
229
291
  coresdk::{
230
292
  common::decode_change_marker_details,
231
293
  workflow_activation::{workflow_activation_job, NotifyHasPatch, WorkflowActivationJob},
294
+ AsJsonPayloadExt, FromJsonPayloadExt,
232
295
  },
233
296
  temporal::api::{
234
297
  command::v1::{
235
298
  command::Attributes, RecordMarkerCommandAttributes,
236
299
  ScheduleActivityTaskCommandAttributes,
300
+ UpsertWorkflowSearchAttributesCommandAttributes,
237
301
  },
238
302
  common::v1::ActivityType,
239
303
  enums::v1::{CommandType, EventType},
240
304
  history::v1::{
241
- history_event, ActivityTaskCompletedEventAttributes,
242
- ActivityTaskScheduledEventAttributes, ActivityTaskStartedEventAttributes,
243
- TimerFiredEventAttributes,
305
+ ActivityTaskCompletedEventAttributes, ActivityTaskScheduledEventAttributes,
306
+ ActivityTaskStartedEventAttributes, TimerFiredEventAttributes,
244
307
  },
245
308
  },
246
309
  };
247
310
 
248
311
  const MY_PATCH_ID: &str = "test_patch_id";
249
- #[derive(Eq, PartialEq, Copy, Clone)]
312
+ #[derive(Eq, PartialEq, Copy, Clone, Debug)]
250
313
  enum MarkerType {
251
314
  Deprecated,
252
315
  NotDeprecated,
@@ -267,51 +330,65 @@ mod tests {
267
330
  /// EVENT_TYPE_WORKFLOW_TASK_STARTED
268
331
  /// EVENT_TYPE_WORKFLOW_TASK_COMPLETED
269
332
  /// EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED
270
- fn patch_marker_single_activity(marker_type: MarkerType) -> TestHistoryBuilder {
333
+ fn patch_marker_single_activity(
334
+ marker_type: MarkerType,
335
+ version: usize,
336
+ replay: bool,
337
+ ) -> TestHistoryBuilder {
271
338
  let mut t = TestHistoryBuilder::default();
272
339
  t.add_by_type(EventType::WorkflowExecutionStarted);
273
340
  t.add_full_wf_task();
341
+ t.set_flags_first_wft(
342
+ &[CoreInternalFlags::UpsertSearchAttributeOnPatch as u32],
343
+ &[],
344
+ );
274
345
  match marker_type {
275
- MarkerType::Deprecated => t.add_has_change_marker(MY_PATCH_ID, true),
276
- MarkerType::NotDeprecated => t.add_has_change_marker(MY_PATCH_ID, false),
346
+ MarkerType::Deprecated => {
347
+ t.add_has_change_marker(MY_PATCH_ID, true);
348
+ t.add_upsert_search_attrs_for_patch(&[MY_PATCH_ID.to_string()]);
349
+ }
350
+ MarkerType::NotDeprecated => {
351
+ t.add_has_change_marker(MY_PATCH_ID, false);
352
+ t.add_upsert_search_attrs_for_patch(&[MY_PATCH_ID.to_string()]);
353
+ }
277
354
  MarkerType::NoMarker => {}
278
355
  };
279
356
 
280
- let scheduled_event_id = t.add_get_event_id(
281
- EventType::ActivityTaskScheduled,
282
- Some(
283
- history_event::Attributes::ActivityTaskScheduledEventAttributes(
284
- ActivityTaskScheduledEventAttributes {
285
- activity_id: "0".to_string(),
286
- activity_type: Some(ActivityType {
287
- name: "".to_string(),
288
- }),
289
- ..Default::default()
290
- },
291
- ),
292
- ),
293
- );
294
- let started_event_id = t.add_get_event_id(
295
- EventType::ActivityTaskStarted,
296
- Some(
297
- history_event::Attributes::ActivityTaskStartedEventAttributes(
298
- ActivityTaskStartedEventAttributes {
299
- scheduled_event_id,
300
- ..Default::default()
301
- },
302
- ),
303
- ),
304
- );
305
- t.add(
306
- EventType::ActivityTaskCompleted,
307
- history_event::Attributes::ActivityTaskCompletedEventAttributes(
308
- ActivityTaskCompletedEventAttributes {
309
- scheduled_event_id,
310
- started_event_id,
311
- ..Default::default()
312
- },
313
- ),
314
- );
357
+ let activity_id = if replay {
358
+ match (marker_type, version) {
359
+ (_, 1) => "no_change",
360
+ (MarkerType::NotDeprecated, 2) => "had_change",
361
+ (MarkerType::Deprecated, 2) => "had_change",
362
+ (MarkerType::NoMarker, 2) => "no_change",
363
+ (_, 3) => "had_change",
364
+ (_, 4) => "had_change",
365
+ v => panic!("Nonsense marker / version combo {v:?}"),
366
+ }
367
+ } else {
368
+ // If the workflow isn't replaying (we're creating history here for a workflow which
369
+ // wasn't replaying at the time of scheduling the activity, and has done that, and now
370
+ // we're feeding back the history it would have produced) then it always has the change,
371
+ // except in v1.
372
+ if version > 1 {
373
+ "had_change"
374
+ } else {
375
+ "no_change"
376
+ }
377
+ };
378
+
379
+ let scheduled_event_id = t.add(ActivityTaskScheduledEventAttributes {
380
+ activity_id: activity_id.to_string(),
381
+ ..Default::default()
382
+ });
383
+ let started_event_id = t.add(ActivityTaskStartedEventAttributes {
384
+ scheduled_event_id,
385
+ ..Default::default()
386
+ });
387
+ t.add(ActivityTaskCompletedEventAttributes {
388
+ scheduled_event_id,
389
+ started_event_id,
390
+ ..Default::default()
391
+ });
315
392
  t.add_full_wf_task();
316
393
  t.add_workflow_execution_completed();
317
394
  t
@@ -384,7 +461,7 @@ mod tests {
384
461
  Ok(().into())
385
462
  });
386
463
 
387
- let t = patch_marker_single_activity(marker_type);
464
+ let t = patch_marker_single_activity(marker_type, workflow_version, replaying);
388
465
  let histinfo = if replaying {
389
466
  t.get_full_history_info()
390
467
  } else {
@@ -422,7 +499,7 @@ mod tests {
422
499
  } else {
423
500
  // Feed more history
424
501
  wfm.new_history(
425
- patch_marker_single_activity(marker_type)
502
+ patch_marker_single_activity(marker_type, wf_version, replaying)
426
503
  .get_full_history_info()
427
504
  .unwrap()
428
505
  .into(),
@@ -447,14 +524,13 @@ mod tests {
447
524
  wfm.shutdown().await.unwrap();
448
525
  }
449
526
 
527
+ // Note that the not-replaying and no-marker cases don't make sense and hence are absent
450
528
  #[rstest]
451
- #[case::v2_no_marker_old_path(false, MarkerType::NoMarker, 2)]
452
529
  #[case::v2_marker_new_path(false, MarkerType::NotDeprecated, 2)]
453
530
  #[case::v2_dep_marker_new_path(false, MarkerType::Deprecated, 2)]
454
531
  #[case::v2_replay_no_marker_old_path(true, MarkerType::NoMarker, 2)]
455
532
  #[case::v2_replay_marker_new_path(true, MarkerType::NotDeprecated, 2)]
456
533
  #[case::v2_replay_dep_marker_new_path(true, MarkerType::Deprecated, 2)]
457
- #[case::v3_no_marker_old_path(false, MarkerType::NoMarker, 3)]
458
534
  #[case::v3_marker_new_path(false, MarkerType::NotDeprecated, 3)]
459
535
  #[case::v3_dep_marker_new_path(false, MarkerType::Deprecated, 3)]
460
536
  #[case::v3_replay_no_marker_old_path(true, MarkerType::NoMarker, 3)]
@@ -483,17 +559,32 @@ mod tests {
483
559
  } else {
484
560
  assert_eq!(act.jobs.len(), 1);
485
561
  }
486
- let commands = wfm.get_server_commands().commands;
487
- assert_eq!(commands.len(), 2);
562
+ let mut commands = VecDeque::from(wfm.get_server_commands().commands);
563
+ let expected_num_cmds = if marker_type == MarkerType::NoMarker {
564
+ 2
565
+ } else {
566
+ 3
567
+ };
568
+ assert_eq!(commands.len(), expected_num_cmds);
488
569
  let dep_flag_expected = wf_version != 2;
489
570
  assert_matches!(
490
- commands[0].attributes.as_ref().unwrap(),
571
+ commands.pop_front().unwrap().attributes.as_ref().unwrap(),
491
572
  Attributes::RecordMarkerCommandAttributes(
492
573
  RecordMarkerCommandAttributes { marker_name, details,.. })
493
574
 
494
575
  if marker_name == PATCH_MARKER_NAME
495
576
  && decode_change_marker_details(details).unwrap().1 == dep_flag_expected
496
577
  );
578
+ if expected_num_cmds == 3 {
579
+ assert_matches!(
580
+ commands.pop_front().unwrap().attributes.as_ref().unwrap(),
581
+ Attributes::UpsertWorkflowSearchAttributesCommandAttributes(
582
+ UpsertWorkflowSearchAttributesCommandAttributes{ search_attributes: Some(attrs) }
583
+ )
584
+ if attrs.indexed_fields.get(VERSION_SEARCH_ATTR_KEY).unwrap()
585
+ == &[MY_PATCH_ID].as_json_payload().unwrap()
586
+ );
587
+ }
497
588
  // The only time the "old" timer should fire is in v2, replaying, without a marker.
498
589
  let expected_activity_id =
499
590
  if replaying && marker_type == MarkerType::NoMarker && wf_version == 2 {
@@ -502,7 +593,7 @@ mod tests {
502
593
  "had_change"
503
594
  };
504
595
  assert_matches!(
505
- commands[1].attributes.as_ref().unwrap(),
596
+ commands.pop_front().unwrap().attributes.as_ref().unwrap(),
506
597
  Attributes::ScheduleActivityTaskCommandAttributes(
507
598
  ScheduleActivityTaskCommandAttributes { activity_id, .. }
508
599
  )
@@ -516,7 +607,7 @@ mod tests {
516
607
  // and the history should have the has-change timer. v3 of course always has the change
517
608
  // regardless.
518
609
  wfm.new_history(
519
- patch_marker_single_activity(marker_type)
610
+ patch_marker_single_activity(marker_type, wf_version, replaying)
520
611
  .get_full_history_info()
521
612
  .unwrap()
522
613
  .into(),
@@ -563,120 +654,73 @@ mod tests {
563
654
  let mut t = TestHistoryBuilder::default();
564
655
  t.add_by_type(EventType::WorkflowExecutionStarted);
565
656
  t.add_full_wf_task();
657
+ t.set_flags_first_wft(
658
+ &[CoreInternalFlags::UpsertSearchAttributeOnPatch as u32],
659
+ &[],
660
+ );
566
661
  if have_marker_in_hist {
567
662
  t.add_has_change_marker(MY_PATCH_ID, false);
568
- let scheduled_event_id = t.add_get_event_id(
569
- EventType::ActivityTaskScheduled,
570
- Some(
571
- history_event::Attributes::ActivityTaskScheduledEventAttributes(
572
- ActivityTaskScheduledEventAttributes {
573
- activity_id: "1".to_owned(),
574
- activity_type: Some(ActivityType {
575
- name: "".to_string(),
576
- }),
577
- ..Default::default()
578
- },
579
- ),
580
- ),
581
- );
582
- let started_event_id = t.add_get_event_id(
583
- EventType::ActivityTaskStarted,
584
- Some(
585
- history_event::Attributes::ActivityTaskStartedEventAttributes(
586
- ActivityTaskStartedEventAttributes {
587
- scheduled_event_id,
588
- ..Default::default()
589
- },
590
- ),
591
- ),
592
- );
593
- t.add(
594
- EventType::ActivityTaskCompleted,
595
- history_event::Attributes::ActivityTaskCompletedEventAttributes(
596
- ActivityTaskCompletedEventAttributes {
597
- scheduled_event_id,
598
- started_event_id,
599
- // TODO result: Some(Payloads { payloads: vec![Payload{ metadata: Default::default(), data: vec![] }] }),
600
- ..Default::default()
601
- },
602
- ),
603
- );
604
- t.add_full_wf_task();
605
- let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
606
- t.add(
607
- EventType::TimerFired,
608
- history_event::Attributes::TimerFiredEventAttributes(TimerFiredEventAttributes {
609
- started_event_id: timer_started_event_id,
610
- timer_id: "1".to_owned(),
663
+ t.add_upsert_search_attrs_for_patch(&[MY_PATCH_ID.to_string()]);
664
+ let scheduled_event_id = t.add(ActivityTaskScheduledEventAttributes {
665
+ activity_id: "1".to_owned(),
666
+ activity_type: Some(ActivityType {
667
+ name: "".to_string(),
611
668
  }),
612
- );
669
+ ..Default::default()
670
+ });
671
+ let started_event_id = t.add(ActivityTaskStartedEventAttributes {
672
+ scheduled_event_id,
673
+ ..Default::default()
674
+ });
675
+ t.add(ActivityTaskCompletedEventAttributes {
676
+ scheduled_event_id,
677
+ started_event_id,
678
+ ..Default::default()
679
+ });
680
+ t.add_full_wf_task();
681
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
682
+ t.add(TimerFiredEventAttributes {
683
+ started_event_id: timer_started_event_id,
684
+ timer_id: "1".to_owned(),
685
+ });
613
686
  } else {
614
- let started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
615
- t.add(
616
- EventType::TimerFired,
617
- history_event::Attributes::TimerFiredEventAttributes(TimerFiredEventAttributes {
618
- started_event_id,
619
- timer_id: "1".to_owned(),
620
- }),
621
- );
687
+ let started_event_id = t.add_by_type(EventType::TimerStarted);
688
+ t.add(TimerFiredEventAttributes {
689
+ started_event_id,
690
+ timer_id: "1".to_owned(),
691
+ });
622
692
  t.add_full_wf_task();
623
- let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
624
- t.add(
625
- EventType::TimerFired,
626
- history_event::Attributes::TimerFiredEventAttributes(TimerFiredEventAttributes {
627
- started_event_id: timer_started_event_id,
628
- timer_id: "2".to_owned(),
629
- }),
630
- );
693
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
694
+ t.add(TimerFiredEventAttributes {
695
+ started_event_id: timer_started_event_id,
696
+ timer_id: "2".to_owned(),
697
+ });
631
698
  }
632
699
  t.add_full_wf_task();
633
700
 
634
701
  if have_marker_in_hist {
635
- let scheduled_event_id = t.add_get_event_id(
636
- EventType::ActivityTaskScheduled,
637
- Some(
638
- history_event::Attributes::ActivityTaskScheduledEventAttributes(
639
- ActivityTaskScheduledEventAttributes {
640
- activity_id: "2".to_string(),
641
- activity_type: Some(ActivityType {
642
- name: "".to_string(),
643
- }),
644
- ..Default::default()
645
- },
646
- ),
647
- ),
648
- );
649
- let started_event_id = t.add_get_event_id(
650
- EventType::ActivityTaskStarted,
651
- Some(
652
- history_event::Attributes::ActivityTaskStartedEventAttributes(
653
- ActivityTaskStartedEventAttributes {
654
- scheduled_event_id,
655
- ..Default::default()
656
- },
657
- ),
658
- ),
659
- );
660
- t.add(
661
- EventType::ActivityTaskCompleted,
662
- history_event::Attributes::ActivityTaskCompletedEventAttributes(
663
- ActivityTaskCompletedEventAttributes {
664
- scheduled_event_id,
665
- started_event_id,
666
- // TODO result: Some(Payloads { payloads: vec![Payload{ metadata: Default::default(), data: vec![] }] }),
667
- ..Default::default()
668
- },
669
- ),
670
- );
671
- } else {
672
- let started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
673
- t.add(
674
- EventType::TimerFired,
675
- history_event::Attributes::TimerFiredEventAttributes(TimerFiredEventAttributes {
676
- started_event_id,
677
- timer_id: "3".to_owned(),
702
+ let scheduled_event_id = t.add(ActivityTaskScheduledEventAttributes {
703
+ activity_id: "2".to_string(),
704
+ activity_type: Some(ActivityType {
705
+ name: "".to_string(),
678
706
  }),
679
- );
707
+ ..Default::default()
708
+ });
709
+ let started_event_id = t.add(ActivityTaskStartedEventAttributes {
710
+ scheduled_event_id,
711
+ ..Default::default()
712
+ });
713
+ t.add(ActivityTaskCompletedEventAttributes {
714
+ scheduled_event_id,
715
+ started_event_id,
716
+ ..Default::default()
717
+ });
718
+ } else {
719
+ let started_event_id = t.add_by_type(EventType::TimerStarted);
720
+ t.add(TimerFiredEventAttributes {
721
+ started_event_id,
722
+ timer_id: "3".to_owned(),
723
+ });
680
724
  }
681
725
  t.add_full_wf_task();
682
726
  t.add_workflow_execution_completed();
@@ -705,4 +749,75 @@ mod tests {
705
749
 
706
750
  wfm.shutdown().await.unwrap();
707
751
  }
752
+
753
+ const SIZE_OVERFLOW_PATCH_AMOUNT: usize = 180;
754
+ #[rstest]
755
+ #[case::happy_path(50)]
756
+ // We start exceeding the 2k size limit at 180 patches with this format
757
+ #[case::size_overflow(SIZE_OVERFLOW_PATCH_AMOUNT)]
758
+ #[tokio::test]
759
+ async fn many_patches_combine_in_search_attrib_update(#[case] num_patches: usize) {
760
+ let mut t = TestHistoryBuilder::default();
761
+ t.add_by_type(EventType::WorkflowExecutionStarted);
762
+ t.add_full_wf_task();
763
+ t.set_flags_first_wft(
764
+ &[CoreInternalFlags::UpsertSearchAttributeOnPatch as u32],
765
+ &[],
766
+ );
767
+ for i in 1..=num_patches {
768
+ let id = format!("patch-{i}");
769
+ t.add_has_change_marker(&id, false);
770
+ if i < SIZE_OVERFLOW_PATCH_AMOUNT {
771
+ t.add_upsert_search_attrs_for_patch(&[id]);
772
+ }
773
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
774
+ t.add(TimerFiredEventAttributes {
775
+ started_event_id: timer_started_event_id,
776
+ timer_id: i.to_string(),
777
+ });
778
+ t.add_full_wf_task();
779
+ }
780
+ t.add_workflow_execution_completed();
781
+
782
+ let mut wfm = ManagedWFFunc::new_from_update(
783
+ t.get_history_info(1).unwrap().into(),
784
+ WorkflowFunction::new(move |ctx: WfContext| async move {
785
+ for i in 1..=num_patches {
786
+ let _dontcare = ctx.patched(&format!("patch-{i}"));
787
+ ctx.timer(ONE_SECOND).await;
788
+ }
789
+ Ok(().into())
790
+ }),
791
+ vec![],
792
+ );
793
+ // Iterate through all activations/responses except the final one with complete workflow
794
+ for i in 2..=num_patches + 1 {
795
+ wfm.get_next_activation().await.unwrap();
796
+ let cmds = wfm.get_server_commands();
797
+ wfm.new_history(t.get_history_info(i).unwrap().into())
798
+ .await
799
+ .unwrap();
800
+ if i > SIZE_OVERFLOW_PATCH_AMOUNT {
801
+ assert_eq!(2, cmds.commands.len());
802
+ assert_matches!(cmds.commands[1].command_type(), CommandType::StartTimer);
803
+ continue;
804
+ }
805
+ assert_eq!(3, cmds.commands.len());
806
+ let attrs = assert_matches!(
807
+ cmds.commands[1].attributes.as_ref().unwrap(),
808
+ Attributes::UpsertWorkflowSearchAttributesCommandAttributes(
809
+ UpsertWorkflowSearchAttributesCommandAttributes{ search_attributes: Some(attrs) }
810
+ )
811
+ => attrs
812
+ );
813
+ let expected_patches: HashSet<String, _> =
814
+ (1..i).map(|i| format!("patch-{i}")).collect();
815
+ let deserialized = HashSet::<String, RandomState>::from_json_payload(
816
+ attrs.indexed_fields.get(VERSION_SEARCH_ATTR_KEY).unwrap(),
817
+ )
818
+ .unwrap();
819
+ assert_eq!(deserialized, expected_patches);
820
+ }
821
+ wfm.shutdown().await.unwrap();
822
+ }
708
823
  }