@temporalio/core-bridge 1.9.0-rc.0 → 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 (57) hide show
  1. package/Cargo.lock +511 -472
  2. package/Cargo.toml +1 -1
  3. package/lib/errors.js +6 -6
  4. package/lib/errors.js.map +1 -1
  5. package/package.json +5 -5
  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/Cargo.toml +4 -0
  12. package/sdk-core/LICENSE.txt +0 -2
  13. package/sdk-core/client/Cargo.toml +1 -1
  14. package/sdk-core/client/src/lib.rs +2 -4
  15. package/sdk-core/core/Cargo.toml +1 -1
  16. package/sdk-core/core/src/core_tests/activity_tasks.rs +4 -2
  17. package/sdk-core/core/src/core_tests/queries.rs +42 -1
  18. package/sdk-core/core/src/protosext/mod.rs +1 -1
  19. package/sdk-core/core/src/replay/mod.rs +1 -2
  20. package/sdk-core/core/src/telemetry/mod.rs +19 -19
  21. package/sdk-core/core/src/test_help/mod.rs +1 -1
  22. package/sdk-core/core/src/worker/mod.rs +5 -19
  23. package/sdk-core/core/src/worker/workflow/history_update.rs +1 -1
  24. package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +35 -1
  25. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +1 -1
  26. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +1 -1
  27. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +23 -15
  28. package/sdk-core/core/src/worker/workflow/mod.rs +8 -12
  29. package/sdk-core/core/src/worker/workflow/run_cache.rs +9 -15
  30. package/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +2 -2
  31. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +4 -6
  32. package/sdk-core/core-api/Cargo.toml +1 -1
  33. package/sdk-core/fsm/LICENSE.txt +0 -2
  34. package/sdk-core/fsm/rustfsm_procmacro/LICENSE.txt +0 -2
  35. package/sdk-core/fsm/rustfsm_trait/LICENSE.txt +0 -2
  36. package/sdk-core/sdk/Cargo.toml +1 -1
  37. package/sdk-core/sdk/src/workflow_future.rs +2 -2
  38. package/sdk-core/sdk-core-protos/Cargo.toml +1 -1
  39. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/batch/v1/message.proto +9 -4
  40. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/common/v1/message.proto +31 -0
  41. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/reset.proto +3 -3
  42. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/export/v1/message.proto +1 -1
  43. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/workflow_metadata.proto +66 -0
  44. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflow/v1/message.proto +6 -2
  45. package/sdk-core/sdk-core-protos/src/history_info.rs +1 -1
  46. package/sdk-core/sdk-core-protos/src/lib.rs +1 -1
  47. package/sdk-core/test-utils/Cargo.toml +1 -0
  48. package/sdk-core/tests/integ_tests/visibility_tests.rs +4 -4
  49. package/sdk-core/tests/integ_tests/workflow_tests.rs +141 -3
  50. package/src/conversions.rs +47 -22
  51. package/src/runtime.rs +13 -10
  52. package/ts/index.ts +2 -2
  53. package/sdk-core/client/LICENSE.txt +0 -23
  54. package/sdk-core/core/LICENSE.txt +0 -23
  55. package/sdk-core/core-api/LICENSE.txt +0 -23
  56. package/sdk-core/sdk/LICENSE.txt +0 -23
  57. package/sdk-core/sdk-core-protos/LICENSE.txt +0 -23
@@ -7,13 +7,12 @@ use crate::{
7
7
  MetricsContext,
8
8
  };
9
9
  use lru::LruCache;
10
- use std::{num::NonZeroUsize, rc::Rc};
10
+ use std::{num::NonZeroUsize, rc::Rc, sync::Arc};
11
+ use temporal_sdk_core_api::worker::WorkerConfig;
11
12
  use temporal_sdk_core_protos::temporal::api::workflowservice::v1::get_system_info_response;
12
13
 
