@temporalio/core-bridge 1.9.0 → 1.9.2
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 +2 -33
- 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/.github/workflows/per-pr.yml +1 -1
- package/sdk-core/Cargo.toml +1 -0
- package/sdk-core/README.md +1 -1
- package/sdk-core/client/src/lib.rs +40 -11
- package/sdk-core/client/src/workflow_handle/mod.rs +4 -0
- package/sdk-core/core/Cargo.toml +3 -2
- package/sdk-core/core/src/core_tests/activity_tasks.rs +69 -2
- package/sdk-core/core/src/core_tests/local_activities.rs +99 -4
- package/sdk-core/core/src/core_tests/queries.rs +90 -1
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +8 -11
- package/sdk-core/core/src/telemetry/metrics.rs +4 -4
- package/sdk-core/core/src/telemetry/mod.rs +1 -3
- package/sdk-core/core/src/test_help/mod.rs +9 -0
- package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +1 -2
- package/sdk-core/core/src/worker/activities/local_activities.rs +1 -1
- package/sdk-core/core/src/worker/activities.rs +11 -4
- package/sdk-core/core/src/worker/mod.rs +6 -1
- package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +0 -1
- package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +28 -6
- package/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +15 -0
- package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +19 -15
- package/sdk-core/core/src/worker/workflow/mod.rs +89 -59
- package/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +1 -1
- package/sdk-core/core-api/Cargo.toml +2 -2
- package/sdk-core/fsm/rustfsm_procmacro/Cargo.toml +1 -1
- package/sdk-core/sdk/Cargo.toml +2 -2
- package/sdk-core/sdk/src/lib.rs +13 -8
- package/sdk-core/sdk/src/workflow_context.rs +2 -2
- package/sdk-core/sdk/src/workflow_future.rs +1 -1
- package/sdk-core/sdk-core-protos/Cargo.toml +1 -1
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/event_type.proto +4 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/reset.proto +16 -3
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/update.proto +11 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/history/v1/message.proto +10 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +4 -1
- package/sdk-core/sdk-core-protos/protos/testsrv_upstream/Makefile +3 -10
- package/sdk-core/sdk-core-protos/protos/testsrv_upstream/api-linter.yaml +0 -5
- package/sdk-core/sdk-core-protos/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +3 -4
- package/sdk-core/sdk-core-protos/src/history_info.rs +2 -2
- package/sdk-core/sdk-core-protos/src/lib.rs +1 -0
- package/sdk-core/tests/integ_tests/queries_tests.rs +12 -12
- package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +48 -0
- package/src/conversions.rs +19 -17
- package/src/runtime.rs +32 -4
- package/sdk-core/sdk-core-protos/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +0 -141
package/sdk-core/sdk/src/lib.rs
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
//! let worker_config = WorkerConfigBuilder::default()
|
|
26
26
|
//! .namespace("default")
|
|
27
27
|
//! .task_queue("task_queue")
|
|
28
|
+
//! .worker_build_id("rust-sdk")
|
|
28
29
|
//! .build()?;
|
|
29
30
|
//!
|
|
30
31
|
//! let core_worker = init_worker(&runtime, worker_config, client)?;
|
|
@@ -710,7 +711,7 @@ impl<F, Fut, O> From<F> for WorkflowFunction
|
|
|
710
711
|
where
|
|
711
712
|
F: Fn(WfContext) -> Fut + Send + Sync + 'static,
|
|
712
713
|
Fut: Future<Output = Result<WfExitValue<O>, anyhow::Error>> + Send + 'static,
|
|
713
|
-
O: Serialize
|
|
714
|
+
O: Serialize,
|
|
714
715
|
{
|
|
715
716
|
fn from(wf_func: F) -> Self {
|
|
716
717
|
Self::new(wf_func)
|
|
@@ -723,7 +724,7 @@ impl WorkflowFunction {
|
|
|
723
724
|
where
|
|
724
725
|
F: Fn(WfContext) -> Fut + Send + Sync + 'static,
|
|
725
726
|
Fut: Future<Output = Result<WfExitValue<O>, anyhow::Error>> + Send + 'static,
|
|
726
|
-
O: Serialize
|
|
727
|
+
O: Serialize,
|
|
727
728
|
{
|
|
728
729
|
Self {
|
|
729
730
|
wf_func: Box::new(move |ctx: WfContext| {
|
|
@@ -749,7 +750,7 @@ pub type WorkflowResult<T> = Result<WfExitValue<T>, anyhow::Error>;
|
|
|
749
750
|
|
|
750
751
|
/// Workflow functions may return these values when exiting
|
|
751
752
|
#[derive(Debug, derive_more::From)]
|
|
752
|
-
pub enum WfExitValue<T
|
|
753
|
+
pub enum WfExitValue<T> {
|
|
753
754
|
/// Continue the workflow as a new execution
|
|
754
755
|
#[from(ignore)]
|
|
755
756
|
ContinueAsNew(Box<ContinueAsNewWorkflowExecution>),
|
|
@@ -763,7 +764,7 @@ pub enum WfExitValue<T: Debug> {
|
|
|
763
764
|
Normal(T),
|
|
764
765
|
}
|
|
765
766
|
|
|
766
|
-
impl<T
|
|
767
|
+
impl<T> WfExitValue<T> {
|
|
767
768
|
/// Construct a [WfExitValue::ContinueAsNew] variant (handles boxing)
|
|
768
769
|
pub fn continue_as_new(can: ContinueAsNewWorkflowExecution) -> Self {
|
|
769
770
|
Self::ContinueAsNew(Box::new(can))
|
|
@@ -771,15 +772,19 @@ impl<T: Debug> WfExitValue<T> {
|
|
|
771
772
|
}
|
|
772
773
|
|
|
773
774
|
/// Activity functions may return these values when exiting
|
|
774
|
-
|
|
775
|
-
pub enum ActExitValue<T: Debug> {
|
|
775
|
+
pub enum ActExitValue<T> {
|
|
776
776
|
/// Completion requires an asynchronous callback
|
|
777
|
-
#[from(ignore)]
|
|
778
777
|
WillCompleteAsync,
|
|
779
778
|
/// Finish with a result
|
|
780
779
|
Normal(T),
|
|
781
780
|
}
|
|
782
781
|
|
|
782
|
+
impl<T: AsJsonPayloadExt> From<T> for ActExitValue<T> {
|
|
783
|
+
fn from(t: T) -> Self {
|
|
784
|
+
Self::Normal(t)
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
783
788
|
type BoxActFn = Arc<
|
|
784
789
|
dyn Fn(ActContext, Payload) -> BoxFuture<'static, Result<ActExitValue<Payload>, anyhow::Error>>
|
|
785
790
|
+ Send
|
|
@@ -834,7 +839,7 @@ where
|
|
|
834
839
|
A: FromJsonPayloadExt + Send,
|
|
835
840
|
Rf: Future<Output = Result<R, anyhow::Error>> + Send + 'static,
|
|
836
841
|
R: Into<ActExitValue<O>>,
|
|
837
|
-
O: AsJsonPayloadExt
|
|
842
|
+
O: AsJsonPayloadExt,
|
|
838
843
|
{
|
|
839
844
|
fn into_activity_fn(self) -> BoxActFn {
|
|
840
845
|
let wrapper = move |ctx: ActContext, input: Payload| {
|
|
@@ -11,7 +11,7 @@ use crate::{
|
|
|
11
11
|
IntoUpdateValidatorFunc, RustWfCmd, SignalExternalWfResult, TimerResult, UnblockEvent,
|
|
12
12
|
Unblockable, UpdateFunctions,
|
|
13
13
|
};
|
|
14
|
-
use
|
|
14
|
+
use crossbeam_channel::{Receiver, Sender};
|
|
15
15
|
use futures::{task::Context, FutureExt, Stream, StreamExt};
|
|
16
16
|
use parking_lot::RwLock;
|
|
17
17
|
use std::{
|
|
@@ -117,7 +117,7 @@ impl WfContext {
|
|
|
117
117
|
am_cancelled: watch::Receiver<bool>,
|
|
118
118
|
) -> (Self, Receiver<RustWfCmd>) {
|
|
119
119
|
// We need to use a normal std channel since our receiving side is non-async
|
|
120
|
-
let (chan, rx) =
|
|
120
|
+
let (chan, rx) = crossbeam_channel::unbounded();
|
|
121
121
|
(
|
|
122
122
|
Self {
|
|
123
123
|
namespace,
|
|
@@ -4,7 +4,7 @@ use crate::{
|
|
|
4
4
|
WorkflowResult,
|
|
5
5
|
};
|
|
6
6
|
use anyhow::{anyhow, bail, Context as AnyhowContext, Error};
|
|
7
|
-
use
|
|
7
|
+
use crossbeam_channel::Receiver;
|
|
8
8
|
use futures::{future::BoxFuture, FutureExt};
|
|
9
9
|
use std::{
|
|
10
10
|
collections::{hash_map::Entry, HashMap},
|
|
@@ -167,4 +167,8 @@ enum EventType {
|
|
|
167
167
|
EVENT_TYPE_ACTIVITY_PROPERTIES_MODIFIED_EXTERNALLY = 45;
|
|
168
168
|
// Workflow properties modified by user workflow code
|
|
169
169
|
EVENT_TYPE_WORKFLOW_PROPERTIES_MODIFIED = 46;
|
|
170
|
+
// An update was requested. Note that not all update requests result in this
|
|
171
|
+
// event. See UpdateRequestedEventOrigin for situations in which this event
|
|
172
|
+
// is created.
|
|
173
|
+
EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_REQUESTED = 47;
|
|
170
174
|
}
|
|
@@ -31,13 +31,26 @@ option java_outer_classname = "ResetProto";
|
|
|
31
31
|
option ruby_package = "Temporalio::Api::Enums::V1";
|
|
32
32
|
option csharp_namespace = "Temporalio.Api.Enums.V1";
|
|
33
33
|
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
// Event types to exclude when reapplying events.
|
|
35
|
+
enum ResetReapplyExcludeType {
|
|
36
|
+
RESET_REAPPLY_EXCLUDE_TYPE_UNSPECIFIED = 0;
|
|
37
|
+
// Exclude signals when reapplying events.
|
|
38
|
+
RESET_REAPPLY_EXCLUDE_TYPE_SIGNAL = 1;
|
|
39
|
+
// Exclude updates when reapplying events.
|
|
40
|
+
RESET_REAPPLY_EXCLUDE_TYPE_UPDATE = 2;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Event types to include when reapplying events. Deprecated: applications
|
|
44
|
+
// should use ResetReapplyExcludeType to specify exclusions from this set, and
|
|
45
|
+
// new event types should be added to ResetReapplyExcludeType instead of here.
|
|
37
46
|
enum ResetReapplyType {
|
|
38
47
|
RESET_REAPPLY_TYPE_UNSPECIFIED = 0;
|
|
48
|
+
// Signals are reapplied when workflow is reset.
|
|
39
49
|
RESET_REAPPLY_TYPE_SIGNAL = 1;
|
|
50
|
+
// No events are reapplied when workflow is reset.
|
|
40
51
|
RESET_REAPPLY_TYPE_NONE = 2;
|
|
52
|
+
// All eligible events are reapplied when workflow is reset.
|
|
53
|
+
RESET_REAPPLY_TYPE_ALL_ELIGIBLE = 3;
|
|
41
54
|
}
|
|
42
55
|
|
|
43
56
|
// Reset type options. Deprecated, see temporal.api.common.v1.ResetOptions.
|
|
@@ -54,3 +54,14 @@ enum UpdateWorkflowExecutionLifecycleStage {
|
|
|
54
54
|
// on a worker and has either been rejected or returned a value or an error.
|
|
55
55
|
UPDATE_WORKFLOW_EXECUTION_LIFECYCLE_STAGE_COMPLETED = 3;
|
|
56
56
|
}
|
|
57
|
+
|
|
58
|
+
// UpdateRequestedEventOrigin records why an
|
|
59
|
+
// WorkflowExecutionUpdateRequestedEvent was written to history. Note that not
|
|
60
|
+
// all update requests result in a WorkflowExecutionUpdateRequestedEvent.
|
|
61
|
+
enum UpdateRequestedEventOrigin {
|
|
62
|
+
UPDATE_REQUESTED_EVENT_ORIGIN_UNSPECIFIED = 0;
|
|
63
|
+
// The UpdateRequested event was created when reapplying events during reset
|
|
64
|
+
// or replication. I.e. an accepted update on one branch of workflow history
|
|
65
|
+
// was converted into a requested update on a different branch.
|
|
66
|
+
UPDATE_REQUESTED_EVENT_ORIGIN_REAPPLY = 1;
|
|
67
|
+
}
|
|
@@ -36,6 +36,7 @@ import "google/protobuf/timestamp.proto";
|
|
|
36
36
|
|
|
37
37
|
import "temporal/api/enums/v1/event_type.proto";
|
|
38
38
|
import "temporal/api/enums/v1/failed_cause.proto";
|
|
39
|
+
import "temporal/api/enums/v1/update.proto";
|
|
39
40
|
import "temporal/api/enums/v1/workflow.proto";
|
|
40
41
|
import "temporal/api/common/v1/message.proto";
|
|
41
42
|
import "temporal/api/failure/v1/message.proto";
|
|
@@ -434,6 +435,8 @@ message WorkflowExecutionSignaledEventAttributes {
|
|
|
434
435
|
temporal.api.common.v1.Header header = 4;
|
|
435
436
|
// Indicates the signal did not generate a new workflow task when received.
|
|
436
437
|
bool skip_generate_workflow_task = 5;
|
|
438
|
+
// When signal origin is a workflow execution, this field is set.
|
|
439
|
+
temporal.api.common.v1.WorkflowExecution external_workflow_execution = 6;
|
|
437
440
|
}
|
|
438
441
|
|
|
439
442
|
message WorkflowExecutionTerminatedEventAttributes {
|
|
@@ -747,6 +750,12 @@ message WorkflowExecutionUpdateRejectedEventAttributes {
|
|
|
747
750
|
temporal.api.failure.v1.Failure failure = 5;
|
|
748
751
|
}
|
|
749
752
|
|
|
753
|
+
message WorkflowExecutionUpdateRequestedEventAttributes {
|
|
754
|
+
// The update request associated with this event.
|
|
755
|
+
temporal.api.update.v1.Request request = 1;
|
|
756
|
+
// A record of why this event was written to history.
|
|
757
|
+
temporal.api.enums.v1.UpdateRequestedEventOrigin origin = 2;
|
|
758
|
+
}
|
|
750
759
|
|
|
751
760
|
// History events are the method by which Temporal SDKs advance (or recreate) workflow state.
|
|
752
761
|
// See the `EventType` enum for more info about what each event is for.
|
|
@@ -812,6 +821,7 @@ message HistoryEvent {
|
|
|
812
821
|
WorkflowPropertiesModifiedExternallyEventAttributes workflow_properties_modified_externally_event_attributes = 49;
|
|
813
822
|
ActivityPropertiesModifiedExternallyEventAttributes activity_properties_modified_externally_event_attributes = 50;
|
|
814
823
|
WorkflowPropertiesModifiedEventAttributes workflow_properties_modified_event_attributes = 51;
|
|
824
|
+
WorkflowExecutionUpdateRequestedEventAttributes workflow_execution_update_requested_event_attributes = 52;
|
|
815
825
|
}
|
|
816
826
|
}
|
|
817
827
|
|
|
@@ -669,8 +669,11 @@ message ResetWorkflowExecutionRequest {
|
|
|
669
669
|
int64 workflow_task_finish_event_id = 4;
|
|
670
670
|
// Used to de-dupe reset requests
|
|
671
671
|
string request_id = 5;
|
|
672
|
-
//
|
|
672
|
+
// Event types to be reapplied (deprecated)
|
|
673
|
+
// Default: RESET_REAPPLY_TYPE_ALL_ELIGIBLE
|
|
673
674
|
temporal.api.enums.v1.ResetReapplyType reset_reapply_type = 6;
|
|
675
|
+
// Event types not to be reapplied
|
|
676
|
+
repeated temporal.api.enums.v1.ResetReapplyExcludeType reset_reapply_exclude_types = 7;
|
|
674
677
|
}
|
|
675
678
|
|
|
676
679
|
message ResetWorkflowExecutionResponse {
|
|
@@ -23,33 +23,26 @@ PROTO_ROOT := .
|
|
|
23
23
|
PROTO_FILES = $(shell find $(PROTO_ROOT) -name "*.proto")
|
|
24
24
|
PROTO_DIRS = $(sort $(dir $(PROTO_FILES)))
|
|
25
25
|
PROTO_OUT := .gen
|
|
26
|
-
PROTO_IMPORTS := -I=$(PROTO_ROOT)
|
|
26
|
+
PROTO_IMPORTS := -I=$(PROTO_ROOT)
|
|
27
27
|
|
|
28
28
|
$(PROTO_OUT):
|
|
29
29
|
mkdir $(PROTO_OUT)
|
|
30
30
|
|
|
31
31
|
##### Compile proto files for go #####
|
|
32
|
-
grpc: buf-lint api-linter buf-breaking
|
|
32
|
+
grpc: buf-lint api-linter buf-breaking fix-path
|
|
33
33
|
|
|
34
34
|
go-grpc: clean $(PROTO_OUT)
|
|
35
35
|
printf $(COLOR) "Compile for go-gRPC..."
|
|
36
36
|
$(foreach PROTO_DIR,$(PROTO_DIRS),protoc $(PROTO_IMPORTS) --go_out=plugins=grpc,paths=source_relative:$(PROTO_OUT) $(PROTO_DIR)*.proto;)
|
|
37
37
|
|
|
38
|
-
gogo-grpc: clean $(PROTO_OUT)
|
|
39
|
-
printf $(COLOR) "Compile for gogo-gRPC..."
|
|
40
|
-
$(foreach PROTO_DIR,$(PROTO_DIRS),protoc $(PROTO_IMPORTS) --gogoslick_out=Mgoogle/protobuf/wrappers.proto=github.com/gogo/protobuf/types,Mgoogle/protobuf/duration.proto=github.com/gogo/protobuf/types,Mgoogle/protobuf/descriptor.proto=github.com/golang/protobuf/protoc-gen-go/descriptor,Mgoogle/protobuf/timestamp.proto=github.com/gogo/protobuf/types,plugins=grpc,paths=source_relative:$(PROTO_OUT) $(PROTO_DIR)*.proto;)
|
|
41
|
-
|
|
42
38
|
fix-path:
|
|
43
39
|
mv -f $(PROTO_OUT)/temporal/api/* $(PROTO_OUT) && rm -rf $(PROTO_OUT)/temporal
|
|
44
40
|
|
|
45
41
|
##### Plugins & tools #####
|
|
46
|
-
grpc-install:
|
|
42
|
+
grpc-install:
|
|
47
43
|
printf $(COLOR) "Install/update gRPC plugins..."
|
|
48
44
|
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1.0
|
|
49
45
|
|
|
50
|
-
gogo-protobuf-install: go-protobuf-install
|
|
51
|
-
GO111MODULE=off go get github.com/temporalio/gogo-protobuf/protoc-gen-gogoslick
|
|
52
|
-
|
|
53
46
|
go-protobuf-install:
|
|
54
47
|
go install github.com/golang/protobuf/protoc-gen-go@v1.4.3
|
|
55
48
|
|
|
@@ -33,7 +33,6 @@ option csharp_namespace = "Temporalio.Api.TestService.V1";
|
|
|
33
33
|
|
|
34
34
|
import "google/protobuf/duration.proto";
|
|
35
35
|
import "google/protobuf/timestamp.proto";
|
|
36
|
-
import "dependencies/gogoproto/gogo.proto";
|
|
37
36
|
|
|
38
37
|
message LockTimeSkippingRequest {
|
|
39
38
|
}
|
|
@@ -48,16 +47,16 @@ message UnlockTimeSkippingResponse {
|
|
|
48
47
|
}
|
|
49
48
|
|
|
50
49
|
message SleepUntilRequest {
|
|
51
|
-
google.protobuf.Timestamp timestamp = 1
|
|
50
|
+
google.protobuf.Timestamp timestamp = 1;
|
|
52
51
|
}
|
|
53
52
|
|
|
54
53
|
message SleepRequest {
|
|
55
|
-
google.protobuf.Duration duration = 1
|
|
54
|
+
google.protobuf.Duration duration = 1;
|
|
56
55
|
}
|
|
57
56
|
|
|
58
57
|
message SleepResponse {
|
|
59
58
|
}
|
|
60
59
|
|
|
61
60
|
message GetCurrentTimeResponse {
|
|
62
|
-
google.protobuf.Timestamp time = 1
|
|
61
|
+
google.protobuf.Timestamp time = 1;
|
|
63
62
|
}
|
|
@@ -6,7 +6,7 @@ use crate::temporal::api::{
|
|
|
6
6
|
workflowservice::v1::{GetWorkflowExecutionHistoryResponse, PollWorkflowTaskQueueResponse},
|
|
7
7
|
};
|
|
8
8
|
use anyhow::{anyhow, bail};
|
|
9
|
-
use rand::
|
|
9
|
+
use rand::random;
|
|
10
10
|
|
|
11
11
|
/// Contains information about a validated history. Used for replay and other testing.
|
|
12
12
|
#[derive(Clone, Debug, PartialEq)]
|
|
@@ -154,7 +154,7 @@ impl HistoryInfo {
|
|
|
154
154
|
/// randomly generated task token. Caller should attach a meaningful `workflow_execution` if
|
|
155
155
|
/// needed.
|
|
156
156
|
pub fn as_poll_wft_response(&self) -> PollWorkflowTaskQueueResponse {
|
|
157
|
-
let task_token: [u8; 16] =
|
|
157
|
+
let task_token: [u8; 16] = random();
|
|
158
158
|
PollWorkflowTaskQueueResponse {
|
|
159
159
|
history: Some(History {
|
|
160
160
|
events: self.events.clone(),
|
|
@@ -1981,6 +1981,7 @@ pub mod temporal {
|
|
|
1981
1981
|
Attributes::WorkflowExecutionUpdateRejectedEventAttributes(_) => {EventType::WorkflowExecutionUpdateRejected}
|
|
1982
1982
|
Attributes::WorkflowExecutionUpdateAcceptedEventAttributes(_) => {EventType::WorkflowExecutionUpdateAccepted}
|
|
1983
1983
|
Attributes::WorkflowExecutionUpdateCompletedEventAttributes(_) => {EventType::WorkflowExecutionUpdateCompleted}
|
|
1984
|
+
Attributes::WorkflowExecutionUpdateRequestedEventAttributes(_) => {EventType::WorkflowExecutionUpdateRequested}
|
|
1984
1985
|
Attributes::WorkflowPropertiesModifiedExternallyEventAttributes(_) => {EventType::WorkflowPropertiesModifiedExternally}
|
|
1985
1986
|
Attributes::ActivityPropertiesModifiedExternallyEventAttributes(_) => {EventType::ActivityPropertiesModifiedExternally}
|
|
1986
1987
|
Attributes::WorkflowPropertiesModifiedEventAttributes(_) => {EventType::WorkflowPropertiesModified}
|
|
@@ -384,8 +384,8 @@ async fn multiple_concurrent_queries_no_new_history() {
|
|
|
384
384
|
}
|
|
385
385
|
|
|
386
386
|
#[tokio::test]
|
|
387
|
-
async fn
|
|
388
|
-
let mut starter = init_core_and_create_wf("
|
|
387
|
+
async fn queries_handled_before_next_wft() {
|
|
388
|
+
let mut starter = init_core_and_create_wf("queries_handled_before_next_wft").await;
|
|
389
389
|
let core = starter.get_worker().await;
|
|
390
390
|
let workflow_id = starter.get_task_queue().to_string();
|
|
391
391
|
let task = core.poll_workflow_activation().await.unwrap();
|
|
@@ -447,16 +447,7 @@ async fn query_superseded_by_newer_wft_is_discarded() {
|
|
|
447
447
|
))
|
|
448
448
|
.await
|
|
449
449
|
.unwrap();
|
|
450
|
-
// We
|
|
451
|
-
let task = core.poll_workflow_activation().await.unwrap();
|
|
452
|
-
assert_matches!(
|
|
453
|
-
task.jobs.as_slice(),
|
|
454
|
-
[WorkflowActivationJob {
|
|
455
|
-
variant: Some(workflow_activation_job::Variant::SignalWorkflow(_)),
|
|
456
|
-
}]
|
|
457
|
-
);
|
|
458
|
-
core.complete_execution(&task.run_id).await;
|
|
459
|
-
// Query will get retried by server since we fail the task w/ the stale query
|
|
450
|
+
// We now get the second query
|
|
460
451
|
let task = core.poll_workflow_activation().await.unwrap();
|
|
461
452
|
let query = assert_matches!(
|
|
462
453
|
task.jobs.as_slice(),
|
|
@@ -479,6 +470,15 @@ async fn query_superseded_by_newer_wft_is_discarded() {
|
|
|
479
470
|
))
|
|
480
471
|
.await
|
|
481
472
|
.unwrap();
|
|
473
|
+
// Then the signal afterward
|
|
474
|
+
let task = core.poll_workflow_activation().await.unwrap();
|
|
475
|
+
assert_matches!(
|
|
476
|
+
task.jobs.as_slice(),
|
|
477
|
+
[WorkflowActivationJob {
|
|
478
|
+
variant: Some(workflow_activation_job::Variant::SignalWorkflow(_)),
|
|
479
|
+
}]
|
|
480
|
+
);
|
|
481
|
+
core.complete_execution(&task.run_id).await;
|
|
482
482
|
};
|
|
483
483
|
join!(join_all(query_futs), complete_fut);
|
|
484
484
|
drain_pollers_and_shutdown(&core).await;
|
|
@@ -5,6 +5,9 @@ use std::{
|
|
|
5
5
|
},
|
|
6
6
|
time::Duration,
|
|
7
7
|
};
|
|
8
|
+
use temporal_client::WorkflowClientTrait;
|
|
9
|
+
use tokio::{join, sync::Notify};
|
|
10
|
+
use tokio_stream::StreamExt;
|
|
8
11
|
|
|
9
12
|
use temporal_sdk::{WfContext, WorkflowResult};
|
|
10
13
|
use temporal_sdk_core_test_utils::CoreWfStarter;
|
|
@@ -151,3 +154,48 @@ async fn can_remove_deprecated_patch_near_other_patch() {
|
|
|
151
154
|
starter.start_with_worker(wf_name, &mut worker).await;
|
|
152
155
|
worker.run_until_done().await.unwrap();
|
|
153
156
|
}
|
|
157
|
+
|
|
158
|
+
#[tokio::test]
|
|
159
|
+
async fn deprecated_patch_removal() {
|
|
160
|
+
let wf_name = "deprecated_patch_removal";
|
|
161
|
+
let mut starter = CoreWfStarter::new(wf_name);
|
|
162
|
+
starter.no_remote_activities();
|
|
163
|
+
let mut worker = starter.worker().await;
|
|
164
|
+
let client = starter.get_client().await;
|
|
165
|
+
let wf_id = starter.get_task_queue().to_string();
|
|
166
|
+
let did_die = Arc::new(AtomicBool::new(false));
|
|
167
|
+
let send_sig = Arc::new(Notify::new());
|
|
168
|
+
let send_sig_c = send_sig.clone();
|
|
169
|
+
worker.register_wf(wf_name, move |ctx: WfContext| {
|
|
170
|
+
let did_die = did_die.clone();
|
|
171
|
+
let send_sig_c = send_sig_c.clone();
|
|
172
|
+
async move {
|
|
173
|
+
if !did_die.load(Ordering::Acquire) {
|
|
174
|
+
assert!(ctx.deprecate_patch("getting-deprecated"));
|
|
175
|
+
}
|
|
176
|
+
send_sig_c.notify_one();
|
|
177
|
+
ctx.make_signal_channel("sig").next().await;
|
|
178
|
+
|
|
179
|
+
ctx.timer(Duration::from_millis(1)).await;
|
|
180
|
+
|
|
181
|
+
if !did_die.load(Ordering::Acquire) {
|
|
182
|
+
did_die.store(true, Ordering::Release);
|
|
183
|
+
ctx.force_task_fail(anyhow::anyhow!("i'm ded"));
|
|
184
|
+
}
|
|
185
|
+
Ok(().into())
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
starter.start_with_worker(wf_name, &mut worker).await;
|
|
190
|
+
let sig_fut = async {
|
|
191
|
+
send_sig.notified().await;
|
|
192
|
+
client
|
|
193
|
+
.signal_workflow_execution(wf_id, "".to_string(), "sig".to_string(), None, None)
|
|
194
|
+
.await
|
|
195
|
+
.unwrap()
|
|
196
|
+
};
|
|
197
|
+
let run_fut = async {
|
|
198
|
+
worker.run_until_done().await.unwrap();
|
|
199
|
+
};
|
|
200
|
+
join!(sig_fut, run_fut);
|
|
201
|
+
}
|
package/src/conversions.rs
CHANGED
|
@@ -44,10 +44,9 @@ impl ArrayHandleConversionsExt for Handle<'_, JsArray> {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
);
|
|
47
|
+
type BoxedMeterMaker = Box<dyn FnOnce() -> Result<Arc<dyn CoreMeter>, String> + Send + Sync>;
|
|
48
|
+
|
|
49
|
+
pub(crate) type TelemOptsRes = (TelemetryOptions, Option<BoxedMeterMaker>);
|
|
51
50
|
|
|
52
51
|
pub trait ObjectHandleConversionsExt {
|
|
53
52
|
fn set_default(&self, cx: &mut FunctionContext, key: &str, value: &str) -> NeonResult<()>;
|
|
@@ -220,20 +219,24 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
|
|
|
220
219
|
.unwrap_err()
|
|
221
220
|
})?;
|
|
222
221
|
|
|
223
|
-
meter_maker =
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
222
|
+
meter_maker =
|
|
223
|
+
Some(
|
|
224
|
+
Box::new(move || match start_prometheus_metric_exporter(options) {
|
|
225
|
+
Ok(prom_info) => Ok(prom_info.meter as Arc<dyn CoreMeter>),
|
|
226
|
+
Err(e) => Err(format!("Failed to start prometheus exporter: {}", e)),
|
|
227
|
+
}) as BoxedMeterMaker,
|
|
228
|
+
);
|
|
229
229
|
} else if let Some(ref otel) = js_optional_getter!(cx, metrics, "otel", JsObject) {
|
|
230
230
|
let mut options = OtelCollectorOptionsBuilder::default();
|
|
231
231
|
|
|
232
232
|
let url = js_value_getter!(cx, otel, "url", JsString);
|
|
233
233
|
match Url::parse(&url) {
|
|
234
234
|
Ok(url) => options.url(url),
|
|
235
|
-
Err(
|
|
236
|
-
return cx.throw_type_error(
|
|
235
|
+
Err(e) => {
|
|
236
|
+
return cx.throw_type_error(format!(
|
|
237
|
+
"Invalid telemetryOptions.metrics.otel.url: {}",
|
|
238
|
+
e
|
|
239
|
+
))?;
|
|
237
240
|
}
|
|
238
241
|
};
|
|
239
242
|
|
|
@@ -269,11 +272,10 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
|
|
|
269
272
|
.unwrap_err()
|
|
270
273
|
})?;
|
|
271
274
|
|
|
272
|
-
meter_maker = Some(Box::new(move || {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
}));
|
|
275
|
+
meter_maker = Some(Box::new(move || match build_otlp_metric_exporter(options) {
|
|
276
|
+
Ok(otlp_exporter) => Ok(Arc::new(otlp_exporter) as Arc<dyn CoreMeter>),
|
|
277
|
+
Err(e) => Err(format!("Failed to start otlp exporter: {}", e)),
|
|
278
|
+
}) as BoxedMeterMaker);
|
|
277
279
|
} else {
|
|
278
280
|
cx.throw_type_error(
|
|
279
281
|
"Invalid telemetryOptions.metrics, missing `prometheus` or `otel` option",
|
package/src/runtime.rs
CHANGED
|
@@ -19,6 +19,7 @@ use temporal_sdk_core::{
|
|
|
19
19
|
replay::{HistoryForReplay, ReplayWorkerInput},
|
|
20
20
|
ClientOptions, RetryClient, WorkerConfig,
|
|
21
21
|
};
|
|
22
|
+
use tokio::sync::oneshot;
|
|
22
23
|
use tokio::sync::{
|
|
23
24
|
mpsc::{channel, unbounded_channel, Sender, UnboundedReceiver, UnboundedSender},
|
|
24
25
|
Mutex,
|
|
@@ -119,6 +120,7 @@ pub fn start_bridge_loop(
|
|
|
119
120
|
telemetry_options: TelemOptsRes,
|
|
120
121
|
channel: Arc<Channel>,
|
|
121
122
|
receiver: &mut UnboundedReceiver<RuntimeRequest>,
|
|
123
|
+
result_sender: oneshot::Sender<Result<(), String>>,
|
|
122
124
|
) {
|
|
123
125
|
let mut tokio_builder = tokio::runtime::Builder::new_multi_thread();
|
|
124
126
|
tokio_builder.enable_all().thread_name("core");
|
|
@@ -129,10 +131,24 @@ pub fn start_bridge_loop(
|
|
|
129
131
|
|
|
130
132
|
core_runtime.tokio_handle().block_on(async {
|
|
131
133
|
if let Some(meter_maker) = meter_maker {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
134
|
+
match meter_maker() {
|
|
135
|
+
Ok(meter) => {
|
|
136
|
+
core_runtime.telemetry_mut().attach_late_init_metrics(meter);
|
|
137
|
+
}
|
|
138
|
+
Err(err) => {
|
|
139
|
+
result_sender
|
|
140
|
+
.send(Err(format!("Failed to create meter: {}", err)))
|
|
141
|
+
.unwrap_or_else(|_| {
|
|
142
|
+
panic!("Failed to report runtime start error: {}", err)
|
|
143
|
+
});
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
135
147
|
}
|
|
148
|
+
result_sender
|
|
149
|
+
.send(Ok(()))
|
|
150
|
+
.expect("Failed to report runtime start success");
|
|
151
|
+
|
|
136
152
|
loop {
|
|
137
153
|
let request_option = receiver.recv().await;
|
|
138
154
|
let request = match request_option {
|
|
@@ -386,7 +402,19 @@ pub fn runtime_new(mut cx: FunctionContext) -> JsResult<BoxedRuntime> {
|
|
|
386
402
|
let channel = Arc::new(cx.channel());
|
|
387
403
|
let (sender, mut receiver) = unbounded_channel::<RuntimeRequest>();
|
|
388
404
|
|
|
389
|
-
|
|
405
|
+
// FIXME: This is a temporary fix to get sync notifications of errors while initializing the runtime.
|
|
406
|
+
// The proper fix would be to avoid spawning a new thread here, so that start_bridge_loop
|
|
407
|
+
// can simply yeild back a Result. But early attempts to do just that caused panics
|
|
408
|
+
// on runtime shutdown, so let's use this hack until we can dig deeper.
|
|
409
|
+
let (result_sender, result_receiver) = oneshot::channel::<Result<(), String>>();
|
|
410
|
+
|
|
411
|
+
std::thread::spawn(move || {
|
|
412
|
+
start_bridge_loop(telemetry_options, channel, &mut receiver, result_sender)
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
if let Ok(Err(e)) = result_receiver.blocking_recv() {
|
|
416
|
+
Err(cx.throw_error::<_, String>(e).unwrap_err())?;
|
|
417
|
+
}
|
|
390
418
|
|
|
391
419
|
Ok(cx.boxed(Arc::new(RuntimeHandle { sender })))
|
|
392
420
|
}
|