@temporalio/core-bridge 0.16.4 → 0.18.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 (170) hide show
  1. package/Cargo.lock +339 -226
  2. package/Cargo.toml +7 -3
  3. package/common.js +50 -0
  4. package/index.d.ts +7 -0
  5. package/index.js +12 -0
  6. package/package.json +7 -4
  7. package/releases/aarch64-apple-darwin/index.node +0 -0
  8. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  9. package/{index.node → releases/index.node} +0 -0
  10. package/releases/x86_64-apple-darwin/index.node +0 -0
  11. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  12. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  13. package/scripts/build.js +10 -50
  14. package/sdk-core/.buildkite/docker/Dockerfile +1 -1
  15. package/sdk-core/.buildkite/docker/docker-compose.yaml +2 -2
  16. package/sdk-core/.buildkite/pipeline.yml +2 -0
  17. package/sdk-core/Cargo.toml +1 -88
  18. package/sdk-core/README.md +30 -6
  19. package/sdk-core/bridge-ffi/Cargo.toml +24 -0
  20. package/sdk-core/bridge-ffi/LICENSE.txt +23 -0
  21. package/sdk-core/bridge-ffi/build.rs +25 -0
  22. package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +216 -0
  23. package/sdk-core/bridge-ffi/src/lib.rs +829 -0
  24. package/sdk-core/bridge-ffi/src/wrappers.rs +193 -0
  25. package/sdk-core/client/Cargo.toml +32 -0
  26. package/sdk-core/{src/pollers/gateway.rs → client/src/lib.rs} +101 -195
  27. package/sdk-core/client/src/metrics.rs +89 -0
  28. package/sdk-core/client/src/mocks.rs +167 -0
  29. package/sdk-core/{src/pollers → client/src}/retry.rs +172 -14
  30. package/sdk-core/core/Cargo.toml +96 -0
  31. package/sdk-core/{src → core/src}/core_tests/activity_tasks.rs +193 -37
  32. package/sdk-core/{src → core/src}/core_tests/child_workflows.rs +14 -14
  33. package/sdk-core/{src → core/src}/core_tests/determinism.rs +8 -8
  34. package/sdk-core/core/src/core_tests/local_activities.rs +328 -0
  35. package/sdk-core/{src → core/src}/core_tests/mod.rs +6 -9
  36. package/sdk-core/{src → core/src}/core_tests/queries.rs +54 -54
  37. package/sdk-core/{src → core/src}/core_tests/replay_flag.rs +8 -12
  38. package/sdk-core/{src → core/src}/core_tests/workers.rs +120 -33
  39. package/sdk-core/{src → core/src}/core_tests/workflow_cancels.rs +16 -26
  40. package/sdk-core/{src → core/src}/core_tests/workflow_tasks.rs +280 -292
  41. package/sdk-core/core/src/lib.rs +374 -0
  42. package/sdk-core/{src → core/src}/log_export.rs +3 -27
  43. package/sdk-core/core/src/pending_activations.rs +162 -0
  44. package/sdk-core/{src → core/src}/pollers/mod.rs +4 -22
  45. package/sdk-core/{src → core/src}/pollers/poll_buffer.rs +1 -1
  46. package/sdk-core/core/src/protosext/mod.rs +396 -0
  47. package/sdk-core/core/src/replay/mod.rs +210 -0
  48. package/sdk-core/core/src/retry_logic.rs +144 -0
  49. package/sdk-core/{src → core/src}/telemetry/metrics.rs +3 -58
  50. package/sdk-core/{src → core/src}/telemetry/mod.rs +8 -8
  51. package/sdk-core/{src → core/src}/telemetry/prometheus_server.rs +0 -0
  52. package/sdk-core/{src → core/src}/test_help/mod.rs +35 -83
  53. package/sdk-core/{src → core/src}/worker/activities/activity_heartbeat_manager.rs +95 -42
  54. package/sdk-core/core/src/worker/activities/local_activities.rs +973 -0
  55. package/sdk-core/{src → core/src}/worker/activities.rs +52 -33
  56. package/sdk-core/{src → core/src}/worker/dispatcher.rs +8 -6
  57. package/sdk-core/{src → core/src}/worker/mod.rs +347 -221
  58. package/sdk-core/core/src/worker/wft_delivery.rs +81 -0
  59. package/sdk-core/{src → core/src}/workflow/bridge.rs +5 -2
  60. package/sdk-core/{src → core/src}/workflow/driven_workflow.rs +17 -7
  61. package/sdk-core/{src → core/src}/workflow/history_update.rs +33 -7
  62. package/sdk-core/{src → core/src/workflow}/machines/activity_state_machine.rs +26 -26
  63. package/sdk-core/{src → core/src/workflow}/machines/cancel_external_state_machine.rs +8 -11
  64. package/sdk-core/{src → core/src/workflow}/machines/cancel_workflow_state_machine.rs +19 -21
  65. package/sdk-core/{src → core/src/workflow}/machines/child_workflow_state_machine.rs +20 -31
  66. package/sdk-core/{src → core/src/workflow}/machines/complete_workflow_state_machine.rs +3 -5
  67. package/sdk-core/{src → core/src/workflow}/machines/continue_as_new_workflow_state_machine.rs +18 -18
  68. package/sdk-core/{src → core/src/workflow}/machines/fail_workflow_state_machine.rs +5 -6
  69. package/sdk-core/core/src/workflow/machines/local_activity_state_machine.rs +1451 -0
  70. package/sdk-core/{src → core/src/workflow}/machines/mod.rs +54 -107
  71. package/sdk-core/{src → core/src/workflow}/machines/mutable_side_effect_state_machine.rs +0 -0
  72. package/sdk-core/{src → core/src/workflow}/machines/patch_state_machine.rs +29 -30
  73. package/sdk-core/{src → core/src/workflow}/machines/side_effect_state_machine.rs +0 -0
  74. package/sdk-core/{src → core/src/workflow}/machines/signal_external_state_machine.rs +17 -19
  75. package/sdk-core/{src → core/src/workflow}/machines/timer_state_machine.rs +20 -21
  76. package/sdk-core/{src → core/src/workflow}/machines/transition_coverage.rs +5 -2
  77. package/sdk-core/{src → core/src/workflow}/machines/upsert_search_attributes_state_machine.rs +0 -0
  78. package/sdk-core/core/src/workflow/machines/workflow_machines/local_acts.rs +96 -0
  79. package/sdk-core/{src → core/src/workflow}/machines/workflow_machines.rs +357 -171
  80. package/sdk-core/{src → core/src/workflow}/machines/workflow_task_state_machine.rs +1 -1
  81. package/sdk-core/{src → core/src}/workflow/mod.rs +200 -39
  82. package/sdk-core/{src → core/src}/workflow/workflow_tasks/cache_manager.rs +0 -0
  83. package/sdk-core/{src → core/src}/workflow/workflow_tasks/concurrency_manager.rs +38 -5
  84. package/sdk-core/{src → core/src}/workflow/workflow_tasks/mod.rs +317 -103
  85. package/sdk-core/{test_utils → core-api}/Cargo.toml +10 -7
  86. package/sdk-core/{src → core-api/src}/errors.rs +42 -92
  87. package/sdk-core/core-api/src/lib.rs +158 -0
  88. package/sdk-core/{src/worker/config.rs → core-api/src/worker.rs} +18 -23
  89. package/sdk-core/etc/deps.svg +156 -0
  90. package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +5 -5
  91. package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +3 -5
  92. package/sdk-core/fsm/rustfsm_trait/src/lib.rs +7 -1
  93. package/sdk-core/histories/fail_wf_task.bin +0 -0
  94. package/sdk-core/histories/timer_workflow_history.bin +0 -0
  95. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +44 -13
  96. package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +19 -1
  97. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +1 -1
  98. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +9 -0
  99. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +1 -0
  100. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +1 -0
  101. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +13 -0
  102. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +14 -7
  103. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +176 -18
  104. package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +6 -0
  105. package/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +11 -0
  106. package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +3 -0
  107. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +156 -7
  108. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +135 -104
  109. package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +78 -0
  110. package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +78 -0
  111. package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +205 -0
  112. package/sdk-core/protos/local/temporal/sdk/core/bridge/service.proto +61 -0
  113. package/sdk-core/protos/local/{child_workflow.proto → temporal/sdk/core/child_workflow/child_workflow.proto} +1 -1
  114. package/sdk-core/protos/local/{common.proto → temporal/sdk/core/common/common.proto} +5 -3
  115. package/sdk-core/protos/local/{core_interface.proto → temporal/sdk/core/core_interface.proto} +10 -10
  116. package/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +30 -0
  117. package/sdk-core/protos/local/{workflow_activation.proto → temporal/sdk/core/workflow_activation/workflow_activation.proto} +35 -11
  118. package/sdk-core/protos/local/{workflow_commands.proto → temporal/sdk/core/workflow_commands/workflow_commands.proto} +55 -4
  119. package/sdk-core/protos/local/{workflow_completion.proto → temporal/sdk/core/workflow_completion/workflow_completion.proto} +3 -3
  120. package/sdk-core/sdk/Cargo.toml +32 -0
  121. package/sdk-core/{src/prototype_rust_sdk → sdk/src}/conversions.rs +0 -0
  122. package/sdk-core/sdk/src/lib.rs +699 -0
  123. package/sdk-core/sdk/src/payload_converter.rs +11 -0
  124. package/sdk-core/sdk/src/workflow_context/options.rs +180 -0
  125. package/sdk-core/{src/prototype_rust_sdk → sdk/src}/workflow_context.rs +201 -124
  126. package/sdk-core/{src/prototype_rust_sdk → sdk/src}/workflow_future.rs +63 -30
  127. package/sdk-core/sdk-core-protos/Cargo.toml +10 -0
  128. package/sdk-core/sdk-core-protos/build.rs +28 -6
  129. package/sdk-core/sdk-core-protos/src/constants.rs +7 -0
  130. package/sdk-core/{src/test_help → sdk-core-protos/src}/history_builder.rs +134 -49
  131. package/sdk-core/sdk-core-protos/src/history_info.rs +216 -0
  132. package/sdk-core/sdk-core-protos/src/lib.rs +601 -168
  133. package/sdk-core/sdk-core-protos/src/task_token.rs +38 -0
  134. package/sdk-core/sdk-core-protos/src/utilities.rs +14 -0
  135. package/sdk-core/test-utils/Cargo.toml +32 -0
  136. package/sdk-core/{src/test_help → test-utils/src}/canned_histories.rs +59 -78
  137. package/sdk-core/test-utils/src/histfetch.rs +28 -0
  138. package/sdk-core/{test_utils → test-utils}/src/lib.rs +131 -68
  139. package/sdk-core/tests/integ_tests/client_tests.rs +1 -1
  140. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +11 -7
  141. package/sdk-core/tests/integ_tests/polling_tests.rs +12 -11
  142. package/sdk-core/tests/integ_tests/queries_tests.rs +82 -78
  143. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +91 -71
  144. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +3 -4
  145. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +2 -4
  146. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +4 -6
  147. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +4 -6
  148. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -4
  149. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +496 -0
  150. package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +5 -8
  151. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +125 -0
  152. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +7 -13
  153. package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +33 -5
  154. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +12 -16
  155. package/sdk-core/tests/integ_tests/workflow_tests.rs +85 -82
  156. package/sdk-core/tests/load_tests.rs +6 -6
  157. package/sdk-core/tests/main.rs +2 -2
  158. package/src/conversions.rs +24 -21
  159. package/src/errors.rs +8 -0
  160. package/src/lib.rs +323 -211
  161. package/sdk-core/protos/local/activity_result.proto +0 -46
  162. package/sdk-core/protos/local/activity_task.proto +0 -66
  163. package/sdk-core/src/core_tests/retry.rs +0 -147
  164. package/sdk-core/src/lib.rs +0 -403
  165. package/sdk-core/src/machines/local_activity_state_machine.rs +0 -117
  166. package/sdk-core/src/pending_activations.rs +0 -249
  167. package/sdk-core/src/protosext/mod.rs +0 -160
  168. package/sdk-core/src/prototype_rust_sdk.rs +0 -412
  169. package/sdk-core/src/task_token.rs +0 -20
  170. package/sdk-core/src/test_help/history_info.rs +0 -157
