temporalio 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +25 -23
- data/bridge/Cargo.lock +168 -59
- data/bridge/Cargo.toml +4 -2
- data/bridge/sdk-core/README.md +19 -6
- data/bridge/sdk-core/client/src/lib.rs +215 -39
- data/bridge/sdk-core/client/src/metrics.rs +17 -8
- data/bridge/sdk-core/client/src/raw.rs +4 -4
- data/bridge/sdk-core/client/src/retry.rs +32 -20
- data/bridge/sdk-core/core/Cargo.toml +22 -9
- data/bridge/sdk-core/core/src/abstractions.rs +203 -14
- data/bridge/sdk-core/core/src/core_tests/activity_tasks.rs +76 -41
- data/bridge/sdk-core/core/src/core_tests/determinism.rs +165 -2
- data/bridge/sdk-core/core/src/core_tests/local_activities.rs +204 -83
- data/bridge/sdk-core/core/src/core_tests/queries.rs +3 -4
- data/bridge/sdk-core/core/src/core_tests/workers.rs +1 -3
- data/bridge/sdk-core/core/src/core_tests/workflow_tasks.rs +397 -54
- data/bridge/sdk-core/core/src/ephemeral_server/mod.rs +106 -12
- data/bridge/sdk-core/core/src/internal_flags.rs +136 -0
- data/bridge/sdk-core/core/src/lib.rs +16 -9
- data/bridge/sdk-core/core/src/telemetry/log_export.rs +1 -1
- data/bridge/sdk-core/core/src/telemetry/metrics.rs +69 -35
- data/bridge/sdk-core/core/src/telemetry/mod.rs +29 -13
- data/bridge/sdk-core/core/src/telemetry/prometheus_server.rs +17 -12
- data/bridge/sdk-core/core/src/test_help/mod.rs +62 -12
- data/bridge/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +112 -156
- data/bridge/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +89 -0
- data/bridge/sdk-core/core/src/worker/activities/local_activities.rs +352 -122
- data/bridge/sdk-core/core/src/worker/activities.rs +233 -157
- data/bridge/sdk-core/core/src/worker/client/mocks.rs +22 -2
- data/bridge/sdk-core/core/src/worker/client.rs +18 -2
- data/bridge/sdk-core/core/src/worker/mod.rs +165 -58
- data/bridge/sdk-core/core/src/worker/workflow/bridge.rs +1 -3
- data/bridge/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -5
- data/bridge/sdk-core/core/src/worker/workflow/history_update.rs +856 -277
- data/bridge/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +100 -43
- data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +7 -7
- data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +5 -4
- data/bridge/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +87 -27
- data/bridge/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +5 -4
- data/bridge/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +5 -4
- data/bridge/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +5 -4
- data/bridge/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +137 -62
- data/bridge/sdk-core/core/src/worker/workflow/machines/mod.rs +25 -17
- data/bridge/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +7 -6
- data/bridge/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +103 -152
- data/bridge/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +7 -7
- data/bridge/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +9 -9
- data/bridge/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +2 -2
- data/bridge/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +14 -7
- data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +5 -16
- data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +201 -121
- data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +11 -14
- data/bridge/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +30 -15
- data/bridge/sdk-core/core/src/worker/workflow/managed_run.rs +1026 -376
- data/bridge/sdk-core/core/src/worker/workflow/mod.rs +460 -384
- data/bridge/sdk-core/core/src/worker/workflow/run_cache.rs +40 -57
- data/bridge/sdk-core/core/src/worker/workflow/wft_extraction.rs +125 -0
- data/bridge/sdk-core/core/src/worker/workflow/wft_poller.rs +1 -4
- data/bridge/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +117 -0
- data/bridge/sdk-core/core/src/worker/workflow/workflow_stream/tonic_status_serde.rs +24 -0
- data/bridge/sdk-core/core/src/worker/workflow/workflow_stream.rs +448 -718
- data/bridge/sdk-core/core-api/Cargo.toml +2 -1
- data/bridge/sdk-core/core-api/src/errors.rs +1 -34
- data/bridge/sdk-core/core-api/src/lib.rs +6 -2
- data/bridge/sdk-core/core-api/src/telemetry.rs +0 -6
- data/bridge/sdk-core/core-api/src/worker.rs +14 -1
- data/bridge/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +18 -15
- data/bridge/sdk-core/fsm/rustfsm_trait/src/lib.rs +8 -3
- data/bridge/sdk-core/histories/evict_while_la_running_no_interference-16_history.bin +0 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +5 -17
- data/bridge/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +11 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +1 -6
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +6 -6
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +5 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +22 -6
- data/bridge/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +48 -19
- data/bridge/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +2 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +3 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/{enums/v1/interaction_type.proto → protocol/v1/message.proto} +29 -11
- data/bridge/sdk-core/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +63 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +111 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +59 -28
- data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +2 -2
- data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +1 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +1 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +1 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/common/common.proto +1 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +1 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +1 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +7 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +1 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +6 -0
- data/bridge/sdk-core/sdk/Cargo.toml +3 -2
- data/bridge/sdk-core/sdk/src/lib.rs +87 -20
- data/bridge/sdk-core/sdk/src/workflow_future.rs +9 -8
- data/bridge/sdk-core/sdk-core-protos/Cargo.toml +5 -2
- data/bridge/sdk-core/sdk-core-protos/build.rs +36 -1
- data/bridge/sdk-core/sdk-core-protos/src/history_builder.rs +100 -87
- data/bridge/sdk-core/sdk-core-protos/src/history_info.rs +5 -1
- data/bridge/sdk-core/sdk-core-protos/src/lib.rs +175 -57
- data/bridge/sdk-core/sdk-core-protos/src/task_token.rs +12 -2
- data/bridge/sdk-core/test-utils/Cargo.toml +3 -1
- data/bridge/sdk-core/test-utils/src/canned_histories.rs +106 -296
- data/bridge/sdk-core/test-utils/src/histfetch.rs +1 -1
- data/bridge/sdk-core/test-utils/src/lib.rs +82 -23
- data/bridge/sdk-core/test-utils/src/wf_input_saver.rs +50 -0
- data/bridge/sdk-core/test-utils/src/workflows.rs +29 -0
- data/bridge/sdk-core/tests/fuzzy_workflow.rs +130 -0
- data/bridge/sdk-core/tests/{load_tests.rs → heavy_tests.rs} +125 -51
- data/bridge/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +25 -3
- data/bridge/sdk-core/tests/integ_tests/heartbeat_tests.rs +5 -3
- data/bridge/sdk-core/tests/integ_tests/metrics_tests.rs +218 -16
- data/bridge/sdk-core/tests/integ_tests/polling_tests.rs +4 -47
- data/bridge/sdk-core/tests/integ_tests/queries_tests.rs +5 -128
- data/bridge/sdk-core/tests/integ_tests/visibility_tests.rs +83 -25
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/activities.rs +93 -69
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +6 -13
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +1 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +6 -2
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -10
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +72 -191
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +1 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/patches.rs +7 -28
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/replay.rs +12 -7
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/signals.rs +18 -14
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +6 -20
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/timers.rs +10 -21
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +6 -4
- data/bridge/sdk-core/tests/integ_tests/workflow_tests.rs +10 -11
- data/bridge/sdk-core/tests/main.rs +3 -13
- data/bridge/sdk-core/tests/runner.rs +75 -36
- data/bridge/sdk-core/tests/wf_input_replay.rs +32 -0
- data/bridge/src/connection.rs +41 -25
- data/bridge/src/lib.rs +269 -14
- data/bridge/src/runtime.rs +1 -1
- data/bridge/src/test_server.rs +153 -0
- data/bridge/src/worker.rs +89 -16
- data/lib/gen/temporal/api/command/v1/message_pb.rb +4 -18
- data/lib/gen/temporal/api/common/v1/message_pb.rb +4 -0
- data/lib/gen/temporal/api/enums/v1/command_type_pb.rb +1 -3
- data/lib/gen/temporal/api/enums/v1/event_type_pb.rb +3 -3
- data/lib/gen/temporal/api/enums/v1/failed_cause_pb.rb +2 -0
- data/lib/gen/temporal/api/enums/v1/update_pb.rb +6 -4
- data/lib/gen/temporal/api/history/v1/message_pb.rb +27 -19
- data/lib/gen/temporal/api/namespace/v1/message_pb.rb +1 -0
- data/lib/gen/temporal/api/operatorservice/v1/request_response_pb.rb +3 -0
- data/lib/gen/temporal/api/protocol/v1/message_pb.rb +30 -0
- data/lib/gen/temporal/api/sdk/v1/task_complete_metadata_pb.rb +23 -0
- data/lib/gen/temporal/api/testservice/v1/request_response_pb.rb +49 -0
- data/lib/gen/temporal/api/testservice/v1/service_pb.rb +21 -0
- data/lib/gen/temporal/api/update/v1/message_pb.rb +72 -0
- data/lib/gen/temporal/api/workflowservice/v1/request_response_pb.rb +26 -16
- data/lib/gen/temporal/sdk/core/activity_result/activity_result_pb.rb +13 -9
- data/lib/gen/temporal/sdk/core/activity_task/activity_task_pb.rb +10 -6
- data/lib/gen/temporal/sdk/core/child_workflow/child_workflow_pb.rb +13 -9
- data/lib/gen/temporal/sdk/core/common/common_pb.rb +7 -3
- data/lib/gen/temporal/sdk/core/core_interface_pb.rb +9 -3
- data/lib/gen/temporal/sdk/core/external_data/external_data_pb.rb +7 -3
- data/lib/gen/temporal/sdk/core/workflow_activation/workflow_activation_pb.rb +27 -21
- data/lib/gen/temporal/sdk/core/workflow_commands/workflow_commands_pb.rb +28 -24
- data/lib/gen/temporal/sdk/core/workflow_completion/workflow_completion_pb.rb +12 -5
- data/lib/temporalio/activity/context.rb +13 -8
- data/lib/temporalio/activity/info.rb +1 -1
- data/lib/temporalio/bridge/connect_options.rb +15 -0
- data/lib/temporalio/bridge/retry_config.rb +24 -0
- data/lib/temporalio/bridge/tls_options.rb +19 -0
- data/lib/temporalio/client/implementation.rb +8 -8
- data/lib/temporalio/connection/retry_config.rb +44 -0
- data/lib/temporalio/connection/service.rb +20 -0
- data/lib/temporalio/connection/test_service.rb +92 -0
- data/lib/temporalio/connection/tls_options.rb +51 -0
- data/lib/temporalio/connection/workflow_service.rb +731 -0
- data/lib/temporalio/connection.rb +55 -720
- data/lib/temporalio/interceptor/activity_inbound.rb +22 -0
- data/lib/temporalio/interceptor/activity_outbound.rb +24 -0
- data/lib/temporalio/interceptor/chain.rb +5 -5
- data/lib/temporalio/interceptor/client.rb +8 -4
- data/lib/temporalio/interceptor.rb +22 -0
- data/lib/temporalio/retry_policy.rb +13 -3
- data/lib/temporalio/testing/time_skipping_handle.rb +32 -0
- data/lib/temporalio/testing/time_skipping_interceptor.rb +23 -0
- data/lib/temporalio/testing/workflow_environment.rb +112 -0
- data/lib/temporalio/testing.rb +175 -0
- data/lib/temporalio/version.rb +1 -1
- data/lib/temporalio/worker/activity_runner.rb +26 -4
- data/lib/temporalio/worker/activity_worker.rb +44 -18
- data/lib/temporalio/worker/sync_worker.rb +47 -11
- data/lib/temporalio/worker.rb +27 -21
- data/lib/temporalio/workflow/async.rb +46 -0
- data/lib/temporalio/workflow/future.rb +138 -0
- data/lib/temporalio/workflow/info.rb +76 -0
- data/temporalio.gemspec +4 -3
- metadata +67 -17
- data/bridge/sdk-core/Cargo.lock +0 -2606
- data/bridge/sdk-core/protos/api_upstream/temporal/api/interaction/v1/message.proto +0 -87
- data/lib/bridge.so +0 -0
- data/lib/gen/temporal/api/enums/v1/interaction_type_pb.rb +0 -25
- data/lib/gen/temporal/api/interaction/v1/message_pb.rb +0 -49
- data/lib/gen/temporal/sdk/core/bridge/bridge_pb.rb +0 -222
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
use crate::{
|
|
2
|
+
internal_flags::CoreInternalFlags,
|
|
2
3
|
replay::DEFAULT_WORKFLOW_TYPE,
|
|
3
4
|
test_help::{canned_histories, mock_sdk, mock_sdk_cfg, MockPollCfg, ResponseType},
|
|
4
5
|
worker::client::mocks::mock_workflow_client,
|
|
@@ -8,8 +9,13 @@ use std::{
|
|
|
8
9
|
time::Duration,
|
|
9
10
|
};
|
|
10
11
|
use temporal_client::WorkflowOptions;
|
|
11
|
-
use temporal_sdk::{
|
|
12
|
-
|
|
12
|
+
use temporal_sdk::{
|
|
13
|
+
ActivityOptions, ChildWorkflowOptions, LocalActivityOptions, WfContext, WorkflowResult,
|
|
14
|
+
};
|
|
15
|
+
use temporal_sdk_core_protos::{
|
|
16
|
+
temporal::api::{enums::v1::WorkflowTaskFailedCause, failure::v1::Failure},
|
|
17
|
+
DEFAULT_ACTIVITY_TYPE,
|
|
18
|
+
};
|
|
13
19
|
|
|
14
20
|
static DID_FAIL: AtomicBool = AtomicBool::new(false);
|
|
15
21
|
pub async fn timer_wf_fails_once(ctx: WfContext) -> WorkflowResult<()> {
|
|
@@ -105,3 +111,160 @@ async fn test_wf_task_rejected_properly_due_to_nondeterminism(#[case] use_cache:
|
|
|
105
111
|
// timer and proceed without restarting
|
|
106
112
|
assert_eq!(2, started_count.load(Ordering::Relaxed));
|
|
107
113
|
}
|
|
114
|
+
|
|
115
|
+
#[rstest::rstest]
|
|
116
|
+
#[tokio::test]
|
|
117
|
+
async fn activity_id_or_type_change_is_nondeterministic(
|
|
118
|
+
#[values(true, false)] use_cache: bool,
|
|
119
|
+
#[values(true, false)] id_change: bool,
|
|
120
|
+
#[values(true, false)] local_act: bool,
|
|
121
|
+
) {
|
|
122
|
+
let wf_id = "fakeid";
|
|
123
|
+
let wf_type = DEFAULT_WORKFLOW_TYPE;
|
|
124
|
+
let mut t = if local_act {
|
|
125
|
+
canned_histories::single_local_activity("1")
|
|
126
|
+
} else {
|
|
127
|
+
canned_histories::single_activity("1")
|
|
128
|
+
};
|
|
129
|
+
t.set_flags_first_wft(&[CoreInternalFlags::IdAndTypeDeterminismChecks as u32], &[]);
|
|
130
|
+
let mock = mock_workflow_client();
|
|
131
|
+
let mut mh = MockPollCfg::from_resp_batches(
|
|
132
|
+
wf_id,
|
|
133
|
+
t,
|
|
134
|
+
// Two polls are needed, since the first will fail
|
|
135
|
+
[ResponseType::AllHistory, ResponseType::AllHistory],
|
|
136
|
+
mock,
|
|
137
|
+
);
|
|
138
|
+
// We should see one wft failure which has nondeterminism cause
|
|
139
|
+
mh.num_expected_fails = 1;
|
|
140
|
+
mh.expect_fail_wft_matcher = Box::new(move |_, cause, f| {
|
|
141
|
+
let should_contain = if id_change {
|
|
142
|
+
"does not match activity id"
|
|
143
|
+
} else {
|
|
144
|
+
"does not match activity type"
|
|
145
|
+
};
|
|
146
|
+
matches!(cause, WorkflowTaskFailedCause::NonDeterministicError)
|
|
147
|
+
&& matches!(f, Some(Failure {
|
|
148
|
+
message,
|
|
149
|
+
..
|
|
150
|
+
}) if message.contains(should_contain))
|
|
151
|
+
});
|
|
152
|
+
let mut worker = mock_sdk_cfg(mh, |cfg| {
|
|
153
|
+
if use_cache {
|
|
154
|
+
cfg.max_cached_workflows = 2;
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
worker.register_wf(wf_type.to_owned(), move |ctx: WfContext| async move {
|
|
159
|
+
if local_act {
|
|
160
|
+
ctx.local_activity(if id_change {
|
|
161
|
+
LocalActivityOptions {
|
|
162
|
+
activity_id: Some("I'm bad and wrong!".to_string()),
|
|
163
|
+
activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
|
|
164
|
+
..Default::default()
|
|
165
|
+
}
|
|
166
|
+
} else {
|
|
167
|
+
LocalActivityOptions {
|
|
168
|
+
activity_type: "not the default act type".to_string(),
|
|
169
|
+
..Default::default()
|
|
170
|
+
}
|
|
171
|
+
})
|
|
172
|
+
.await;
|
|
173
|
+
} else {
|
|
174
|
+
ctx.activity(if id_change {
|
|
175
|
+
ActivityOptions {
|
|
176
|
+
activity_id: Some("I'm bad and wrong!".to_string()),
|
|
177
|
+
activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
|
|
178
|
+
..Default::default()
|
|
179
|
+
}
|
|
180
|
+
} else {
|
|
181
|
+
ActivityOptions {
|
|
182
|
+
activity_type: "not the default act type".to_string(),
|
|
183
|
+
..Default::default()
|
|
184
|
+
}
|
|
185
|
+
})
|
|
186
|
+
.await;
|
|
187
|
+
}
|
|
188
|
+
Ok(().into())
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
worker
|
|
192
|
+
.submit_wf(
|
|
193
|
+
wf_id.to_owned(),
|
|
194
|
+
wf_type.to_owned(),
|
|
195
|
+
vec![],
|
|
196
|
+
WorkflowOptions::default(),
|
|
197
|
+
)
|
|
198
|
+
.await
|
|
199
|
+
.unwrap();
|
|
200
|
+
worker.run_until_done().await.unwrap();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
#[rstest::rstest]
|
|
204
|
+
#[tokio::test]
|
|
205
|
+
async fn child_wf_id_or_type_change_is_nondeterministic(
|
|
206
|
+
#[values(true, false)] use_cache: bool,
|
|
207
|
+
#[values(true, false)] id_change: bool,
|
|
208
|
+
) {
|
|
209
|
+
let wf_id = "fakeid";
|
|
210
|
+
let wf_type = DEFAULT_WORKFLOW_TYPE;
|
|
211
|
+
let mut t = canned_histories::single_child_workflow("1");
|
|
212
|
+
t.set_flags_first_wft(&[CoreInternalFlags::IdAndTypeDeterminismChecks as u32], &[]);
|
|
213
|
+
let mock = mock_workflow_client();
|
|
214
|
+
let mut mh = MockPollCfg::from_resp_batches(
|
|
215
|
+
wf_id,
|
|
216
|
+
t,
|
|
217
|
+
// Two polls are needed, since the first will fail
|
|
218
|
+
[ResponseType::AllHistory, ResponseType::AllHistory],
|
|
219
|
+
mock,
|
|
220
|
+
);
|
|
221
|
+
// We should see one wft failure which has nondeterminism cause
|
|
222
|
+
mh.num_expected_fails = 1;
|
|
223
|
+
mh.expect_fail_wft_matcher = Box::new(move |_, cause, f| {
|
|
224
|
+
let should_contain = if id_change {
|
|
225
|
+
"does not match child workflow id"
|
|
226
|
+
} else {
|
|
227
|
+
"does not match child workflow type"
|
|
228
|
+
};
|
|
229
|
+
matches!(cause, WorkflowTaskFailedCause::NonDeterministicError)
|
|
230
|
+
&& matches!(f, Some(Failure {
|
|
231
|
+
message,
|
|
232
|
+
..
|
|
233
|
+
}) if message.contains(should_contain))
|
|
234
|
+
});
|
|
235
|
+
let mut worker = mock_sdk_cfg(mh, |cfg| {
|
|
236
|
+
if use_cache {
|
|
237
|
+
cfg.max_cached_workflows = 2;
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
worker.register_wf(wf_type.to_owned(), move |ctx: WfContext| async move {
|
|
242
|
+
ctx.child_workflow(if id_change {
|
|
243
|
+
ChildWorkflowOptions {
|
|
244
|
+
workflow_id: "I'm bad and wrong!".to_string(),
|
|
245
|
+
workflow_type: DEFAULT_ACTIVITY_TYPE.to_string(),
|
|
246
|
+
..Default::default()
|
|
247
|
+
}
|
|
248
|
+
} else {
|
|
249
|
+
ChildWorkflowOptions {
|
|
250
|
+
workflow_id: "1".to_string(),
|
|
251
|
+
workflow_type: "not the child wf type".to_string(),
|
|
252
|
+
..Default::default()
|
|
253
|
+
}
|
|
254
|
+
})
|
|
255
|
+
.start(&ctx)
|
|
256
|
+
.await;
|
|
257
|
+
Ok(().into())
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
worker
|
|
261
|
+
.submit_wf(
|
|
262
|
+
wf_id.to_owned(),
|
|
263
|
+
wf_type.to_owned(),
|
|
264
|
+
vec![],
|
|
265
|
+
WorkflowOptions::default(),
|
|
266
|
+
)
|
|
267
|
+
.await
|
|
268
|
+
.unwrap();
|
|
269
|
+
worker.run_until_done().await.unwrap();
|
|
270
|
+
}
|
|
@@ -8,6 +8,7 @@ use crate::{
|
|
|
8
8
|
worker::{client::mocks::mock_workflow_client, LEGACY_QUERY_ID},
|
|
9
9
|
};
|
|
10
10
|
use anyhow::anyhow;
|
|
11
|
+
use crossbeam::queue::SegQueue;
|
|
11
12
|
use futures::{future::join_all, FutureExt};
|
|
12
13
|
use std::{
|
|
13
14
|
collections::HashMap,
|
|
@@ -19,7 +20,9 @@ use std::{
|
|
|
19
20
|
time::{Duration, SystemTime},
|
|
20
21
|
};
|
|
21
22
|
use temporal_client::WorkflowOptions;
|
|
22
|
-
use temporal_sdk::{
|
|
23
|
+
use temporal_sdk::{
|
|
24
|
+
ActContext, ActivityCancelledError, LocalActivityOptions, WfContext, WorkflowResult,
|
|
25
|
+
};
|
|
23
26
|
use temporal_sdk_core_api::Worker;
|
|
24
27
|
use temporal_sdk_core_protos::{
|
|
25
28
|
coresdk::{
|
|
@@ -31,11 +34,11 @@ use temporal_sdk_core_protos::{
|
|
|
31
34
|
},
|
|
32
35
|
temporal::api::{
|
|
33
36
|
common::v1::RetryPolicy,
|
|
34
|
-
enums::v1::{EventType, TimeoutType},
|
|
37
|
+
enums::v1::{EventType, TimeoutType, WorkflowTaskFailedCause},
|
|
35
38
|
failure::v1::Failure,
|
|
36
|
-
history::v1::History,
|
|
37
39
|
query::v1::WorkflowQuery,
|
|
38
40
|
},
|
|
41
|
+
DEFAULT_ACTIVITY_TYPE,
|
|
39
42
|
};
|
|
40
43
|
use temporal_sdk_core_test_utils::{
|
|
41
44
|
schedule_local_activity_cmd, start_timer_cmd, WorkerTestHelpers,
|
|
@@ -58,7 +61,7 @@ async fn local_act_two_wfts_before_marker(#[case] replay: bool, #[case] cached:
|
|
|
58
61
|
let mut t = TestHistoryBuilder::default();
|
|
59
62
|
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
60
63
|
t.add_full_wf_task();
|
|
61
|
-
let timer_started_event_id = t.
|
|
64
|
+
let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
|
|
62
65
|
t.add_full_wf_task();
|
|
63
66
|
t.add_local_activity_result_marker(1, "1", b"echo".into());
|
|
64
67
|
t.add_timer_fired(timer_started_event_id, "1".to_string());
|
|
@@ -83,7 +86,7 @@ async fn local_act_two_wfts_before_marker(#[case] replay: bool, #[case] cached:
|
|
|
83
86
|
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
84
87
|
|ctx: WfContext| async move {
|
|
85
88
|
let la = ctx.local_activity(LocalActivityOptions {
|
|
86
|
-
activity_type:
|
|
89
|
+
activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
|
|
87
90
|
input: "hi".as_json_payload().expect("serializes fine"),
|
|
88
91
|
..Default::default()
|
|
89
92
|
});
|
|
@@ -92,7 +95,7 @@ async fn local_act_two_wfts_before_marker(#[case] replay: bool, #[case] cached:
|
|
|
92
95
|
Ok(().into())
|
|
93
96
|
},
|
|
94
97
|
);
|
|
95
|
-
worker.register_activity(
|
|
98
|
+
worker.register_activity(DEFAULT_ACTIVITY_TYPE, echo);
|
|
96
99
|
worker
|
|
97
100
|
.submit_wf(
|
|
98
101
|
wf_id.to_owned(),
|
|
@@ -110,7 +113,7 @@ pub async fn local_act_fanout_wf(ctx: WfContext) -> WorkflowResult<()> {
|
|
|
110
113
|
.map(|i| {
|
|
111
114
|
ctx.local_activity(LocalActivityOptions {
|
|
112
115
|
activity_type: "echo".to_string(),
|
|
113
|
-
input: format!("Hi {}"
|
|
116
|
+
input: format!("Hi {i}")
|
|
114
117
|
.as_json_payload()
|
|
115
118
|
.expect("serializes fine"),
|
|
116
119
|
..Default::default()
|
|
@@ -127,7 +130,7 @@ async fn local_act_many_concurrent() {
|
|
|
127
130
|
let mut t = TestHistoryBuilder::default();
|
|
128
131
|
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
129
132
|
t.add_full_wf_task();
|
|
130
|
-
let timer_started_event_id = t.
|
|
133
|
+
let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
|
|
131
134
|
t.add_full_wf_task();
|
|
132
135
|
for i in 1..=50 {
|
|
133
136
|
t.add_local_activity_result_marker(i, &i.to_string(), b"echo".into());
|
|
@@ -168,12 +171,7 @@ async fn local_act_many_concurrent() {
|
|
|
168
171
|
async fn local_act_heartbeat(#[case] shutdown_middle: bool) {
|
|
169
172
|
let mut t = TestHistoryBuilder::default();
|
|
170
173
|
let wft_timeout = Duration::from_millis(200);
|
|
171
|
-
|
|
172
|
-
wes_short_wft_timeout.workflow_task_timeout = Some(wft_timeout.try_into().unwrap());
|
|
173
|
-
t.add(
|
|
174
|
-
EventType::WorkflowExecutionStarted,
|
|
175
|
-
wes_short_wft_timeout.into(),
|
|
176
|
-
);
|
|
174
|
+
t.add_wfe_started_with_wft_timeout(wft_timeout);
|
|
177
175
|
t.add_full_wf_task();
|
|
178
176
|
// Task created by WFT heartbeat
|
|
179
177
|
t.add_full_wf_task();
|
|
@@ -304,7 +302,7 @@ async fn local_act_retry_long_backoff_uses_timer() {
|
|
|
304
302
|
"1",
|
|
305
303
|
Failure::application_failure("la failed".to_string(), false),
|
|
306
304
|
);
|
|
307
|
-
let timer_started_event_id = t.
|
|
305
|
+
let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
|
|
308
306
|
t.add_timer_fired(timer_started_event_id, "1".to_string());
|
|
309
307
|
t.add_full_wf_task();
|
|
310
308
|
t.add_local_activity_fail_marker(
|
|
@@ -312,7 +310,7 @@ async fn local_act_retry_long_backoff_uses_timer() {
|
|
|
312
310
|
"2",
|
|
313
311
|
Failure::application_failure("la failed".to_string(), false),
|
|
314
312
|
);
|
|
315
|
-
let timer_started_event_id = t.
|
|
313
|
+
let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
|
|
316
314
|
t.add_timer_fired(timer_started_event_id, "2".to_string());
|
|
317
315
|
t.add_full_wf_task();
|
|
318
316
|
t.add_workflow_execution_completed();
|
|
@@ -332,7 +330,7 @@ async fn local_act_retry_long_backoff_uses_timer() {
|
|
|
332
330
|
|ctx: WfContext| async move {
|
|
333
331
|
let la_res = ctx
|
|
334
332
|
.local_activity(LocalActivityOptions {
|
|
335
|
-
activity_type:
|
|
333
|
+
activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
|
|
336
334
|
input: "hi".as_json_payload().expect("serializes fine"),
|
|
337
335
|
retry_policy: RetryPolicy {
|
|
338
336
|
initial_interval: Some(prost_dur!(from_millis(65))),
|
|
@@ -351,9 +349,12 @@ async fn local_act_retry_long_backoff_uses_timer() {
|
|
|
351
349
|
Ok(().into())
|
|
352
350
|
},
|
|
353
351
|
);
|
|
354
|
-
worker.register_activity(
|
|
355
|
-
|
|
356
|
-
|
|
352
|
+
worker.register_activity(
|
|
353
|
+
DEFAULT_ACTIVITY_TYPE,
|
|
354
|
+
move |_ctx: ActContext, _: String| async move {
|
|
355
|
+
Result::<(), _>::Err(anyhow!("Oh no I failed!"))
|
|
356
|
+
},
|
|
357
|
+
);
|
|
357
358
|
worker
|
|
358
359
|
.submit_wf(
|
|
359
360
|
wf_id.to_owned(),
|
|
@@ -413,7 +414,7 @@ async fn local_act_command_immediately_follows_la_marker() {
|
|
|
413
414
|
t.add_full_wf_task();
|
|
414
415
|
t.add_full_wf_task();
|
|
415
416
|
t.add_local_activity_result_marker(1, "1", "done".into());
|
|
416
|
-
t.
|
|
417
|
+
t.add_by_type(EventType::TimerStarted);
|
|
417
418
|
t.add_full_wf_task();
|
|
418
419
|
|
|
419
420
|
let wf_id = "fakeid";
|
|
@@ -452,12 +453,7 @@ async fn local_act_command_immediately_follows_la_marker() {
|
|
|
452
453
|
async fn query_during_wft_heartbeat_doesnt_accidentally_fail_to_continue_heartbeat() {
|
|
453
454
|
let wfid = "fake_wf_id";
|
|
454
455
|
let mut t = TestHistoryBuilder::default();
|
|
455
|
-
|
|
456
|
-
wes_short_wft_timeout.workflow_task_timeout = Some(prost_dur!(from_millis(200)));
|
|
457
|
-
t.add(
|
|
458
|
-
EventType::WorkflowExecutionStarted,
|
|
459
|
-
wes_short_wft_timeout.into(),
|
|
460
|
-
);
|
|
456
|
+
t.add_wfe_started_with_wft_timeout(Duration::from_millis(200));
|
|
461
457
|
t.add_full_wf_task();
|
|
462
458
|
// get query here
|
|
463
459
|
t.add_full_wf_task();
|
|
@@ -506,7 +502,7 @@ async fn query_during_wft_heartbeat_doesnt_accidentally_fail_to_continue_heartbe
|
|
|
506
502
|
task.run_id,
|
|
507
503
|
schedule_local_activity_cmd(
|
|
508
504
|
1,
|
|
509
|
-
"
|
|
505
|
+
"1",
|
|
510
506
|
ActivityCancellationType::TryCancel,
|
|
511
507
|
Duration::from_secs(60),
|
|
512
508
|
),
|
|
@@ -572,15 +568,11 @@ async fn la_resolve_during_legacy_query_does_not_combine(#[case] impossible_quer
|
|
|
572
568
|
// never happen, but there was an issue where an LA resolving could trigger that.
|
|
573
569
|
let wfid = "fake_wf_id";
|
|
574
570
|
let mut t = TestHistoryBuilder::default();
|
|
575
|
-
|
|
576
|
-
t.add(
|
|
577
|
-
EventType::WorkflowExecutionStarted,
|
|
578
|
-
wes_short_wft_timeout.into(),
|
|
579
|
-
);
|
|
571
|
+
t.add(default_wes_attribs());
|
|
580
572
|
// Since we don't send queries with start workflow, need one workflow task of something else
|
|
581
573
|
// b/c we want to get an activation with a job and a nonlegacy query
|
|
582
574
|
t.add_full_wf_task();
|
|
583
|
-
let timer_started_event_id = t.
|
|
575
|
+
let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
|
|
584
576
|
t.add_timer_fired(timer_started_event_id, "1".to_string());
|
|
585
577
|
|
|
586
578
|
// nonlegacy query got here & LA started here
|
|
@@ -622,8 +614,12 @@ async fn la_resolve_during_legacy_query_does_not_combine(#[case] impossible_quer
|
|
|
622
614
|
2,
|
|
623
615
|
),
|
|
624
616
|
);
|
|
625
|
-
// Strip history
|
|
626
|
-
|
|
617
|
+
// Strip beginning of history so the only events are WFT sched/started, we need to look
|
|
618
|
+
// like we hit the cache
|
|
619
|
+
{
|
|
620
|
+
let h = pr.history.as_mut().unwrap();
|
|
621
|
+
h.events = h.events.split_off(6);
|
|
622
|
+
}
|
|
627
623
|
// In the nonsense server response case, we attach a legacy query, otherwise this
|
|
628
624
|
// response looks like a normal response to a forced WFT heartbeat.
|
|
629
625
|
if impossible_query_in_task {
|
|
@@ -783,12 +779,19 @@ async fn test_schedule_to_start_timeout() {
|
|
|
783
779
|
worker.run_until_done().await.unwrap();
|
|
784
780
|
}
|
|
785
781
|
|
|
782
|
+
#[rstest::rstest]
|
|
783
|
+
#[case::sched_to_start(true)]
|
|
784
|
+
#[case::sched_to_close(false)]
|
|
786
785
|
#[tokio::test]
|
|
787
|
-
async fn test_schedule_to_start_timeout_not_based_on_original_time(
|
|
786
|
+
async fn test_schedule_to_start_timeout_not_based_on_original_time(
|
|
787
|
+
#[case] is_sched_to_start: bool,
|
|
788
|
+
) {
|
|
788
789
|
// We used to carry over the schedule time of LAs from the "original" schedule time if these LAs
|
|
789
790
|
// created newly after backing off across a timer. That was a mistake, since schedule-to-start
|
|
790
|
-
// timeouts should apply to when the new attempt was scheduled. This test verifies
|
|
791
|
-
// time out on s-t-s timeouts because of that.
|
|
791
|
+
// timeouts should apply to when the new attempt was scheduled. This test verifies:
|
|
792
|
+
// * we don't time out on s-t-s timeouts because of that, when the param is true.
|
|
793
|
+
// * we do properly time out on s-t-c timeouts when the param is false
|
|
794
|
+
|
|
792
795
|
let mut t = TestHistoryBuilder::default();
|
|
793
796
|
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
794
797
|
t.add_full_wf_task();
|
|
@@ -805,7 +808,7 @@ async fn test_schedule_to_start_timeout_not_based_on_original_time() {
|
|
|
805
808
|
deets.backoff = Some(prost_dur!(from_secs(100)));
|
|
806
809
|
},
|
|
807
810
|
);
|
|
808
|
-
let timer_started_event_id = t.
|
|
811
|
+
let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
|
|
809
812
|
t.add_timer_fired(timer_started_event_id, "1".to_string());
|
|
810
813
|
t.add_workflow_task_scheduled_and_started();
|
|
811
814
|
|
|
@@ -814,9 +817,18 @@ async fn test_schedule_to_start_timeout_not_based_on_original_time() {
|
|
|
814
817
|
let mh = MockPollCfg::from_resp_batches(wf_id, t, [ResponseType::AllHistory], mock);
|
|
815
818
|
let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
|
|
816
819
|
|
|
820
|
+
let schedule_to_close_timeout = Some(if is_sched_to_start {
|
|
821
|
+
// This 60 minute timeout will not have elapsed according to the original
|
|
822
|
+
// schedule time in the history.
|
|
823
|
+
Duration::from_secs(60 * 60)
|
|
824
|
+
} else {
|
|
825
|
+
// This 10 minute timeout will have already elapsed
|
|
826
|
+
Duration::from_secs(10 * 60)
|
|
827
|
+
});
|
|
828
|
+
|
|
817
829
|
worker.register_wf(
|
|
818
830
|
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
819
|
-
|ctx: WfContext| async move {
|
|
831
|
+
move |ctx: WfContext| async move {
|
|
820
832
|
let la_res = ctx
|
|
821
833
|
.local_activity(LocalActivityOptions {
|
|
822
834
|
activity_type: "echo".to_string(),
|
|
@@ -829,11 +841,15 @@ async fn test_schedule_to_start_timeout_not_based_on_original_time() {
|
|
|
829
841
|
non_retryable_error_types: vec![],
|
|
830
842
|
},
|
|
831
843
|
schedule_to_start_timeout: Some(Duration::from_secs(60)),
|
|
832
|
-
schedule_to_close_timeout
|
|
844
|
+
schedule_to_close_timeout,
|
|
833
845
|
..Default::default()
|
|
834
846
|
})
|
|
835
847
|
.await;
|
|
836
|
-
|
|
848
|
+
if is_sched_to_start {
|
|
849
|
+
assert!(la_res.completed_ok());
|
|
850
|
+
} else {
|
|
851
|
+
assert_eq!(la_res.timed_out(), Some(TimeoutType::ScheduleToClose));
|
|
852
|
+
}
|
|
837
853
|
Ok(().into())
|
|
838
854
|
},
|
|
839
855
|
);
|
|
@@ -854,64 +870,162 @@ async fn test_schedule_to_start_timeout_not_based_on_original_time() {
|
|
|
854
870
|
}
|
|
855
871
|
|
|
856
872
|
#[tokio::test]
|
|
857
|
-
async fn
|
|
858
|
-
// We used to carry over the schedule time of LAs from the "original" schedule time if these LAs
|
|
859
|
-
// created newly after backing off across a timer. That was a mistake, since schedule-to-start
|
|
860
|
-
// timeouts should apply to when the new attempt was scheduled. This test verifies we don't
|
|
861
|
-
// time out on s-t-s timeouts because of that.
|
|
873
|
+
async fn wft_failure_cancels_running_las() {
|
|
862
874
|
let mut t = TestHistoryBuilder::default();
|
|
863
|
-
t.
|
|
875
|
+
t.add_wfe_started_with_wft_timeout(Duration::from_millis(200));
|
|
864
876
|
t.add_full_wf_task();
|
|
865
|
-
let
|
|
866
|
-
t.add_local_activity_marker(
|
|
867
|
-
1,
|
|
868
|
-
"1",
|
|
869
|
-
None,
|
|
870
|
-
Some(Failure::application_failure("la failed".to_string(), false)),
|
|
871
|
-
|deets| {
|
|
872
|
-
// Really old schedule time, which should _not_ count against schedule_to_start
|
|
873
|
-
deets.original_schedule_time = Some(orig_sched.into());
|
|
874
|
-
// Backoff value must be present since we're simulating timer backoff
|
|
875
|
-
deets.backoff = Some(prost_dur!(from_secs(100)));
|
|
876
|
-
},
|
|
877
|
-
);
|
|
878
|
-
let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
|
|
877
|
+
let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
|
|
879
878
|
t.add_timer_fired(timer_started_event_id, "1".to_string());
|
|
880
879
|
t.add_workflow_task_scheduled_and_started();
|
|
881
880
|
|
|
882
881
|
let wf_id = "fakeid";
|
|
883
882
|
let mock = mock_workflow_client();
|
|
884
|
-
let mh = MockPollCfg::from_resp_batches(wf_id, t, [
|
|
883
|
+
let mut mh = MockPollCfg::from_resp_batches(wf_id, t, [1, 2], mock);
|
|
884
|
+
mh.num_expected_fails = 1;
|
|
885
885
|
let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
|
|
886
886
|
|
|
887
887
|
worker.register_wf(
|
|
888
888
|
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
889
889
|
|ctx: WfContext| async move {
|
|
890
|
-
let
|
|
891
|
-
.
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
// schedule time in the history.
|
|
903
|
-
schedule_to_close_timeout: Some(Duration::from_secs(10 * 60)),
|
|
904
|
-
..Default::default()
|
|
905
|
-
})
|
|
906
|
-
.await;
|
|
907
|
-
assert_eq!(la_res.timed_out(), Some(TimeoutType::ScheduleToClose));
|
|
890
|
+
let la_handle = ctx.local_activity(LocalActivityOptions {
|
|
891
|
+
activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
|
|
892
|
+
input: "hi".as_json_payload().expect("serializes fine"),
|
|
893
|
+
..Default::default()
|
|
894
|
+
});
|
|
895
|
+
tokio::join!(
|
|
896
|
+
async {
|
|
897
|
+
ctx.timer(Duration::from_secs(1)).await;
|
|
898
|
+
panic!("ahhh I'm failing wft")
|
|
899
|
+
},
|
|
900
|
+
la_handle
|
|
901
|
+
);
|
|
908
902
|
Ok(().into())
|
|
909
903
|
},
|
|
910
904
|
);
|
|
905
|
+
worker.register_activity(
|
|
906
|
+
DEFAULT_ACTIVITY_TYPE,
|
|
907
|
+
move |ctx: ActContext, _: String| async move {
|
|
908
|
+
let res = tokio::time::timeout(Duration::from_millis(500), ctx.cancelled()).await;
|
|
909
|
+
if res.is_err() {
|
|
910
|
+
panic!("Activity must be cancelled!!!!");
|
|
911
|
+
}
|
|
912
|
+
Result::<(), _>::Err(ActivityCancelledError::default().into())
|
|
913
|
+
},
|
|
914
|
+
);
|
|
915
|
+
worker
|
|
916
|
+
.submit_wf(
|
|
917
|
+
wf_id.to_owned(),
|
|
918
|
+
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
919
|
+
vec![],
|
|
920
|
+
WorkflowOptions::default(),
|
|
921
|
+
)
|
|
922
|
+
.await
|
|
923
|
+
.unwrap();
|
|
924
|
+
worker.run_until_done().await.unwrap();
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
#[tokio::test]
|
|
928
|
+
async fn resolved_las_not_recorded_if_wft_fails_many_times() {
|
|
929
|
+
// We shouldn't record any LA results if the workflow activation is repeatedly failing. There
|
|
930
|
+
// was an issue that, because we stop reporting WFT failures after 2 tries, this meant the WFT
|
|
931
|
+
// was not marked as "completed" and the WFT could accidentally be replied to with LA results.
|
|
932
|
+
let mut t = TestHistoryBuilder::default();
|
|
933
|
+
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
934
|
+
t.add_workflow_task_scheduled_and_started();
|
|
935
|
+
t.add_workflow_task_failed_with_failure(
|
|
936
|
+
WorkflowTaskFailedCause::Unspecified,
|
|
937
|
+
Default::default(),
|
|
938
|
+
);
|
|
939
|
+
t.add_workflow_task_scheduled_and_started();
|
|
940
|
+
|
|
941
|
+
let wf_id = "fakeid";
|
|
942
|
+
let mock = mock_workflow_client();
|
|
943
|
+
let mut mh = MockPollCfg::from_resp_batches(
|
|
944
|
+
wf_id,
|
|
945
|
+
t,
|
|
946
|
+
[1.into(), ResponseType::AllHistory, ResponseType::AllHistory],
|
|
947
|
+
mock,
|
|
948
|
+
);
|
|
949
|
+
mh.num_expected_fails = 2;
|
|
950
|
+
mh.num_expected_completions = Some(0.into());
|
|
951
|
+
let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
|
|
952
|
+
|
|
953
|
+
worker.register_wf(
|
|
954
|
+
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
955
|
+
|ctx: WfContext| async move {
|
|
956
|
+
ctx.local_activity(LocalActivityOptions {
|
|
957
|
+
activity_type: "echo".to_string(),
|
|
958
|
+
input: "hi".as_json_payload().expect("serializes fine"),
|
|
959
|
+
..Default::default()
|
|
960
|
+
})
|
|
961
|
+
.await;
|
|
962
|
+
panic!("Oh nooooo")
|
|
963
|
+
},
|
|
964
|
+
);
|
|
911
965
|
worker.register_activity(
|
|
912
966
|
"echo",
|
|
913
|
-
move |
|
|
967
|
+
move |_: ActContext, _: String| async move { Ok(()) },
|
|
968
|
+
);
|
|
969
|
+
worker
|
|
970
|
+
.submit_wf(
|
|
971
|
+
wf_id.to_owned(),
|
|
972
|
+
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
973
|
+
vec![],
|
|
974
|
+
WorkflowOptions::default(),
|
|
975
|
+
)
|
|
976
|
+
.await
|
|
977
|
+
.unwrap();
|
|
978
|
+
worker.run_until_done().await.unwrap();
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
#[tokio::test]
|
|
982
|
+
async fn local_act_records_nonfirst_attempts_ok() {
|
|
983
|
+
let mut t = TestHistoryBuilder::default();
|
|
984
|
+
let wft_timeout = Duration::from_millis(200);
|
|
985
|
+
t.add_wfe_started_with_wft_timeout(wft_timeout);
|
|
986
|
+
t.add_full_wf_task();
|
|
987
|
+
t.add_full_wf_task();
|
|
988
|
+
t.add_full_wf_task();
|
|
989
|
+
t.add_workflow_task_scheduled_and_started();
|
|
990
|
+
|
|
991
|
+
let wf_id = "fakeid";
|
|
992
|
+
let mock = mock_workflow_client();
|
|
993
|
+
let mut mh = MockPollCfg::from_resp_batches(wf_id, t, [1, 2, 3], mock);
|
|
994
|
+
let nonfirst_counts = Arc::new(SegQueue::new());
|
|
995
|
+
let nfc_c = nonfirst_counts.clone();
|
|
996
|
+
mh.completion_asserts = Some(Box::new(move |c| {
|
|
997
|
+
nfc_c.push(
|
|
998
|
+
c.metering_metadata
|
|
999
|
+
.nonfirst_local_activity_execution_attempts,
|
|
1000
|
+
);
|
|
1001
|
+
}));
|
|
1002
|
+
let mut worker = mock_sdk_cfg(mh, |wc| {
|
|
1003
|
+
wc.max_cached_workflows = 1;
|
|
1004
|
+
wc.max_outstanding_workflow_tasks = 1;
|
|
1005
|
+
});
|
|
1006
|
+
|
|
1007
|
+
worker.register_wf(
|
|
1008
|
+
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
1009
|
+
|ctx: WfContext| async move {
|
|
1010
|
+
ctx.local_activity(LocalActivityOptions {
|
|
1011
|
+
activity_type: "echo".to_string(),
|
|
1012
|
+
input: "hi".as_json_payload().expect("serializes fine"),
|
|
1013
|
+
retry_policy: RetryPolicy {
|
|
1014
|
+
initial_interval: Some(prost_dur!(from_millis(10))),
|
|
1015
|
+
backoff_coefficient: 1.0,
|
|
1016
|
+
maximum_interval: None,
|
|
1017
|
+
maximum_attempts: 0,
|
|
1018
|
+
non_retryable_error_types: vec![],
|
|
1019
|
+
},
|
|
1020
|
+
..Default::default()
|
|
1021
|
+
})
|
|
1022
|
+
.await;
|
|
1023
|
+
Ok(().into())
|
|
1024
|
+
},
|
|
914
1025
|
);
|
|
1026
|
+
worker.register_activity("echo", move |_ctx: ActContext, _: String| async move {
|
|
1027
|
+
Result::<(), _>::Err(anyhow!("I fail"))
|
|
1028
|
+
});
|
|
915
1029
|
worker
|
|
916
1030
|
.submit_wf(
|
|
917
1031
|
wf_id.to_owned(),
|
|
@@ -922,4 +1036,11 @@ async fn test_schedule_to_close_timeout_based_on_original_time() {
|
|
|
922
1036
|
.await
|
|
923
1037
|
.unwrap();
|
|
924
1038
|
worker.run_until_done().await.unwrap();
|
|
1039
|
+
// 3 workflow tasks
|
|
1040
|
+
assert_eq!(nonfirst_counts.len(), 3);
|
|
1041
|
+
// First task's non-first count should, of course, be 0
|
|
1042
|
+
assert_eq!(nonfirst_counts.pop().unwrap(), 0);
|
|
1043
|
+
// Next two, some nonzero amount which could vary based on test load
|
|
1044
|
+
assert!(nonfirst_counts.pop().unwrap() > 0);
|
|
1045
|
+
assert!(nonfirst_counts.pop().unwrap() > 0);
|
|
925
1046
|
}
|
|
@@ -162,7 +162,7 @@ async fn new_queries(#[case] num_queries: usize) {
|
|
|
162
162
|
pr.queries = HashMap::new();
|
|
163
163
|
for i in 1..=num_queries {
|
|
164
164
|
pr.queries.insert(
|
|
165
|
-
format!("q{}"
|
|
165
|
+
format!("q{i}"),
|
|
166
166
|
WorkflowQuery {
|
|
167
167
|
query_type: "query-type".to_string(),
|
|
168
168
|
query_args: Some(b"hi".into()),
|
|
@@ -206,7 +206,7 @@ async fn new_queries(#[case] num_queries: usize) {
|
|
|
206
206
|
let mut qresults: Vec<_> = (1..=num_queries)
|
|
207
207
|
.map(|i| {
|
|
208
208
|
QueryResult {
|
|
209
|
-
query_id: format!("q{}"
|
|
209
|
+
query_id: format!("q{i}"),
|
|
210
210
|
variant: Some(
|
|
211
211
|
QuerySuccess {
|
|
212
212
|
response: Some(query_resp.into()),
|
|
@@ -769,11 +769,10 @@ async fn legacy_query_combined_with_timer_fire_repro() {
|
|
|
769
769
|
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
770
770
|
t.add_full_wf_task();
|
|
771
771
|
let scheduled_event_id = t.add_activity_task_scheduled("1");
|
|
772
|
-
let timer_started_event_id = t.
|
|
772
|
+
let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
|
|
773
773
|
t.add_timer_fired(timer_started_event_id, "1".to_string());
|
|
774
774
|
t.add_full_wf_task();
|
|
775
775
|
t.add(
|
|
776
|
-
EventType::ActivityTaskCancelRequested,
|
|
777
776
|
history_event::Attributes::ActivityTaskCancelRequestedEventAttributes(
|
|
778
777
|
ActivityTaskCancelRequestedEventAttributes {
|
|
779
778
|
scheduled_event_id,
|