@temporalio/core-bridge 1.11.6 → 1.11.8
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.
- package/Cargo.lock +902 -468
- package/package.json +3 -3
- package/releases/aarch64-apple-darwin/index.node +0 -0
- package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
- package/releases/x86_64-apple-darwin/index.node +0 -0
- package/releases/x86_64-pc-windows-msvc/index.node +0 -0
- package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
- package/sdk-core/.cargo/config.toml +5 -0
- package/sdk-core/.github/workflows/per-pr.yml +59 -5
- package/sdk-core/Cargo.toml +3 -2
- package/sdk-core/client/Cargo.toml +3 -3
- package/sdk-core/client/src/lib.rs +154 -161
- package/sdk-core/client/src/metrics.rs +15 -8
- package/sdk-core/client/src/proxy.rs +1 -1
- package/sdk-core/client/src/raw.rs +176 -33
- package/sdk-core/client/src/retry.rs +102 -465
- package/sdk-core/client/src/worker_registry/mod.rs +2 -2
- package/sdk-core/client/src/workflow_handle/mod.rs +19 -1
- package/sdk-core/core/Cargo.toml +12 -14
- package/sdk-core/core/benches/workflow_replay.rs +1 -1
- package/sdk-core/core/src/abstractions.rs +2 -2
- package/sdk-core/core/src/core_tests/activity_tasks.rs +99 -46
- package/sdk-core/core/src/core_tests/child_workflows.rs +68 -9
- package/sdk-core/core/src/core_tests/determinism.rs +2 -2
- package/sdk-core/core/src/core_tests/local_activities.rs +20 -33
- package/sdk-core/core/src/core_tests/mod.rs +7 -8
- package/sdk-core/core/src/core_tests/queries.rs +79 -79
- package/sdk-core/core/src/core_tests/replay_flag.rs +5 -5
- package/sdk-core/core/src/core_tests/updates.rs +6 -6
- package/sdk-core/core/src/core_tests/workers.rs +19 -22
- package/sdk-core/core/src/core_tests/workflow_cancels.rs +3 -3
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +154 -106
- package/sdk-core/core/src/ephemeral_server/mod.rs +66 -10
- package/sdk-core/core/src/internal_flags.rs +103 -12
- package/sdk-core/core/src/lib.rs +21 -13
- package/sdk-core/core/src/pollers/mod.rs +200 -6
- package/sdk-core/core/src/pollers/poll_buffer.rs +32 -8
- package/sdk-core/core/src/protosext/mod.rs +7 -7
- package/sdk-core/core/src/protosext/protocol_messages.rs +2 -2
- package/sdk-core/core/src/replay/mod.rs +8 -9
- package/sdk-core/core/src/retry_logic.rs +8 -6
- package/sdk-core/core/src/telemetry/log_export.rs +4 -4
- package/sdk-core/core/src/telemetry/metrics.rs +111 -25
- package/sdk-core/core/src/telemetry/mod.rs +11 -4
- package/sdk-core/core/src/telemetry/otel.rs +108 -144
- package/sdk-core/core/src/telemetry/prometheus_server.rs +1 -4
- package/sdk-core/core/src/test_help/mod.rs +27 -21
- package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +7 -5
- package/sdk-core/core/src/worker/activities/local_activities.rs +9 -9
- package/sdk-core/core/src/worker/activities.rs +34 -46
- package/sdk-core/core/src/worker/client/mocks.rs +24 -2
- package/sdk-core/core/src/worker/client.rs +169 -33
- package/sdk-core/core/src/worker/mod.rs +132 -56
- package/sdk-core/core/src/worker/nexus.rs +410 -0
- package/sdk-core/core/src/worker/tuner/resource_based.rs +27 -5
- package/sdk-core/core/src/worker/tuner.rs +29 -2
- package/sdk-core/core/src/worker/workflow/driven_workflow.rs +8 -3
- package/sdk-core/core/src/worker/workflow/history_update.rs +5 -8
- package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +83 -87
- package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +38 -38
- package/sdk-core/core/src/worker/workflow/machines/cancel_nexus_op_state_machine.rs +117 -0
- package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +8 -18
- package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +114 -108
- package/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +16 -31
- package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +7 -14
- package/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +8 -15
- package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +34 -75
- package/sdk-core/core/src/worker/workflow/machines/mod.rs +26 -48
- package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +10 -17
- package/sdk-core/core/src/worker/workflow/machines/nexus_operation_state_machine.rs +543 -0
- package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +22 -31
- package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +53 -51
- package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +40 -45
- package/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +2 -2
- package/sdk-core/core/src/worker/workflow/machines/update_state_machine.rs +8 -10
- package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +24 -30
- package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +182 -116
- package/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +4 -8
- package/sdk-core/core/src/worker/workflow/managed_run.rs +75 -45
- package/sdk-core/core/src/worker/workflow/mod.rs +104 -55
- package/sdk-core/core/src/worker/workflow/run_cache.rs +23 -4
- package/sdk-core/core/src/worker/workflow/wft_extraction.rs +4 -4
- package/sdk-core/core/src/worker/workflow/wft_poller.rs +3 -3
- package/sdk-core/core/src/worker/workflow/workflow_stream.rs +32 -13
- package/sdk-core/core-api/Cargo.toml +2 -3
- package/sdk-core/core-api/src/errors.rs +22 -20
- package/sdk-core/core-api/src/lib.rs +24 -5
- package/sdk-core/core-api/src/telemetry/metrics.rs +27 -1
- package/sdk-core/core-api/src/telemetry.rs +37 -3
- package/sdk-core/core-api/src/worker.rs +36 -3
- package/sdk-core/docker/docker-compose-ci.yaml +25 -0
- package/sdk-core/etc/otel-collector-ci.yaml +36 -0
- package/sdk-core/etc/otel-collector-config.yaml +3 -3
- package/sdk-core/etc/prometheus.yaml +1 -1
- package/sdk-core/fsm/Cargo.toml +1 -1
- package/sdk-core/fsm/rustfsm_procmacro/Cargo.toml +1 -1
- package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +3 -4
- package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +1 -1
- package/sdk-core/fsm/rustfsm_trait/Cargo.toml +1 -1
- package/sdk-core/sdk/Cargo.toml +1 -2
- package/sdk-core/sdk/src/activity_context.rs +1 -1
- package/sdk-core/sdk/src/interceptors.rs +1 -1
- package/sdk-core/sdk/src/lib.rs +126 -54
- package/sdk-core/sdk/src/workflow_context/options.rs +184 -74
- package/sdk-core/sdk/src/workflow_context.rs +193 -79
- package/sdk-core/sdk/src/workflow_future.rs +151 -131
- package/sdk-core/sdk-core-protos/Cargo.toml +3 -4
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/VERSION +1 -1
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/account/v1/message.proto +46 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/cloudservice/v1/request_response.proto +254 -5
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/cloudservice/v1/service.proto +108 -2
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/identity/v1/message.proto +94 -15
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/namespace/v1/message.proto +102 -4
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/nexus/v1/message.proto +84 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/operation/v1/message.proto +25 -10
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/region/v1/message.proto +14 -1
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/resource/v1/message.proto +25 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/sink/v1/message.proto +41 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/usage/v1/message.proto +59 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/.github/PULL_REQUEST_TEMPLATE.md +2 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/.github/workflows/create-release.yml +135 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/.github/workflows/push-to-buf.yml +20 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/.github/workflows/trigger-api-go-delete-release.yml +13 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/.github/workflows/trigger-api-go-publish-release.yml +13 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/.github/workflows/trigger-api-go-update.yml +13 -21
- package/sdk-core/sdk-core-protos/protos/api_upstream/Makefile +2 -2
- package/sdk-core/sdk-core-protos/protos/api_upstream/buf.yaml +1 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv2.json +3386 -1047
- package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv3.yaml +3529 -1144
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/batch/v1/message.proto +39 -1
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/command/v1/message.proto +6 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/common/v1/message.proto +39 -1
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/deployment/v1/message.proto +252 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +1 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/common.proto +6 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/deployment.proto +96 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/event_type.proto +2 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +2 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/nexus.proto +42 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/reset.proto +2 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/workflow.proto +43 -2
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/errordetails/v1/message.proto +13 -1
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/failure/v1/message.proto +14 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/history/v1/message.proto +70 -12
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/nexus/v1/message.proto +12 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/query/v1/message.proto +9 -2
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +46 -2
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflow/v1/message.proto +206 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +482 -97
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +230 -43
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/core_interface.proto +6 -0
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/nexus/nexus.proto +71 -0
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +46 -2
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +55 -9
- package/sdk-core/sdk-core-protos/src/history_builder.rs +5 -5
- package/sdk-core/sdk-core-protos/src/history_info.rs +5 -6
- package/sdk-core/sdk-core-protos/src/lib.rs +414 -34
- package/sdk-core/sdk-core-protos/src/task_token.rs +1 -1
- package/sdk-core/test-utils/Cargo.toml +3 -11
- package/sdk-core/test-utils/src/canned_histories.rs +1 -1
- package/sdk-core/test-utils/src/lib.rs +159 -85
- package/sdk-core/tests/fuzzy_workflow.rs +3 -3
- package/sdk-core/tests/heavy_tests.rs +3 -3
- package/sdk-core/tests/integ_tests/client_tests.rs +171 -20
- package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +45 -39
- package/sdk-core/tests/integ_tests/heartbeat_tests.rs +7 -6
- package/sdk-core/tests/integ_tests/metrics_tests.rs +492 -35
- package/sdk-core/tests/integ_tests/polling_tests.rs +7 -5
- package/sdk-core/tests/integ_tests/queries_tests.rs +14 -17
- package/sdk-core/tests/integ_tests/update_tests.rs +47 -44
- package/sdk-core/tests/integ_tests/visibility_tests.rs +4 -3
- package/sdk-core/tests/integ_tests/worker_tests.rs +5 -5
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +15 -13
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +28 -14
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +7 -1
- package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +57 -4
- package/sdk-core/tests/integ_tests/workflow_tests/eager.rs +1 -1
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +24 -18
- package/sdk-core/tests/integ_tests/workflow_tests/nexus.rs +506 -0
- package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +1 -1
- package/sdk-core/tests/integ_tests/workflow_tests/priority.rs +104 -0
- package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +34 -31
- package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -1
- package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +10 -7
- package/sdk-core/tests/integ_tests/workflow_tests.rs +152 -116
- package/sdk-core/tests/main.rs +36 -6
- package/sdk-core/tests/runner.rs +30 -9
- package/src/conversions/slot_supplier_bridge.rs +4 -0
- package/src/conversions.rs +1 -0
- package/src/worker.rs +5 -7
- package/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +0 -78
|
@@ -1,56 +1,57 @@
|
|
|
1
1
|
use crate::{
|
|
2
|
-
advance_fut,
|
|
2
|
+
Worker, advance_fut,
|
|
3
3
|
internal_flags::CoreInternalFlags,
|
|
4
4
|
job_assert,
|
|
5
5
|
replay::TestHistoryBuilder,
|
|
6
6
|
test_help::{
|
|
7
|
+
FakeWfResponses, MockPollCfg, MocksHolder, ResponseType, WorkerExt,
|
|
8
|
+
WorkflowCachingPolicy::{self, AfterEveryReply, NonSticky},
|
|
7
9
|
build_fake_worker, build_mock_pollers, build_multihist_mock_sg, canned_histories,
|
|
8
10
|
gen_assert_and_fail, gen_assert_and_reply, hist_to_poll_resp, mock_sdk, mock_sdk_cfg,
|
|
9
11
|
mock_worker, poll_and_reply, poll_and_reply_clears_outstanding_evicts, single_hist_mock_sg,
|
|
10
|
-
test_worker_cfg,
|
|
11
|
-
WorkflowCachingPolicy::{self, AfterEveryReply, NonSticky},
|
|
12
|
+
test_worker_cfg,
|
|
12
13
|
},
|
|
13
14
|
worker::{
|
|
14
|
-
client::mocks::{mock_manual_workflow_client, mock_workflow_client},
|
|
15
15
|
TunerBuilder,
|
|
16
|
+
client::mocks::{mock_manual_workflow_client, mock_workflow_client},
|
|
16
17
|
},
|
|
17
|
-
Worker,
|
|
18
18
|
};
|
|
19
|
-
use futures_util::{
|
|
19
|
+
use futures_util::{FutureExt, stream};
|
|
20
20
|
use mockall::TimesRange;
|
|
21
21
|
use rstest::{fixture, rstest};
|
|
22
22
|
use std::{
|
|
23
23
|
collections::{HashMap, HashSet, VecDeque},
|
|
24
24
|
sync::{
|
|
25
|
+
Arc,
|
|
25
26
|
atomic::{AtomicU64, AtomicUsize, Ordering},
|
|
26
27
|
mpsc::sync_channel,
|
|
27
|
-
Arc,
|
|
28
28
|
},
|
|
29
29
|
time::Duration,
|
|
30
30
|
};
|
|
31
31
|
use temporal_client::WorkflowOptions;
|
|
32
|
-
use temporal_sdk::{ActivityOptions, CancellableFuture, WfContext};
|
|
32
|
+
use temporal_sdk::{ActivityOptions, CancellableFuture, TimerOptions, WfContext};
|
|
33
33
|
use temporal_sdk_core_api::{
|
|
34
|
-
|
|
34
|
+
Worker as WorkerTrait,
|
|
35
|
+
errors::PollError,
|
|
35
36
|
worker::{
|
|
36
37
|
SlotMarkUsedContext, SlotReleaseContext, SlotReservationContext, SlotSupplier,
|
|
37
38
|
SlotSupplierPermit, WorkflowSlotKind,
|
|
38
39
|
},
|
|
39
|
-
Worker as WorkerTrait,
|
|
40
40
|
};
|
|
41
41
|
use temporal_sdk_core_protos::{
|
|
42
|
+
DEFAULT_ACTIVITY_TYPE, DEFAULT_WORKFLOW_TYPE,
|
|
42
43
|
coresdk::{
|
|
43
|
-
activity_result::{self as ar,
|
|
44
|
+
activity_result::{self as ar, ActivityResolution, activity_resolution},
|
|
44
45
|
common::VersioningIntent,
|
|
45
46
|
workflow_activation::{
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
FireTimer, InitializeWorkflow, ResolveActivity, UpdateRandomSeed,
|
|
48
|
+
WorkflowActivationJob, remove_from_cache::EvictionReason, workflow_activation_job,
|
|
48
49
|
},
|
|
49
50
|
workflow_commands::{
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
ActivityCancellationType, CancelTimer, CompleteWorkflowExecution,
|
|
52
|
+
ContinueAsNewWorkflowExecution, FailWorkflowExecution, RequestCancelActivity,
|
|
53
|
+
ScheduleActivity, SetPatchMarker, StartChildWorkflowExecution, UpdateResponse,
|
|
54
|
+
update_response::Response, workflow_command,
|
|
54
55
|
},
|
|
55
56
|
workflow_completion::WorkflowActivationCompletion,
|
|
56
57
|
},
|
|
@@ -61,16 +62,16 @@ use temporal_sdk_core_protos::{
|
|
|
61
62
|
enums::v1::{CommandType, EventType, WorkflowTaskFailedCause},
|
|
62
63
|
failure::v1::Failure,
|
|
63
64
|
history::v1::{
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
TimerFiredEventAttributes, WorkflowPropertiesModifiedExternallyEventAttributes,
|
|
66
|
+
history_event,
|
|
66
67
|
},
|
|
68
|
+
sdk::v1::UserMetadata,
|
|
67
69
|
workflowservice::v1::{
|
|
68
70
|
GetWorkflowExecutionHistoryResponse, RespondWorkflowTaskCompletedResponse,
|
|
69
71
|
},
|
|
70
72
|
},
|
|
71
|
-
DEFAULT_ACTIVITY_TYPE, DEFAULT_WORKFLOW_TYPE,
|
|
72
73
|
};
|
|
73
|
-
use temporal_sdk_core_test_utils::{fanout_tasks, start_timer_cmd
|
|
74
|
+
use temporal_sdk_core_test_utils::{WorkerTestHelpers, fanout_tasks, start_timer_cmd};
|
|
74
75
|
use tokio::{
|
|
75
76
|
join,
|
|
76
77
|
sync::{Barrier, Semaphore},
|
|
@@ -139,11 +140,13 @@ async fn single_activity_completion(worker: Worker) {
|
|
|
139
140
|
&[
|
|
140
141
|
gen_assert_and_reply(
|
|
141
142
|
&job_assert!(workflow_activation_job::Variant::InitializeWorkflow(_)),
|
|
142
|
-
vec![
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
143
|
+
vec![
|
|
144
|
+
ScheduleActivity {
|
|
145
|
+
activity_id: "fake_activity".to_string(),
|
|
146
|
+
..default_act_sched()
|
|
147
|
+
}
|
|
148
|
+
.into(),
|
|
149
|
+
],
|
|
147
150
|
),
|
|
148
151
|
gen_assert_and_reply(
|
|
149
152
|
&job_assert!(workflow_activation_job::Variant::ResolveActivity(_)),
|
|
@@ -262,13 +265,15 @@ async fn scheduled_activity_cancellation_try_cancel(hist_batches: &'static [usiz
|
|
|
262
265
|
&[
|
|
263
266
|
gen_assert_and_reply(
|
|
264
267
|
&job_assert!(workflow_activation_job::Variant::InitializeWorkflow(_)),
|
|
265
|
-
vec![
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
268
|
+
vec![
|
|
269
|
+
ScheduleActivity {
|
|
270
|
+
seq: activity_seq,
|
|
271
|
+
activity_id: activity_id.to_string(),
|
|
272
|
+
cancellation_type: ActivityCancellationType::TryCancel as i32,
|
|
273
|
+
..default_act_sched()
|
|
274
|
+
}
|
|
275
|
+
.into(),
|
|
276
|
+
],
|
|
272
277
|
),
|
|
273
278
|
gen_assert_and_reply(
|
|
274
279
|
&job_assert!(workflow_activation_job::Variant::SignalWorkflow(_)),
|
|
@@ -407,12 +412,14 @@ async fn cancelled_activity_timeout(hist_batches: &'static [usize]) {
|
|
|
407
412
|
&[
|
|
408
413
|
gen_assert_and_reply(
|
|
409
414
|
&job_assert!(workflow_activation_job::Variant::InitializeWorkflow(_)),
|
|
410
|
-
vec![
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
415
|
+
vec![
|
|
416
|
+
ScheduleActivity {
|
|
417
|
+
seq: activity_seq,
|
|
418
|
+
activity_id: activity_id.to_string(),
|
|
419
|
+
..default_act_sched()
|
|
420
|
+
}
|
|
421
|
+
.into(),
|
|
422
|
+
],
|
|
416
423
|
),
|
|
417
424
|
gen_assert_and_reply(
|
|
418
425
|
&job_assert!(workflow_activation_job::Variant::SignalWorkflow(_)),
|
|
@@ -559,13 +566,15 @@ async fn verify_activity_cancellation(
|
|
|
559
566
|
&[
|
|
560
567
|
gen_assert_and_reply(
|
|
561
568
|
&job_assert!(workflow_activation_job::Variant::InitializeWorkflow(_)),
|
|
562
|
-
vec![
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
+
vec![
|
|
570
|
+
ScheduleActivity {
|
|
571
|
+
seq: activity_seq,
|
|
572
|
+
activity_id: activity_seq.to_string(),
|
|
573
|
+
cancellation_type: cancel_type as i32,
|
|
574
|
+
..default_act_sched()
|
|
575
|
+
}
|
|
576
|
+
.into(),
|
|
577
|
+
],
|
|
569
578
|
),
|
|
570
579
|
gen_assert_and_reply(
|
|
571
580
|
&job_assert!(workflow_activation_job::Variant::SignalWorkflow(_)),
|
|
@@ -627,13 +636,16 @@ async fn verify_activity_cancellation_wait_for_cancellation(activity_id: u32, wo
|
|
|
627
636
|
&[
|
|
628
637
|
gen_assert_and_reply(
|
|
629
638
|
&job_assert!(workflow_activation_job::Variant::InitializeWorkflow(_)),
|
|
630
|
-
vec![
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
639
|
+
vec![
|
|
640
|
+
ScheduleActivity {
|
|
641
|
+
seq: activity_id,
|
|
642
|
+
activity_id: activity_id.to_string(),
|
|
643
|
+
cancellation_type: ActivityCancellationType::WaitCancellationCompleted
|
|
644
|
+
as i32,
|
|
645
|
+
..default_act_sched()
|
|
646
|
+
}
|
|
647
|
+
.into(),
|
|
648
|
+
],
|
|
637
649
|
),
|
|
638
650
|
gen_assert_and_reply(
|
|
639
651
|
&job_assert!(workflow_activation_job::Variant::SignalWorkflow(_)),
|
|
@@ -810,13 +822,15 @@ async fn simple_timer_fail_wf_execution(hist_batches: &'static [usize]) {
|
|
|
810
822
|
),
|
|
811
823
|
gen_assert_and_reply(
|
|
812
824
|
&job_assert!(workflow_activation_job::Variant::FireTimer(_)),
|
|
813
|
-
vec![
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
825
|
+
vec![
|
|
826
|
+
FailWorkflowExecution {
|
|
827
|
+
failure: Some(Failure {
|
|
828
|
+
message: "I'm ded".to_string(),
|
|
829
|
+
..Default::default()
|
|
830
|
+
}),
|
|
831
|
+
}
|
|
832
|
+
.into(),
|
|
833
|
+
],
|
|
820
834
|
),
|
|
821
835
|
],
|
|
822
836
|
)
|
|
@@ -1007,13 +1021,15 @@ async fn activity_not_canceled_when_also_completed_repro(hist_batches: &'static
|
|
|
1007
1021
|
&[
|
|
1008
1022
|
gen_assert_and_reply(
|
|
1009
1023
|
&job_assert!(workflow_activation_job::Variant::InitializeWorkflow(_)),
|
|
1010
|
-
vec![
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1024
|
+
vec![
|
|
1025
|
+
ScheduleActivity {
|
|
1026
|
+
seq: activity_id,
|
|
1027
|
+
activity_id: "act-1".to_string(),
|
|
1028
|
+
cancellation_type: ActivityCancellationType::TryCancel as i32,
|
|
1029
|
+
..default_act_sched()
|
|
1030
|
+
}
|
|
1031
|
+
.into(),
|
|
1032
|
+
],
|
|
1017
1033
|
),
|
|
1018
1034
|
gen_assert_and_reply(
|
|
1019
1035
|
&job_assert!(workflow_activation_job::Variant::SignalWorkflow(_)),
|
|
@@ -1108,13 +1124,15 @@ async fn wft_timeout_repro(hist_batches: &'static [usize]) {
|
|
|
1108
1124
|
&[
|
|
1109
1125
|
gen_assert_and_reply(
|
|
1110
1126
|
&job_assert!(workflow_activation_job::Variant::InitializeWorkflow(_)),
|
|
1111
|
-
vec![
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1127
|
+
vec![
|
|
1128
|
+
ScheduleActivity {
|
|
1129
|
+
seq: activity_id,
|
|
1130
|
+
activity_id: activity_id.to_string(),
|
|
1131
|
+
cancellation_type: ActivityCancellationType::TryCancel as i32,
|
|
1132
|
+
..default_act_sched()
|
|
1133
|
+
}
|
|
1134
|
+
.into(),
|
|
1135
|
+
],
|
|
1118
1136
|
),
|
|
1119
1137
|
gen_assert_and_reply(
|
|
1120
1138
|
&job_assert!(
|
|
@@ -1320,26 +1338,18 @@ async fn fail_wft_then_recover() {
|
|
|
1320
1338
|
// Start an activity instead of a timer, triggering nondeterminism error
|
|
1321
1339
|
core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
|
|
1322
1340
|
act.run_id.clone(),
|
|
1323
|
-
vec![
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1341
|
+
vec![
|
|
1342
|
+
ScheduleActivity {
|
|
1343
|
+
activity_id: "fake_activity".to_string(),
|
|
1344
|
+
..default_act_sched()
|
|
1345
|
+
}
|
|
1346
|
+
.into(),
|
|
1347
|
+
],
|
|
1328
1348
|
))
|
|
1329
1349
|
.await
|
|
1330
1350
|
.unwrap();
|
|
1331
1351
|
// We must handle an eviction now
|
|
1332
|
-
|
|
1333
|
-
assert_eq!(evict_act.run_id, act.run_id);
|
|
1334
|
-
assert_matches!(
|
|
1335
|
-
evict_act.jobs.as_slice(),
|
|
1336
|
-
[WorkflowActivationJob {
|
|
1337
|
-
variant: Some(workflow_activation_job::Variant::RemoveFromCache(_)),
|
|
1338
|
-
}]
|
|
1339
|
-
);
|
|
1340
|
-
core.complete_workflow_activation(WorkflowActivationCompletion::empty(evict_act.run_id))
|
|
1341
|
-
.await
|
|
1342
|
-
.unwrap();
|
|
1352
|
+
core.handle_eviction().await;
|
|
1343
1353
|
|
|
1344
1354
|
// Workflow starting over, this time issue the right command
|
|
1345
1355
|
let act = core.poll_workflow_activation().await.unwrap();
|
|
@@ -1530,6 +1540,7 @@ async fn failing_wft_doesnt_eat_permit_forever() {
|
|
|
1530
1540
|
// row because we purposefully time out rather than spamming.
|
|
1531
1541
|
for _ in 1..=2 {
|
|
1532
1542
|
let activation = worker.poll_workflow_activation().await.unwrap();
|
|
1543
|
+
run_id.clone_from(&activation.run_id);
|
|
1533
1544
|
// Issue a nonsense completion that will trigger a WFT failure
|
|
1534
1545
|
worker
|
|
1535
1546
|
.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
|
|
@@ -1538,18 +1549,7 @@ async fn failing_wft_doesnt_eat_permit_forever() {
|
|
|
1538
1549
|
))
|
|
1539
1550
|
.await
|
|
1540
1551
|
.unwrap();
|
|
1541
|
-
|
|
1542
|
-
assert_matches!(
|
|
1543
|
-
activation.jobs.as_slice(),
|
|
1544
|
-
[WorkflowActivationJob {
|
|
1545
|
-
variant: Some(workflow_activation_job::Variant::RemoveFromCache(_)),
|
|
1546
|
-
},]
|
|
1547
|
-
);
|
|
1548
|
-
run_id.clone_from(&activation.run_id);
|
|
1549
|
-
worker
|
|
1550
|
-
.complete_workflow_activation(WorkflowActivationCompletion::empty(activation.run_id))
|
|
1551
|
-
.await
|
|
1552
|
-
.unwrap();
|
|
1552
|
+
worker.handle_eviction().await;
|
|
1553
1553
|
}
|
|
1554
1554
|
assert_eq!(worker.outstanding_workflow_tasks().await, 0);
|
|
1555
1555
|
// We should be "out of work" because the mock service thinks we didn't complete the last task,
|
|
@@ -2043,7 +2043,7 @@ async fn autocompletes_wft_no_work() {
|
|
|
2043
2043
|
// work
|
|
2044
2044
|
assert_matches!(
|
|
2045
2045
|
core.poll_workflow_activation().await.unwrap_err(),
|
|
2046
|
-
|
|
2046
|
+
PollError::ShutDown
|
|
2047
2047
|
);
|
|
2048
2048
|
|
|
2049
2049
|
core.shutdown().await;
|
|
@@ -2600,6 +2600,7 @@ async fn _do_post_terminal_commands_test(
|
|
|
2600
2600
|
|
|
2601
2601
|
let act = core.poll_workflow_activation().await.unwrap();
|
|
2602
2602
|
|
|
2603
|
+
core.initiate_shutdown();
|
|
2603
2604
|
core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
|
|
2604
2605
|
act.run_id,
|
|
2605
2606
|
commands_sent_by_lang,
|
|
@@ -2608,7 +2609,7 @@ async fn _do_post_terminal_commands_test(
|
|
|
2608
2609
|
.unwrap();
|
|
2609
2610
|
|
|
2610
2611
|
let act = core.poll_workflow_activation().await;
|
|
2611
|
-
assert_matches!(act.unwrap_err(),
|
|
2612
|
+
assert_matches!(act.unwrap_err(), PollError::ShutDown);
|
|
2612
2613
|
core.shutdown().await;
|
|
2613
2614
|
}
|
|
2614
2615
|
|
|
@@ -2810,7 +2811,7 @@ async fn poller_wont_run_ahead_of_task_slots() {
|
|
|
2810
2811
|
// This should end up getting shut down after the other routine finishes tasks
|
|
2811
2812
|
assert_matches!(
|
|
2812
2813
|
worker.poll_workflow_activation().await.unwrap_err(),
|
|
2813
|
-
|
|
2814
|
+
PollError::ShutDown
|
|
2814
2815
|
);
|
|
2815
2816
|
};
|
|
2816
2817
|
// Wait for a bit concurrently with above, verify no extra tasks got taken, shutdown
|
|
@@ -3065,7 +3066,7 @@ async fn slot_provider_cant_hand_out_more_permits_than_cache_size() {
|
|
|
3065
3066
|
// This should end up getting shut down after the other routine finishes tasks
|
|
3066
3067
|
assert_matches!(
|
|
3067
3068
|
worker.poll_workflow_activation().await.unwrap_err(),
|
|
3068
|
-
|
|
3069
|
+
PollError::ShutDown
|
|
3069
3070
|
);
|
|
3070
3071
|
};
|
|
3071
3072
|
// Wait for a bit concurrently with above, verify no extra tasks got taken, shutdown
|
|
@@ -3086,3 +3087,50 @@ async fn slot_provider_cant_hand_out_more_permits_than_cache_size() {
|
|
|
3086
3087
|
// polling is not exceeding the task limit
|
|
3087
3088
|
assert_eq!(popped_tasks.load(Ordering::Relaxed), 10);
|
|
3088
3089
|
}
|
|
3090
|
+
|
|
3091
|
+
#[tokio::test]
|
|
3092
|
+
async fn pass_timer_summary_to_metadata() {
|
|
3093
|
+
let t = canned_histories::single_timer("1");
|
|
3094
|
+
let mut mock_cfg = MockPollCfg::from_hist_builder(t);
|
|
3095
|
+
let wf_id = mock_cfg.hists[0].wf_id.clone();
|
|
3096
|
+
let wf_type = DEFAULT_WORKFLOW_TYPE;
|
|
3097
|
+
let expected_user_metadata = Some(UserMetadata {
|
|
3098
|
+
summary: Some(b"timer summary".into()),
|
|
3099
|
+
details: None,
|
|
3100
|
+
});
|
|
3101
|
+
mock_cfg.completion_asserts_from_expectations(|mut asserts| {
|
|
3102
|
+
asserts
|
|
3103
|
+
.then(move |wft| {
|
|
3104
|
+
assert_eq!(wft.commands.len(), 1);
|
|
3105
|
+
assert_eq!(wft.commands[0].command_type(), CommandType::StartTimer);
|
|
3106
|
+
assert_eq!(wft.commands[0].user_metadata, expected_user_metadata)
|
|
3107
|
+
})
|
|
3108
|
+
.then(move |wft| {
|
|
3109
|
+
assert_eq!(wft.commands.len(), 1);
|
|
3110
|
+
assert_eq!(
|
|
3111
|
+
wft.commands[0].command_type(),
|
|
3112
|
+
CommandType::CompleteWorkflowExecution
|
|
3113
|
+
);
|
|
3114
|
+
});
|
|
3115
|
+
});
|
|
3116
|
+
|
|
3117
|
+
let mut worker = mock_sdk_cfg(mock_cfg, |_| {});
|
|
3118
|
+
worker.register_wf(wf_type, |ctx: WfContext| async move {
|
|
3119
|
+
ctx.timer(TimerOptions {
|
|
3120
|
+
duration: Duration::from_secs(1),
|
|
3121
|
+
summary: Some("timer summary".to_string()),
|
|
3122
|
+
})
|
|
3123
|
+
.await;
|
|
3124
|
+
Ok(().into())
|
|
3125
|
+
});
|
|
3126
|
+
worker
|
|
3127
|
+
.submit_wf(
|
|
3128
|
+
wf_id.to_owned(),
|
|
3129
|
+
wf_type.to_owned(),
|
|
3130
|
+
vec![],
|
|
3131
|
+
WorkflowOptions::default(),
|
|
3132
|
+
)
|
|
3133
|
+
.await
|
|
3134
|
+
.unwrap();
|
|
3135
|
+
worker.run_until_done().await.unwrap();
|
|
3136
|
+
}
|
|
@@ -13,7 +13,7 @@ use std::{
|
|
|
13
13
|
use temporal_client::ClientOptionsBuilder;
|
|
14
14
|
use tokio::{
|
|
15
15
|
task::spawn_blocking,
|
|
16
|
-
time::{
|
|
16
|
+
time::{Duration, sleep},
|
|
17
17
|
};
|
|
18
18
|
use tokio_util::io::{StreamReader, SyncIoBridge};
|
|
19
19
|
use url::Url;
|
|
@@ -101,7 +101,7 @@ impl TemporalDevServerConfig {
|
|
|
101
101
|
"frontend.enableUpdateWorkflowExecutionAsyncAccepted=true".to_owned(),
|
|
102
102
|
];
|
|
103
103
|
if let Some(db_filename) = &self.db_filename {
|
|
104
|
-
args.push("--filename".to_owned());
|
|
104
|
+
args.push("--db-filename".to_owned());
|
|
105
105
|
args.push(db_filename.clone());
|
|
106
106
|
}
|
|
107
107
|
if let Some(ui_port) = self.ui_port {
|
|
@@ -270,6 +270,8 @@ pub enum EphemeralExe {
|
|
|
270
270
|
version: EphemeralExeVersion,
|
|
271
271
|
/// Destination directory or the user temp directory if none set.
|
|
272
272
|
dest_dir: Option<String>,
|
|
273
|
+
/// How long to cache the download for. None means forever.
|
|
274
|
+
ttl: Option<Duration>,
|
|
273
275
|
},
|
|
274
276
|
}
|
|
275
277
|
|
|
@@ -309,7 +311,11 @@ impl EphemeralExe {
|
|
|
309
311
|
}
|
|
310
312
|
Ok(path)
|
|
311
313
|
}
|
|
312
|
-
EphemeralExe::CachedDownload {
|
|
314
|
+
EphemeralExe::CachedDownload {
|
|
315
|
+
version,
|
|
316
|
+
dest_dir,
|
|
317
|
+
ttl,
|
|
318
|
+
} => {
|
|
313
319
|
let dest_dir = dest_dir
|
|
314
320
|
.as_ref()
|
|
315
321
|
.map(PathBuf::from)
|
|
@@ -334,8 +340,7 @@ impl EphemeralExe {
|
|
|
334
340
|
dest.display()
|
|
335
341
|
);
|
|
336
342
|
|
|
337
|
-
|
|
338
|
-
if dest.exists() {
|
|
343
|
+
if dest.exists() && remove_file_past_ttl(ttl, &dest)? {
|
|
339
344
|
return Ok(dest);
|
|
340
345
|
}
|
|
341
346
|
|
|
@@ -379,6 +384,7 @@ impl EphemeralExe {
|
|
|
379
384
|
&info.archive_url,
|
|
380
385
|
Path::new(&info.file_to_extract),
|
|
381
386
|
&dest,
|
|
387
|
+
false,
|
|
382
388
|
)
|
|
383
389
|
.await?
|
|
384
390
|
{
|
|
@@ -433,7 +439,7 @@ fn get_free_port(bind_ip: &str) -> io::Result<u16> {
|
|
|
433
439
|
let (socket, _addr) = listen.accept()?;
|
|
434
440
|
|
|
435
441
|
// Explicitly drop the socket to close the connection from the listening side first
|
|
436
|
-
|
|
442
|
+
drop(socket);
|
|
437
443
|
}
|
|
438
444
|
|
|
439
445
|
Ok(addr.port())
|
|
@@ -447,6 +453,7 @@ async fn lazy_download_exe(
|
|
|
447
453
|
uri: &str,
|
|
448
454
|
file_to_extract: &Path,
|
|
449
455
|
dest: &Path,
|
|
456
|
+
already_tried_cleaning_old: bool,
|
|
450
457
|
) -> anyhow::Result<bool> {
|
|
451
458
|
// If it already exists, do not extract
|
|
452
459
|
if dest.exists() {
|
|
@@ -474,7 +481,16 @@ async fn lazy_download_exe(
|
|
|
474
481
|
// This match only gets Ok if the file was downloaded and extracted to the
|
|
475
482
|
// temporary path
|
|
476
483
|
match file {
|
|
477
|
-
Err(err) if err.kind() ==
|
|
484
|
+
Err(err) if err.kind() == io::ErrorKind::AlreadyExists => {
|
|
485
|
+
// If the download lock file exists but is old, delete it and try again, since it may
|
|
486
|
+
// have been left by an abandoned process.
|
|
487
|
+
if !already_tried_cleaning_old
|
|
488
|
+
&& temp_dest.metadata()?.modified()?.elapsed()?.as_secs() > 90
|
|
489
|
+
{
|
|
490
|
+
std::fs::remove_file(temp_dest)?;
|
|
491
|
+
return Box::pin(lazy_download_exe(client, uri, file_to_extract, dest, true)).await;
|
|
492
|
+
}
|
|
493
|
+
|
|
478
494
|
// Since it already exists, we'll try once a second for 20 seconds
|
|
479
495
|
// to wait for it to be done, then return false so the caller can
|
|
480
496
|
// try again.
|
|
@@ -530,7 +546,7 @@ async fn download_and_extract(
|
|
|
530
546
|
// We have to map the error type to an io error
|
|
531
547
|
let stream = resp
|
|
532
548
|
.bytes_stream()
|
|
533
|
-
.map(|item| item.map_err(|err|
|
|
549
|
+
.map(|item| item.map_err(|err| io::Error::new(io::ErrorKind::Other, err)));
|
|
534
550
|
|
|
535
551
|
// Since our tar/zip impls use sync IO, we have to create a bridge and run
|
|
536
552
|
// in a blocking closure.
|
|
@@ -574,12 +590,36 @@ async fn download_and_extract(
|
|
|
574
590
|
.await?
|
|
575
591
|
}
|
|
576
592
|
|
|
593
|
+
/// Remove the file if it's older than the TTL. Returns true if the current file can be re-used,
|
|
594
|
+
/// returns false if it was removed or should otherwise be re-downloaded.
|
|
595
|
+
fn remove_file_past_ttl(ttl: &Option<Duration>, dest: &PathBuf) -> Result<bool, anyhow::Error> {
|
|
596
|
+
match ttl {
|
|
597
|
+
None => return Ok(true),
|
|
598
|
+
Some(ttl) => {
|
|
599
|
+
if let Ok(mtime) = dest.metadata().and_then(|d| d.modified()) {
|
|
600
|
+
if mtime.elapsed().unwrap_or_default().lt(ttl) {
|
|
601
|
+
return Ok(true);
|
|
602
|
+
} else {
|
|
603
|
+
// Remove so we can re-download
|
|
604
|
+
std::fs::remove_file(dest)?;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
// If we couldn't read the mtime something weird is probably up, so
|
|
608
|
+
// re-download
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
Ok(false)
|
|
612
|
+
}
|
|
613
|
+
|
|
577
614
|
#[cfg(test)]
|
|
578
615
|
mod tests {
|
|
579
|
-
use super::get_free_port;
|
|
616
|
+
use super::{get_free_port, remove_file_past_ttl};
|
|
580
617
|
use std::{
|
|
581
618
|
collections::HashSet,
|
|
619
|
+
env::temp_dir,
|
|
620
|
+
fs::File,
|
|
582
621
|
net::{TcpListener, TcpStream},
|
|
622
|
+
time::{Duration, SystemTime},
|
|
583
623
|
};
|
|
584
624
|
|
|
585
625
|
#[test]
|
|
@@ -609,6 +649,22 @@ mod tests {
|
|
|
609
649
|
}
|
|
610
650
|
}
|
|
611
651
|
|
|
652
|
+
#[tokio::test]
|
|
653
|
+
async fn respects_file_ttl() {
|
|
654
|
+
let rand_fname = format!("{}", rand::random::<u64>());
|
|
655
|
+
let temp_dir = temp_dir();
|
|
656
|
+
|
|
657
|
+
let dest_file_path = temp_dir.join(format!("core-test-{}", &rand_fname));
|
|
658
|
+
let dest_file = File::create(&dest_file_path).unwrap();
|
|
659
|
+
let set_time_to = SystemTime::now() - Duration::from_secs(100);
|
|
660
|
+
dest_file.set_modified(set_time_to).unwrap();
|
|
661
|
+
|
|
662
|
+
remove_file_past_ttl(&Some(Duration::from_secs(60)), &dest_file_path).unwrap();
|
|
663
|
+
|
|
664
|
+
// file should be gone
|
|
665
|
+
assert!(!dest_file_path.exists());
|
|
666
|
+
}
|
|
667
|
+
|
|
612
668
|
fn try_listen_and_dial_on(host: &str, port: u16) -> std::io::Result<()> {
|
|
613
669
|
let listener = TcpListener::bind((host, port))?;
|
|
614
670
|
let _stream = TcpStream::connect((host, port))?;
|
|
@@ -617,7 +673,7 @@ mod tests {
|
|
|
617
673
|
let (socket, _addr) = listener.accept()?;
|
|
618
674
|
|
|
619
675
|
// Explicitly drop the socket to close the connection from the listening side first
|
|
620
|
-
|
|
676
|
+
drop(socket);
|
|
621
677
|
|
|
622
678
|
Ok(())
|
|
623
679
|
}
|