@@ -1,46 +0,0 @@
1
- syntax = "proto3";
2
-
3
- package coresdk.activity_result;
4
-
5
- import "common.proto";
6
- import "temporal/api/failure/v1/message.proto";
7
-
8
- /**
9
- * Used to report activity completion to core and to resolve the activity in a workflow activation
10
- */
11
- message ActivityResult {
12
- oneof status {
13
- Success completed = 1;
14
- Failure failed = 2;
15
- Cancellation cancelled = 3;
16
- WillCompleteAsync will_complete_async = 4;
17
- }
18
- }
19
-
20
- /** Used in ActivityResult to report successful completion */
21
- message Success {
22
- common.Payload result = 1;
23
- }
24
-
25
- /** Used in ActivityResult to report failure */
26
- message Failure {
27
- temporal.api.failure.v1.Failure failure = 1;
28
- }
29
-
30
- /**
31
- * Used in ActivityResult to report cancellation from both Core and Lang.
32
- * When Lang reports a cancelled ActivityResult, it must put a CancelledFailure in the failure field.
33
- * When Core reports a cancelled ActivityResult, it must put an ActivityFailure with CancelledFailure
34
- * as the cause in the failure field.
35
- */
36
- message Cancellation {
37
- temporal.api.failure.v1.Failure failure = 1;
38
- }
39
-
40
- /**
41
- * Used in ActivityResult to notify Core that this Activity will complete asynchronously.
42
- * Core will forget about this Activity and free up resources used to track this Activity.
43
- */
44
- message WillCompleteAsync {
45
- }
46
-
@@ -1,66 +0,0 @@
1
- syntax = "proto3";
2
-
3
- /**
4
- * Definitions of the different activity tasks returned from [crate::Core::poll_task].
5
- */
6
- package coresdk.activity_task;
7
-
8
- import "common.proto";
9
-
10
- import "google/protobuf/timestamp.proto";
11
- import "google/protobuf/duration.proto";
12
-
13
- message ActivityTask {
14
- /// A unique identifier for this task
15
- bytes task_token = 1;
16
- /// The activity's ID
17
- string activity_id = 2;
18
- oneof variant {
19
- /// Start activity execution.
20
- Start start = 3;
21
- /// Attempt to cancel activity execution.
22
- Cancel cancel = 4;
23
- }
24
- }
25
-
26
- /// Begin executing an activity
27
- message Start {
28
- /// The namespace the workflow lives in
29
- string workflow_namespace = 1;
30
- /// The workflow's type name or function identifier
31
- string workflow_type = 2;
32
- common.WorkflowExecution workflow_execution = 3;
33
- /// The activity's type name or function identifier
34
- string activity_type = 4;
35
- map<string, common.Payload> header_fields = 5;
36
- /// Arguments to the activity
37
- repeated common.Payload input = 6;
38
- repeated common.Payload heartbeat_details = 7;
39
-
40
- google.protobuf.Timestamp scheduled_time = 8;
41
- google.protobuf.Timestamp current_attempt_scheduled_time = 9;
42
- google.protobuf.Timestamp started_time = 10;
43
- int32 attempt = 11;
44
-
45
- google.protobuf.Duration schedule_to_close_timeout = 12;
46
- google.protobuf.Duration start_to_close_timeout = 13;
47
- google.protobuf.Duration heartbeat_timeout = 14;
48
- /// This is an actual retry policy the service uses. It can be different from the one provided
49
- /// (or not) during activity scheduling as the service can override the provided one in case its
50
- /// values are not specified or exceed configured system limits.
51
- common.RetryPolicy retry_policy = 15;
52
- }
53
-
54
- /// Attempt to cancel a running activity
55
- message Cancel {
56
- ActivityCancelReason reason = 1;
57
- }
58
-
59
- enum ActivityCancelReason {
60
- /// The activity no longer exists according to server (may be already completed)
61
- NOT_FOUND = 0;
62
- /// Activity was explicitly cancelled
63
- CANCELLED = 1;
64
- }
65
-
66
-
@@ -1,147 +0,0 @@
1
- use crate::{
2
- pollers::{MockServerGatewayApis, RetryGateway, RETRYABLE_ERROR_CODES},
3
- ServerGatewayApis,
4
- };
5
- use tonic::{Code, Status};
6
-
7
- #[tokio::test]
8
- async fn non_retryable_errors() {
9
- for code in [
10
- Code::InvalidArgument,
11
- Code::NotFound,
12
- Code::AlreadyExists,
13
- Code::PermissionDenied,
14
- Code::FailedPrecondition,
15
- Code::Cancelled,
16
- Code::DeadlineExceeded,
17
- Code::Unauthenticated,
18
- Code::Unimplemented,
19
- ] {
20
- let mut mock_gateway = MockServerGatewayApis::new();
21
- mock_gateway
22
- .expect_cancel_activity_task()
23
- .returning(move |_, _| Err(Status::new(code, "non-retryable failure")))
24
- .times(1);
25
- let retry_gateway = RetryGateway::new(mock_gateway, Default::default());
26
- let result = retry_gateway
27
- .cancel_activity_task(vec![1].into(), None)
28
- .await;
29
- // Expecting an error after a single attempt, since there was a non-retryable error.
30
- assert!(result.is_err());
31
- }
32
- }
33
-
34
- #[tokio::test]
35
- async fn long_poll_non_retryable_errors() {
36
- for code in [
37
- Code::InvalidArgument,
38
- Code::NotFound,
39
- Code::AlreadyExists,
40
- Code::PermissionDenied,
41
- Code::FailedPrecondition,
42
- Code::Unauthenticated,
43
- Code::Unimplemented,
44
- ] {
45
- let mut mock_gateway = MockServerGatewayApis::new();
46
- mock_gateway
47
- .expect_poll_workflow_task()
48
- .returning(move |_, _| Err(Status::new(code, "non-retryable failure")))
49
- .times(1);
50
- mock_gateway
51
- .expect_poll_activity_task()
52
- .returning(move |_| Err(Status::new(code, "non-retryable failure")))
53
- .times(1);
54
- let retry_gateway = RetryGateway::new(mock_gateway, Default::default());
55
- let result = retry_gateway
56
- .poll_workflow_task("tq".to_string(), false)
57
- .await;
58
- assert!(result.is_err());
59
- let result = retry_gateway.poll_activity_task("tq".to_string()).await;
60
- assert!(result.is_err());
61
- }
62
- }
63
-
64
- #[tokio::test]
65
- async fn retryable_errors() {
66
- for code in RETRYABLE_ERROR_CODES {
67
- let mut mock_gateway = MockServerGatewayApis::new();
68
- mock_gateway
69
- .expect_cancel_activity_task()
70
- .returning(move |_, _| Err(Status::new(code, "retryable failure")))
71
- .times(3);
72
- mock_gateway
73
- .expect_cancel_activity_task()
74
- .returning(|_, _| Ok(Default::default()))
75
- .times(1);
76
-
77
- let retry_gateway = RetryGateway::new(mock_gateway, Default::default());
78
- let result = retry_gateway
79
- .cancel_activity_task(vec![1].into(), None)
80
- .await;
81
- // Expecting successful response after retries
82
- assert!(result.is_ok());
83
- }
84
- }
85
-
86
- #[tokio::test]
87
- async fn long_poll_retries_forever() {
88
- let mut mock_gateway = MockServerGatewayApis::new();
89
- mock_gateway
90
- .expect_poll_workflow_task()
91
- .returning(move |_, _| Err(Status::new(Code::Unknown, "retryable failure")))
92
- .times(50);
93
- mock_gateway
94
- .expect_poll_workflow_task()
95
- .returning(|_, _| Ok(Default::default()))
96
- .times(1);
97
- mock_gateway
98
- .expect_poll_activity_task()
99
- .returning(move |_| Err(Status::new(Code::Unknown, "retryable failure")))
100
- .times(50);
101
- mock_gateway
102
- .expect_poll_activity_task()
103
- .returning(|_| Ok(Default::default()))
104
- .times(1);
105
-
106
- let retry_gateway = RetryGateway::new(mock_gateway, Default::default());
107
-
108
- let result = retry_gateway
109
- .poll_workflow_task("tq".to_string(), false)
110
- .await;
111
- assert!(result.is_ok());
112
- let result = retry_gateway.poll_activity_task("tq".to_string()).await;
113
- assert!(result.is_ok());
114
- }
115
-
116
- #[tokio::test]
117
- async fn long_poll_retries_deadline_exceeded() {
118
- // For some reason we will get cancelled in these situations occasionally (always?) too
119
- for code in [Code::Cancelled, Code::DeadlineExceeded] {
120
- let mut mock_gateway = MockServerGatewayApis::new();
121
- mock_gateway
122
- .expect_poll_workflow_task()
123
- .returning(move |_, _| Err(Status::new(code, "retryable failure")))
124
- .times(5);
125
- mock_gateway
126
- .expect_poll_workflow_task()
127
- .returning(|_, _| Ok(Default::default()))
128
- .times(1);
129
- mock_gateway
130
- .expect_poll_activity_task()
131
- .returning(move |_| Err(Status::new(code, "retryable failure")))
132
- .times(5);
133
- mock_gateway
134
- .expect_poll_activity_task()
135
- .returning(|_| Ok(Default::default()))
136
- .times(1);
137
-
138
- let retry_gateway = RetryGateway::new(mock_gateway, Default::default());
139
-
140
- let result = retry_gateway
141
- .poll_workflow_task("tq".to_string(), false)
142
- .await;
143
- assert!(result.is_ok());
144
- let result = retry_gateway.poll_activity_task("tq".to_string()).await;
145
- assert!(result.is_ok());
146
- }
147
- }
@@ -1,403 +0,0 @@
1
- #![warn(missing_docs)] // error if there are missing docs
2
- #![allow(clippy::upper_case_acronyms)]
3
-
4
- //! This crate provides a basis for creating new Temporal SDKs without completely starting from
5
- //! scratch
6
-
7
- #[cfg(test)]
8
- #[macro_use]
9
- pub extern crate assert_matches;
10
- #[macro_use]
11
- extern crate tracing;
12
-
13
- pub mod errors;
14
- pub mod prototype_rust_sdk;
15
-
16
- mod log_export;
17
- mod machines;
18
- mod pending_activations;
19
- mod pollers;
20
- mod protosext;
21
- pub(crate) mod task_token;
22
- pub(crate) mod telemetry;
23
- mod worker;
24
- mod workflow;
25
-
26
- #[cfg(test)]
27
- mod core_tests;
28
- #[cfg(test)]
29
- #[macro_use]
30
- mod test_help;
31
-
32
- pub use log_export::CoreLog;
33
- pub use pollers::{
34
- ClientTlsConfig, RetryConfig, RetryGateway, ServerGateway, ServerGatewayApis,
35
- ServerGatewayOptions, ServerGatewayOptionsBuilder, TlsConfig,
36
- };
37
- pub use telemetry::{TelemetryOptions, TelemetryOptionsBuilder};
38
- pub use url::Url;
39
- pub use worker::{WorkerConfig, WorkerConfigBuilder};
40
-
41
- use crate::{
42
- errors::{
43
- ActivityHeartbeatError, CompleteActivityError, CompleteWfError, CoreInitError,
44
- PollActivityError, PollWfError, WorkerRegistrationError,
45
- },
46
- pollers::GatewayRef,
47
- task_token::TaskToken,
48
- telemetry::{fetch_global_buffered_logs, telemetry_init},
49
- worker::{Worker, WorkerDispatcher},
50
- };
51
- use std::{
52
- ops::Deref,
53
- sync::{
54
- atomic::{AtomicBool, Ordering},
55
- Arc,
56
- },
57
- };
58
- use temporal_sdk_core_protos::coresdk::{
59
- activity_task::ActivityTask, workflow_activation::WfActivation,
60
- workflow_completion::WfActivationCompletion, ActivityHeartbeat, ActivityTaskCompletion,
61
- };
62
-
63
- use crate::telemetry::metrics::MetricsContext;
64
- #[cfg(test)]
65
- use crate::test_help::MockWorker;
66
-
67
- lazy_static::lazy_static! {
68
- /// A process-wide unique string, which will be different on every startup
69
- static ref PROCCESS_UNIQ_ID: String = {
70
- uuid::Uuid::new_v4().to_simple().to_string()
71
- };
72
- }
73
-
74
- /// This trait is the primary way by which language specific SDKs interact with the core SDK. It is
75
- /// expected that only one instance of an implementation will exist for the lifetime of the
76
- /// worker(s) using it.
77
- #[async_trait::async_trait]
78
- pub trait Core: Send + Sync {
79
- /// Register a worker with core. Workers poll on a specific task queue, and when calling core's
80
- /// poll functions, you must provide a task queue name. If there was already a worker registered
81
- /// with the same task queue name, it will be shut down and a new one will be created.
82
- async fn register_worker(&self, config: WorkerConfig) -> Result<(), WorkerRegistrationError>;
83
-
84
- /// Ask the core for some work, returning a [WfActivation]. It is then the language SDK's
85
- /// responsibility to call the appropriate workflow code with the provided inputs. Blocks
86
- /// indefinitely until such work is available or [Core::shutdown] is called.
87
- ///
88
- /// The returned activation is guaranteed to be for the same task queue / worker which was
89
- /// provided as the `task_queue` argument.
90
- ///
91
- /// It is important to understand that all activations must be responded to. There can only
92
- /// be one outstanding activation for a particular run of a workflow at any time. If an
93
- /// activation is not responded to, it will cause that workflow to become stuck forever.
94
- ///
95
- /// Activations that contain only a `remove_from_cache` job should not cause the workflow code
96
- /// to be invoked and may be responded to with an empty command list. Eviction jobs may also
97
- /// appear with other jobs, but will always appear last in the job list. In this case it is
98
- /// expected that the workflow code will be invoked, and the response produced as normal, but
99
- /// the caller should evict the run after doing so.
100
- ///
101
- /// It is rarely a good idea to call poll concurrently. It handles polling the server
102
- /// concurrently internally.
103
- ///
104
- /// TODO: Examples
105
- async fn poll_workflow_activation(&self, task_queue: &str)
106
- -> Result<WfActivation, PollWfError>;
107
-
108
- /// Ask the core for some work, returning an [ActivityTask]. It is then the language SDK's
109
- /// responsibility to call the appropriate activity code with the provided inputs. Blocks
110
- /// indefinitely until such work is available or [Core::shutdown] is called.
111
- ///
112
- /// The returned activation is guaranteed to be for the same task queue / worker which was
113
- /// provided as the `task_queue` argument.
114
- ///
115
- /// It is rarely a good idea to call poll concurrently. It handles polling the server
116
- /// concurrently internally.
117
- ///
118
- /// TODO: Examples
119
- async fn poll_activity_task(&self, task_queue: &str)
120
- -> Result<ActivityTask, PollActivityError>;
121
-
122
- /// Tell the core that a workflow activation has completed. May be freely called concurrently.
123
- async fn complete_workflow_activation(
124
- &self,
125
- completion: WfActivationCompletion,
126
- ) -> Result<(), CompleteWfError>;
127
-
128
- /// Tell the core that an activity has finished executing. May be freely called concurrently.
129
- async fn complete_activity_task(
130
- &self,
131
- completion: ActivityTaskCompletion,
132
- ) -> Result<(), CompleteActivityError>;
133
-
134
- /// Notify workflow that an activity is still alive. Long running activities that take longer
135
- /// than `activity_heartbeat_timeout` to finish must call this function in order to report
136
- /// progress, otherwise the activity will timeout and a new attempt will be scheduled.
137
- ///
138
- /// The first heartbeat request will be sent immediately, subsequent rapid calls to this
139
- /// function will result in heartbeat requests being aggregated and the last one received during
140
- /// the aggregation period will be sent to the server, where that period is defined as half the
141
- /// heartbeat timeout.
142
- ///
143
- /// Unlike java/go SDKs we do not return cancellation status as part of heartbeat response and
144
- /// instead send it as a separate activity task to the lang, decoupling heartbeat and
145
- /// cancellation processing.
146
- ///
147
- /// For now activity still need to send heartbeats if they want to receive cancellation
148
- /// requests. In the future we will change this and will dispatch cancellations more
149
- /// proactively. Note that this function does not block on the server call and returns
150
- /// immediately. Underlying validation errors are swallowed and logged, this has been agreed to
151
- /// be optimal behavior for the user as we don't want to break activity execution due to badly
152
- /// configured heartbeat options.
153
- fn record_activity_heartbeat(&self, details: ActivityHeartbeat);
154
-
155
- /// Request that a workflow be evicted by its run id. This will generate a workflow activation
156
- /// with the eviction job inside it to be eventually returned by
157
- /// [Core::poll_workflow_activation]. If the workflow had any existing outstanding activations,
158
- /// such activations are invalidated and subsequent completions of them will do nothing and log
159
- /// a warning.
160
- fn request_workflow_eviction(&self, task_queue: &str, run_id: &str);
161
-
162
- /// Returns core's instance of the [ServerGatewayApis] implementor it is using.
163
- fn server_gateway(&self) -> Arc<dyn ServerGatewayApis + Send + Sync>;
164
-
165
- /// Initiates async shutdown procedure, eventually ceases all polling of the server and shuts
166
- /// down all registered workers. [Core::poll_workflow_activation] should be called until it
167
- /// returns [PollWfError::ShutDown] to ensure that any workflows which are still undergoing
168
- /// replay have an opportunity to finish. This means that the lang sdk will need to call
169
- /// [Core::complete_workflow_activation] for those workflows until they are done. At that point,
170
- /// the lang SDK can end the process, or drop the [Core] instance, which will close the
171
- /// connection.
172
- async fn shutdown(&self);
173
-
174
- /// Shut down a specific worker. Will cease all polling on the task queue and future attempts
175
- /// to poll that queue will return [PollWfError::NoWorkerForQueue].
176
- async fn shutdown_worker(&self, task_queue: &str);
177
-
178
- /// Retrieve options that were passed in when initializing core
179
- fn get_init_options(&self) -> &CoreInitOptions;
180
-
181
- /// Core buffers logs that should be shuttled over to lang so that they may be rendered with
182
- /// the user's desired logging library. Use this function to grab the most recent buffered logs
183
- /// since the last time it was called. A fixed number of such logs are retained at maximum, with
184
- /// the oldest being dropped when full.
185
- ///
186
- /// Returns the list of logs from oldest to newest. Returns an empty vec if the feature is not
187
- /// configured.
188
- fn fetch_buffered_logs(&self) -> Vec<CoreLog>;
189
- }
190
-
191
- /// Holds various configuration information required to call [init]
192
- #[derive(Debug, Clone, derive_builder::Builder)]
193
- #[builder(setter(into))]
194
- #[non_exhaustive]
195
- pub struct CoreInitOptions {
196
- /// Options for the connection to the temporal server
197
- pub gateway_opts: ServerGatewayOptions,
198
- /// Options for telemetry (traces and metrics)
199
- #[builder(default)]
200
- pub telemetry_opts: TelemetryOptions,
201
- }
202
-
203
- /// Initializes an instance of the core sdk and establishes a connection to the temporal server.
204
- ///
205
- /// Note: Also creates a tokio runtime that will be used for all client-server interactions.
206
- ///
207
- /// # Panics
208
- /// * Will panic if called from within an async context, as it will construct a runtime and you
209
- /// cannot construct a runtime from within a runtime.
210
- pub async fn init(opts: CoreInitOptions) -> Result<impl Core, CoreInitError> {
211
- telemetry_init(&opts.telemetry_opts).map_err(CoreInitError::TelemetryInitError)?;
212
- // Initialize server client
213
- let server_gateway = opts.gateway_opts.connect().await?;
214
-
215
- Ok(CoreSDK::new(server_gateway, opts))
216
- }
217
-
218
- struct CoreSDK {
219
- /// Options provided at initialization time
220
- init_options: CoreInitOptions,
221
- /// Provides work in the form of responses the server would send from polling task Qs
222
- server_gateway: Arc<GatewayRef>,
223
- /// Controls access to workers
224
- workers: WorkerDispatcher,
225
- /// Has shutdown been called?
226
- shutdown_requested: AtomicBool,
227
- /// Top-level metrics context
228
- metrics: MetricsContext,
229
- }
230
-
231
- #[async_trait::async_trait]
232
- impl Core for CoreSDK {
233
- async fn register_worker(&self, config: WorkerConfig) -> Result<(), WorkerRegistrationError> {
234
- info!(
235
- task_queue = config.task_queue.as_str(),
236
- "Registering worker"
237
- );
238
- let sticky_q = self.get_sticky_q_name_for_worker(&config);
239
- self.workers
240
- .new_worker(
241
- config,
242
- sticky_q,
243
- self.server_gateway.clone(),
244
- self.metrics.clone(),
245
- )
246
- .await
247
- }
248
-
249
- #[instrument(level = "debug", skip(self), fields(run_id))]
250
- async fn poll_workflow_activation(
251
- &self,
252
- task_queue: &str,
253
- ) -> Result<WfActivation, PollWfError> {
254
- let worker = self.worker(task_queue)?;
255
- worker.next_workflow_activation().await
256
- }
257
-
258
- #[instrument(level = "debug", skip(self))]
259
- async fn poll_activity_task(
260
- &self,
261
- task_queue: &str,
262
- ) -> Result<ActivityTask, PollActivityError> {
263
- loop {
264
- if self.shutdown_requested.load(Ordering::Relaxed) {
265
- return Err(PollActivityError::ShutDown);
266
- }
267
- let worker = self.worker(task_queue)?;
268
- match worker.activity_poll().await.transpose() {
269
- Some(r) => break r,
270
- None => continue,
271
- }
272
- }
273
- }
274
-
275
- #[instrument(level = "debug", skip(self, completion),
276
- fields(completion=%&completion, run_id=%completion.run_id))]
277
- async fn complete_workflow_activation(
278
- &self,
279
- completion: WfActivationCompletion,
280
- ) -> Result<(), CompleteWfError> {
281
- let worker = self.worker(&completion.task_queue)?;
282
- worker.complete_workflow_activation(completion).await
283
- }
284
-
285
- #[instrument(level = "debug", skip(self))]
286
- async fn complete_activity_task(
287
- &self,
288
- completion: ActivityTaskCompletion,
289
- ) -> Result<(), CompleteActivityError> {
290
- let task_token = TaskToken(completion.task_token);
291
- let status = if let Some(s) = completion.result.and_then(|r| r.status) {
292
- s
293
- } else {
294
- return Err(CompleteActivityError::MalformedActivityCompletion {
295
- reason: "Activity completion had empty result/status field".to_owned(),
296
- completion: None,
297
- });
298
- };
299
-
300
- let worker = self.worker(&completion.task_queue)?;
301
- worker.complete_activity(task_token, status).await
302
- }
303
-
304
- fn record_activity_heartbeat(&self, details: ActivityHeartbeat) {
305
- if let Ok(w) = self.worker(&details.task_queue) {
306
- w.record_heartbeat(details);
307
- }
308
- }
309
-
310
- fn request_workflow_eviction(&self, task_queue: &str, run_id: &str) {
311
- if let Ok(w) = self.worker(task_queue) {
312
- w.request_wf_eviction(run_id, "Eviction explicitly requested by lang");
313
- }
314
- }
315
-
316
- fn server_gateway(&self) -> Arc<dyn ServerGatewayApis + Send + Sync> {
317
- self.server_gateway.gw.clone()
318
- }
319
-
320
- async fn shutdown(&self) {
321
- self.shutdown_requested.store(true, Ordering::SeqCst);
322
- self.workers.shutdown_all().await;
323
- }
324
-
325
- async fn shutdown_worker(&self, task_queue: &str) {
326
- self.workers.shutdown_one(task_queue).await;
327
- }
328
-
329
- fn get_init_options(&self) -> &CoreInitOptions {
330
- &self.init_options
331
- }
332
-
333
- fn fetch_buffered_logs(&self) -> Vec<CoreLog> {
334
- fetch_global_buffered_logs()
335
- }
336
- }
337
-
338
- impl CoreSDK {
339
- pub(crate) fn new<SG: ServerGatewayApis + Send + Sync + 'static>(
340
- server_gateway: SG,
341
- init_options: CoreInitOptions,
342
- ) -> Self {
343
- let sg = GatewayRef::new(Arc::new(server_gateway), init_options.gateway_opts.clone());
344
- let workers = WorkerDispatcher::default();
345
- Self {
346
- workers,
347
- init_options,
348
- shutdown_requested: AtomicBool::new(false),
349
- metrics: MetricsContext::top_level(sg.options.namespace.clone()),
350
- server_gateway: Arc::new(sg),
351
- }
352
- }
353
-
354
- /// Allow construction of workers with mocked poll responses during testing
355
- #[cfg(test)]
356
- pub(crate) fn reg_worker_sync(&mut self, worker: MockWorker) {
357
- let sticky_q = self.get_sticky_q_name_for_worker(&worker.config);
358
- let tq = worker.config.task_queue.clone();
359
- let worker = Worker::new_with_pollers(
360
- worker.config,
361
- sticky_q,
362
- self.server_gateway.clone(),
363
- worker.wf_poller,
364
- worker.act_poller,
365
- self.metrics.clone(),
366
- );
367
- self.workers.set_worker_for_task_queue(tq, worker).unwrap();
368
- }
369
-
370
- #[cfg(test)]
371
- pub(crate) fn outstanding_wfts(&self, tq: &str) -> usize {
372
- self.worker(tq).unwrap().outstanding_workflow_tasks()
373
- }
374
- #[cfg(test)]
375
- pub(crate) fn available_wft_permits(&self, tq: &str) -> usize {
376
- self.worker(tq).unwrap().available_wft_permits()
377
- }
378
-
379
- fn get_sticky_q_name_for_worker(&self, config: &WorkerConfig) -> Option<String> {
380
- if config.max_cached_workflows > 0 {
381
- Some(format!(
382
- "{}-{}-{}",
383
- &self.init_options.gateway_opts.identity, &config.task_queue, *PROCCESS_UNIQ_ID
384
- ))
385
- } else {
386
- None
387
- }
388
- }
389
-
390
- fn worker(&self, tq: &str) -> Result<impl Deref<Target = Worker>, WorkerLookupErr> {
391
- let worker = self.workers.get(tq);
392
- if worker.is_err() && self.shutdown_requested.load(Ordering::Relaxed) {
393
- return Err(WorkerLookupErr::Shutdown(tq.to_owned()));
394
- }
395
- worker
396
- }
397
- }
398
-
399
- #[derive(Debug)]
400
- enum WorkerLookupErr {
401
- Shutdown(String),
402
- NoWorker(String),
403
- }