13
14
  pub(super) struct RunCache {
14
- max: usize,
15
- namespace: String,
16
- task_queue: String,
15
+ worker_config: Arc<WorkerConfig>,
17
16
  server_capabilities: get_system_info_response::Capabilities,
18
17
  /// Run id -> Data
19
18
  runs: LruCache<String, ManagedRun>,
@@ -24,24 +23,20 @@ pub(super) struct RunCache {
24
23
 
25
24
  impl RunCache {
26
25
  pub fn new(
27
- max_cache_size: usize,
28
- namespace: String,
29
- task_queue: String,
26
+ worker_config: Arc<WorkerConfig>,
30
27
  server_capabilities: get_system_info_response::Capabilities,
31
28
  local_activity_request_sink: impl LocalActivityRequestSink,
32
29
  metrics: MetricsContext,
33
30
  ) -> Self {
34
31
  // The cache needs room for at least one run, otherwise we couldn't do anything. In
35
32
  // "0" size mode, the run is evicted once the workflow task is complete.
36
- let lru_size = if max_cache_size > 0 {
37
- max_cache_size
33
+ let lru_size = if worker_config.max_cached_workflows > 0 {
34
+ worker_config.max_cached_workflows
38
35
  } else {
39
36
  1
40
37
  };
41
38
  Self {
42
- max: max_cache_size,
43
- namespace,
44
- task_queue,
39
+ worker_config,
45
40
  server_capabilities,
46
41
  runs: LruCache::new(
47
42
  NonZeroUsize::new(lru_size).expect("LRU size is guaranteed positive"),
@@ -68,11 +63,10 @@ impl RunCache {
68
63
  .with_new_attrs([workflow_type(pwft.work.workflow_type.clone())]);
69
64
  let (mrh, rur) = ManagedRun::new(
70
65
  RunBasics {
71
- namespace: self.namespace.clone(),
66
+ worker_config: self.worker_config.clone(),
72
67
  workflow_id: pwft.work.execution.workflow_id.clone(),
73
68
  workflow_type: pwft.work.workflow_type.clone(),
74
69
  run_id: run_id.clone(),
75
- task_queue: self.task_queue.clone(),
76
70
  history: HistoryUpdate::dummy(),
77
71
  metrics,
78
72
  capabilities: &self.server_capabilities,
@@ -124,6 +118,6 @@ impl RunCache {
124
118
  self.runs.len()
125
119
  }
126
120
  pub fn cache_capacity(&self) -> usize {
127
- self.max
121
+ self.worker_config.max_cached_workflows
128
122
  }
129
123
  }
@@ -23,7 +23,7 @@ use tokio_util::sync::CancellationToken;
23
23
  ///
24
24
  /// Use `CoreWfStarter::enable_wf_state_input_recording` from the integration test utilities to
25
25
  /// activate saving the data to disk, and use the `wf_input_replay` example binary to replay.
26
- pub async fn replay_wf_state_inputs(mut config: WorkerConfig, inputs: impl Stream<Item = Vec<u8>>) {
26
+ pub async fn replay_wf_state_inputs(config: WorkerConfig, inputs: impl Stream<Item = Vec<u8>>) {
27
27
  use crate::worker::build_wf_basics;
28
28
 
29
29
  let la_resp_q = Arc::new(SegQueue::new());
@@ -43,7 +43,7 @@ pub async fn replay_wf_state_inputs(mut config: WorkerConfig, inputs: impl Strea
43
43
  })
44
44
  });
45
45
  let basics = build_wf_basics(
46
- &mut config,
46
+ config,
47
47
  MetricsContext::no_op(),
48
48
  CancellationToken::new(),
49
49
  DEFAULT_TEST_CAPABILITIES.clone(),
@@ -95,21 +95,19 @@ impl WFStream {
95
95
  let mut state = WFStream {
96
96
  buffered_polls_need_cache_slot: Default::default(),
97
97
  runs: RunCache::new(
98
- basics.max_cached_workflows,
99
- basics.namespace.clone(),
100
- basics.task_queue.clone(),
98
+ basics.worker_config.clone(),
101
99
  basics.server_capabilities.clone(),
102
100
  local_activity_request_sink,
103
101
  basics.metrics.clone(),
104
102
  ),
105
103
  shutdown_token: basics.shutdown_token,
106
- ignore_evicts_on_shutdown: basics.ignore_evicts_on_shutdown,
104
+ ignore_evicts_on_shutdown: basics.worker_config.ignore_evicts_on_shutdown,
107
105
  metrics: basics.metrics,
108
106
  runs_needing_fetching: Default::default(),
109
107
  history_fetch_refcounter: Arc::new(HistfetchRC {}),
110
108
 
111
109
  #[cfg(feature = "save_wf_inputs")]
112
- wf_state_inputs: basics.wf_state_inputs,
110
+ wf_state_inputs: basics.worker_config.wf_state_inputs.clone(),
113
111
  };
114
112
  all_inputs
115
113
  .map(move |action: WFStreamInput| {
@@ -386,7 +384,7 @@ impl WFStream {
386
384
  // We accept that there might be query tasks remaining in the buffer if we evicted
387
385
  // and re-instantiated here. It's likely those tasks are now invalidated anyway.
388
386
  if maybe_buffered.has_tasks() && should_evict {
389
- panic!("There were leftover buffered tasks");
387
+ warn!("There were leftover buffered tasks when evicting run");
390
388
  }
391
389
  }
392
390
  }
@@ -3,7 +3,7 @@ name = "temporal-sdk-core-api"
3
3
  version = "0.1.0"
4
4
  edition = "2021"
5
5
  authors = ["Spencer Judge <spencer@temporal.io>"]
6
- license-file = "LICENSE.txt"
6
+ license-file = { workspace = true }
7
7
  description = "Interface definitions for the Temporal Core SDK"
8
8
  homepage = "https://temporal.io/"
9
9
  repository = "https://github.com/temporalio/sdk-core"
@@ -1,5 +1,3 @@
1
- Temporal Core SDK
2
-
3
1
  The MIT License
4
2
 
5
3
  Copyright (c) 2021 Temporal Technologies, Inc. All Rights Reserved
@@ -1,5 +1,3 @@
1
- Temporal Core SDK
2
-
3
1
  The MIT License
4
2
 
5
3
  Copyright (c) 2021 Temporal Technologies, Inc. All Rights Reserved
@@ -1,5 +1,3 @@
1
- Temporal Core SDK
2
-
3
1
  The MIT License
4
2
 
5
3
  Copyright (c) 2021 Temporal Technologies, Inc. All Rights Reserved
@@ -3,7 +3,7 @@ name = "temporal-sdk"
3
3
  version = "0.1.0-alpha.1"
4
4
  edition = "2021"
5
5
  authors = ["Spencer Judge <spencer@temporal.io>"]
6
- license-file = "LICENSE.txt"
6
+ license-file = { workspace = true }
7
7
  description = "Temporal Rust SDK"
8
8
  homepage = "https://temporal.io/"
9
9
  repository = "https://github.com/temporalio/sdk-core"
@@ -236,7 +236,7 @@ impl WorkflowFuture {
236
236
  let defp = Payload::default();
237
237
  let val_res = if u.run_validator {
238
238
  match panic::catch_unwind(AssertUnwindSafe(|| {
239
- (impls.validator)(&info, u.input.get(0).unwrap_or(&defp))
239
+ (impls.validator)(&info, u.input.first().unwrap_or(&defp))
240
240
  })) {
241
241
  Ok(r) => r,
242
242
  Err(e) => {
@@ -257,7 +257,7 @@ impl WorkflowFuture {
257
257
  wf_ctx: self.wf_ctx.clone(),
258
258
  info,
259
259
  },
260
- u.input.get(0).unwrap_or(&defp),
260
+ u.input.first().unwrap_or(&defp),
261
261
  );
262
262
  self.update_futures
263
263
  .push((u.protocol_instance_id, handler_fut));
@@ -3,7 +3,7 @@ name = "temporal-sdk-core-protos"
3
3
  version = "0.1.0"
4
4
  edition = "2021"
5
5
  authors = ["Spencer Judge <spencer@temporal.io>"]
6
- license-file = "LICENSE.txt"
6
+ license-file = { workspace = true }
7
7
  description = "Protobuf definitions for Temporal SDKs Core/Lang interface"
8
8
  homepage = "https://temporal.io/"
9
9
  repository = "https://github.com/temporalio/sdk-core"
@@ -90,10 +90,15 @@ message BatchOperationDeletion {
90
90
  // BatchOperationReset sends reset requests to batch workflows.
91
91
  // Keep the parameter in sync with temporal.api.workflowservice.v1.ResetWorkflowExecutionRequest.
92
92
  message BatchOperationReset {
93
- // Reset type.
94
- temporal.api.enums.v1.ResetType reset_type = 1;
95
- // History event reapply options.
96
- temporal.api.enums.v1.ResetReapplyType reset_reapply_type = 2;
97
93
  // The identity of the worker/client.
98
94
  string identity = 3;
95
+
96
+ // Describes what to reset to and how. If set, `reset_type` and `reset_reapply_type` are ignored.
97
+ temporal.api.common.v1.ResetOptions options = 4;
98
+
99
+ // Reset type (deprecated, use `options`).
100
+ temporal.api.enums.v1.ResetType reset_type = 1;
101
+ // History event reapply options (deprecated, use `options`).
102
+ temporal.api.enums.v1.ResetReapplyType reset_reapply_type = 2;
103
+
99
104
  }
@@ -32,8 +32,10 @@ option ruby_package = "Temporalio::Api::Common::V1";
32
32
  option csharp_namespace = "Temporalio.Api.Common.V1";
33
33
 
34
34
  import "google/protobuf/duration.proto";
35
+ import "google/protobuf/empty.proto";
35
36
 
36
37
  import "temporal/api/enums/v1/common.proto";
38
+ import "temporal/api/enums/v1/reset.proto";
37
39
 
38
40
  message DataBlob {
39
41
  temporal.api.enums.v1.EncodingType encoding_type = 1;
@@ -147,3 +149,32 @@ message WorkerVersionCapabilities {
147
149
 
148
150
  // Later, may include info like "I can process WASM and/or JS bundles"
149
151
  }
152
+
153
+ // Describes where and how to reset a workflow, used for batch reset currently
154
+ // and may be used for single-workflow reset later.
155
+ message ResetOptions {
156
+ // Which workflow task to reset to.
157
+ oneof target {
158
+ // Resets to the first workflow task completed or started event.
159
+ google.protobuf.Empty first_workflow_task = 1;
160
+ // Resets to the last workflow task completed or started event.
161
+ google.protobuf.Empty last_workflow_task = 2;
162
+ // The id of a specific `WORKFLOW_TASK_COMPLETED`,`WORKFLOW_TASK_TIMED_OUT`, `WORKFLOW_TASK_FAILED`, or
163
+ // `WORKFLOW_TASK_STARTED` event to reset to.
164
+ // Note that this option doesn't make sense when used as part of a batch request.
165
+ int64 workflow_task_id = 3;
166
+ // Resets to the first workflow task processed by this build id.
167
+ // If the workflow was not processed by the build id, or the workflow task can't be
168
+ // determined, no reset will be performed.
169
+ // Note that by default, this reset is allowed to be to a prior run in a chain of
170
+ // continue-as-new.
171
+ string build_id = 4;
172
+ }
173
+
174
+ // History event reapply options.
175
+ temporal.api.enums.v1.ResetReapplyType reset_reapply_type = 10;
176
+
177
+ // If true, limit the reset to only within the current run. (Applies to build_id targets and
178
+ // possibly others in the future.)
179
+ bool current_run_only = 11;
180
+ }
@@ -40,11 +40,11 @@ enum ResetReapplyType {
40
40
  RESET_REAPPLY_TYPE_NONE = 2;
41
41
  }
42
42
 
43
- // Reset type options
44
- enum ResetType {
43
+ // Reset type options. Deprecated, see temporal.api.common.v1.ResetOptions.
44
+ enum ResetType {
45
45
  RESET_TYPE_UNSPECIFIED = 0;
46
46
  // Resets to event of the first workflow task completed, or if it does not exist, the event after task scheduled.
47
47
  RESET_TYPE_FIRST_WORKFLOW_TASK = 1;
48
48
  // Resets to event of the last workflow task completed, or if it does not exist, the event after task scheduled.
49
49
  RESET_TYPE_LAST_WORKFLOW_TASK = 2;
50
- }
50
+ }
@@ -24,7 +24,7 @@ syntax = "proto3";
24
24
 
25
25
  package temporal.api.export.v1;
26
26
 
27
- option go_package = "go.temporal.io/api/export/v1;workflow";
27
+ option go_package = "go.temporal.io/api/export/v1;export";
28
28
  option java_package = "io.temporal.api.export.v1";
29
29
  option java_multiple_files = true;
30
30
  option java_outer_classname = "MessageProto";
@@ -0,0 +1,66 @@
1
+ // The MIT License
2
+ //
3
+ // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved.
4
+ //
5
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ // of this software and associated documentation files (the "Software"), to deal
7
+ // in the Software without restriction, including without limitation the rights
8
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ // copies of the Software, and to permit persons to whom the Software is
10
+ // furnished to do so, subject to the following conditions:
11
+ //
12
+ // The above copyright notice and this permission notice shall be included in
13
+ // all copies or substantial portions of the Software.
14
+ //
15
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ // THE SOFTWARE.
22
+
23
+ syntax = "proto3";
24
+
25
+ package temporal.api.sdk.v1;
26
+
27
+ option go_package = "go.temporal.io/api/sdk/v1;sdk";
28
+ option java_package = "io.temporal.api.sdk.v1";
29
+ option java_multiple_files = true;
30
+ option java_outer_classname = "WorkflowMetadataProto";
31
+ option ruby_package = "Temporalio::Api::Sdk::V1";
32
+ option csharp_namespace = "Temporalio.Api.Sdk.V1";
33
+
34
+ // The name of the query to retrieve this information is `__temporal_getWorkflowMetadata`.
35
+ message WorkflowMetadata {
36
+ // Metadata provided at declaration or creation time.
37
+ WorkflowDefinition definition = 1;
38
+ }
39
+
40
+ // (-- api-linter: core::0203::optional=disabled --)
41
+ message WorkflowDefinition {
42
+ // A name scoped by the task queue that maps to this workflow definition.
43
+ // If missing, this workflow is a dynamic workflow.
44
+ string type = 1;
45
+ // An optional workflow description provided by the application.
46
+ // By convention, external tools may interpret its first part,
47
+ // i.e., ending with a line break, as a summary of the description.
48
+ string description = 2;
49
+ repeated WorkflowInteractionDefinition query_definitions = 3;
50
+ repeated WorkflowInteractionDefinition signal_definitions = 4;
51
+ repeated WorkflowInteractionDefinition update_definitions = 5;
52
+ }
53
+
54
+ // (-- api-linter: core::0123::resource-annotation=disabled
55
+ // aip.dev/not-precedent: The `name` field is optional. --)
56
+ // (-- api-linter: core::0203::optional=disabled --)
57
+ message WorkflowInteractionDefinition {
58
+ // An optional name for the handler. If missing, it represents
59
+ // a dynamic handler that processes any interactions not handled by others.
60
+ // There is at most one dynamic handler per workflow and interaction kind.
61
+ string name = 1;
62
+ // An optional interaction description provided by the application.
63
+ // By convention, external tools may interpret its first part,
64
+ // i.e., ending with a line break, as a summary of the description.
65
+ string description = 2;
66
+ }
@@ -106,9 +106,13 @@ message ResetPoints {
106
106
  repeated ResetPointInfo points = 1;
107
107
  }
108
108
 
109
+ // ResetPointInfo records the workflow event id that is the first one processed by a given
110
+ // build id or binary checksum. A new reset point will be created if either build id or binary
111
+ // checksum changes (although in general only one or the other will be used at a time).
109
112
  message ResetPointInfo {
110
- // A worker binary version identifier, will be deprecated and superseded by a newer concept of
111
- // build_id.
113
+ // Worker build id.
114
+ string build_id = 7;
115
+ // A worker binary version identifier (deprecated).
112
116
  string binary_checksum = 1;
113
117
  // The first run ID in the execution chain that was touched by this worker build.
114
118
  string run_id = 2;
@@ -37,7 +37,7 @@ impl HistoryInfo {
37
37
  let mut workflow_task_started_event_id = 0;
38
38
  let mut wf_task_count = 0;
39
39
  let mut history = events.iter().peekable();
40
- let started_attrs = match &events.get(0).unwrap().attributes {
40
+ let started_attrs = match &events.first().unwrap().attributes {
41
41
  Some(history_event::Attributes::WorkflowExecutionStartedEventAttributes(attrs)) => {
42
42
  attrs.clone()
43
43
  }
@@ -336,7 +336,7 @@ pub mod coresdk {
336
336
  ) -> Option<LocalActivityMarkerData> {
337
337
  details
338
338
  .get("data")
339
- .and_then(|p| p.payloads.get(0))
339
+ .and_then(|p| p.payloads.first())
340
340
  .and_then(|p| std::str::from_utf8(&p.data).ok())
341
341
  .and_then(|s| serde_json::from_str(s).ok())
342
342
  }
@@ -3,6 +3,7 @@ name = "temporal-sdk-core-test-utils"
3
3
  version = "0.1.0"
4
4
  authors = ["Spencer Judge <spencer@temporal.io>"]
5
5
  edition = "2021"
6
+ license-file = { workspace = true }
6
7
 
7
8
  [[bin]]
8
9
  name = "histfetch"
@@ -35,8 +35,8 @@ async fn client_list_open_closed_workflow_executions() {
35
35
 
36
36
  // List above OPEN workflow
37
37
  let start_time_filter = StartTimeFilter {
38
- earliest_time: Some(earliest).and_then(|t| t.try_into().ok()),
39
- latest_time: Some(latest).and_then(|t| t.try_into().ok()),
38
+ earliest_time: Some(earliest).map(|t| t.into()),
39
+ latest_time: Some(latest).map(|t| t.into()),
40
40
  };
41
41
  let filter = ListOpenFilters::ExecutionFilter(WorkflowExecutionFilter {
42
42
  workflow_id: wf_name.clone(),
@@ -63,8 +63,8 @@ async fn client_list_open_closed_workflow_executions() {
63
63
  1,
64
64
  Default::default(),
65
65
  Some(StartTimeFilter {
66
- earliest_time: Some(earliest).and_then(|t| t.try_into().ok()),
67
- latest_time: Some(latest).and_then(|t| t.try_into().ok()),
66
+ earliest_time: Some(earliest).map(|t| t.into()),
67
+ latest_time: Some(latest).map(|t| t.into()),
68
68
  }),
69
69
  Some(ListClosedFilters::ExecutionFilter(
70
70
  WorkflowExecutionFilter {
@@ -35,17 +35,19 @@ use temporal_sdk_core_protos::{
35
35
  coresdk::{
36
36
  activity_result::ActivityExecutionResult,
37
37
  workflow_activation::{workflow_activation_job, WorkflowActivation, WorkflowActivationJob},
38
- workflow_commands::{ActivityCancellationType, FailWorkflowExecution, StartTimer},
38
+ workflow_commands::{
39
+ ActivityCancellationType, FailWorkflowExecution, QueryResult, QuerySuccess, StartTimer,
40
+ },
39
41
  workflow_completion::WorkflowActivationCompletion,
40
42
  ActivityTaskCompletion, AsJsonPayloadExt, IntoCompletion,
41
43
  },
42
- temporal::api::{failure::v1::Failure, history::v1::history_event},
44
+ temporal::api::{failure::v1::Failure, history::v1::history_event, query::v1::WorkflowQuery},
43
45
  };
44
46
  use temporal_sdk_core_test_utils::{
45
47
  drain_pollers_and_shutdown, history_from_proto_binary, init_core_and_create_wf,
46
48
  init_core_replay_preloaded, schedule_activity_cmd, CoreWfStarter, WorkerTestHelpers,
47
49
  };
48
- use tokio::time::sleep;
50
+ use tokio::{join, time::sleep};
49
51
  use uuid::Uuid;
50
52
 
51
53
  // TODO: We should get expected histories for these tests and confirm that the history at the end
@@ -592,3 +594,139 @@ async fn slow_completes_with_small_cache() {
592
594
  .await
593
595
  .unwrap();
594
596
  }
597
+
598
+ #[tokio::test]
599
+ async fn build_id_correct_in_wf_info() {
600
+ let wf_type = "build_id_correct_in_wf_info";
601
+ let mut starter = CoreWfStarter::new(wf_type);
602
+ starter
603
+ .worker_config
604
+ .worker_build_id("1.0")
605
+ .no_remote_activities(true);
606
+ let core = starter.get_worker().await;
607
+ starter.start_wf().await;
608
+ let client = starter.get_client().await;
609
+ let workflow_id = starter.get_task_queue().to_string();
610
+
611
+ let res = core.poll_workflow_activation().await.unwrap();
612
+ assert_eq!(res.build_id_for_current_task, "1.0");
613
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
614
+ res.run_id.clone(),
615
+ vec![],
616
+ ))
617
+ .await
618
+ .unwrap();
619
+
620
+ // Ensure a query on first wft also sees the correct id
621
+ let query_fut = async {
622
+ client
623
+ .query_workflow_execution(
624
+ workflow_id.clone(),
625
+ res.run_id.to_string(),
626
+ WorkflowQuery {
627
+ query_type: "q1".to_string(),
628
+ ..Default::default()
629
+ },
630
+ )
631
+ .await
632
+ .unwrap()
633
+ };
634
+ let complete_fut = async {
635
+ let task = core.poll_workflow_activation().await.unwrap();
636
+ let query = assert_matches!(
637
+ task.jobs.as_slice(),
638
+ [WorkflowActivationJob {
639
+ variant: Some(workflow_activation_job::Variant::QueryWorkflow(q)),
640
+ }] => q
641
+ );
642
+ assert_eq!(task.build_id_for_current_task, "1.0");
643
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
644
+ task.run_id,
645
+ QueryResult {
646
+ query_id: query.query_id.clone(),
647
+ variant: Some(
648
+ QuerySuccess {
649
+ response: Some("done".into()),
650
+ }
651
+ .into(),
652
+ ),
653
+ }
654
+ .into(),
655
+ ))
656
+ .await
657
+ .unwrap();
658
+ };
659
+ join!(query_fut, complete_fut);
660
+ starter.shutdown().await;
661
+ client
662
+ .reset_sticky_task_queue(workflow_id.clone(), "".to_string())
663
+ .await
664
+ .unwrap();
665
+
666
+ let mut starter = starter.clone_no_worker();
667
+ starter.worker_config.worker_build_id("2.0");
668
+ let core = starter.get_worker().await;
669
+
670
+ let query_fut = async {
671
+ client
672
+ .query_workflow_execution(
673
+ workflow_id.clone(),
674
+ res.run_id.to_string(),
675
+ WorkflowQuery {
676
+ query_type: "q2".to_string(),
677
+ ..Default::default()
678
+ },
679
+ )
680
+ .await
681
+ .unwrap()
682
+ };
683
+ let complete_fut = async {
684
+ let res = core.poll_workflow_activation().await.unwrap();
685
+ assert_eq!(res.build_id_for_current_task, "1.0");
686
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
687
+ res.run_id.clone(),
688
+ vec![],
689
+ ))
690
+ .await
691
+ .unwrap();
692
+ let task = core.poll_workflow_activation().await.unwrap();
693
+ let query = assert_matches!(
694
+ task.jobs.as_slice(),
695
+ [WorkflowActivationJob {
696
+ variant: Some(workflow_activation_job::Variant::QueryWorkflow(q)),
697
+ }] => q
698
+ );
699
+ assert_eq!(task.build_id_for_current_task, "1.0");
700
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
701
+ task.run_id,
702
+ QueryResult {
703
+ query_id: query.query_id.clone(),
704
+ variant: Some(
705
+ QuerySuccess {
706
+ response: Some("done".into()),
707
+ }
708
+ .into(),
709
+ ),
710
+ }
711
+ .into(),
712
+ ))
713
+ .await
714
+ .unwrap();
715
+ };
716
+ join!(query_fut, complete_fut);
717
+
718
+ client
719
+ .signal_workflow_execution(
720
+ workflow_id.clone(),
721
+ "".to_string(),
722
+ "whatever".to_string(),
723
+ None,
724
+ None,
725
+ )
726
+ .await
727
+ .unwrap();
728
+
729
+ let res = core.poll_workflow_activation().await.unwrap();
730
+ assert_eq!(res.build_id_for_current_task, "2.0");
731
+ core.complete_execution(&res.run_id).await;
732
+ }