Skip to content
Closed
46 changes: 46 additions & 0 deletions crates/client/src/grpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1878,6 +1878,52 @@ proxier! {
r.extensions_mut().insert(labels);
}
);
(
attach_stream_callback,
AttachStreamCallbackRequest,
AttachStreamCallbackResponse,
|r| {
let labels = namespaced_request!(r);
r.extensions_mut().insert(labels);
}
);
(
detach_stream_callback,
DetachStreamCallbackRequest,
DetachStreamCallbackResponse,
|r| {
let labels = namespaced_request!(r);
r.extensions_mut().insert(labels);
}
);
(
notify_stream,
NotifyStreamRequest,
NotifyStreamResponse,
|r| {
let labels = namespaced_request!(r);
r.extensions_mut().insert(labels);
}
);
(
describe_stream_notifier,
DescribeStreamNotifierRequest,
DescribeStreamNotifierResponse,
|r| {
let labels = namespaced_request!(r);
r.extensions_mut().insert(labels);
}
);
(
poll_stream_notifier,
PollStreamNotifierRequest,
PollStreamNotifierResponse,
|r| {
let labels = namespaced_request!(r);
r.extensions_mut().insert(labels);
r.extensions_mut().insert(IsUserLongPoll);
}
);
}

proxier! {
Expand Down
2 changes: 2 additions & 0 deletions crates/common/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,8 @@ const BLOB_FIELDS: &[&str] = &[
"temporal.api.protocol.v1.Message.body", // whole Any body; see EXTRA_WHOLE_MESSAGE_LEAVES
"temporal.api.query.v1.WorkflowQuery.query_args",
"temporal.api.workflow.v1.NewWorkflowExecutionInfo.input",
"temporal.api.workflowservice.v1.NotifyStreamRequest.close_failure",
"temporal.api.workflowservice.v1.NotifyStreamRequest.close_result",
"temporal.api.workflowservice.v1.RecordActivityTaskHeartbeatByIdRequest.details",
"temporal.api.workflowservice.v1.RecordActivityTaskHeartbeatRequest.details",
"temporal.api.workflowservice.v1.RespondActivityTaskCanceledByIdRequest.details",
Expand Down
1 change: 1 addition & 0 deletions crates/protos/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const SERDE_DERIVE_PREFIXES: &[&str] = &[
".temporal.api.rules",
".temporal.api.schedule",
".temporal.api.sdk",
".temporal.api.stream",
".temporal.api.taskqueue",
".temporal.api.testservice",
".temporal.api.update",
Expand Down
54 changes: 54 additions & 0 deletions crates/protos/protos/api_upstream/openapi/openapiv2.json
Original file line number Diff line number Diff line change
Expand Up @@ -20049,6 +20049,52 @@
}
}
},
"v1StreamWake": {
"type": "object",
"properties": {
"subscriptionId": {
"type": "string",
"description": "The `subscription_id` of the `WorkflowWakeTarget` this wake is for."
},
"position": {
"type": "string",
"description": "The position that came with `counter`, in the producer's terms."
},
"counter": {
"type": "string",
"format": "int64",
"description": "The highest counter the notifier has seen. A reader that armed its read at a counter wakes\nwhen this value is above it. Zero when the notifier has seen no notification."
},
"metadata": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "The metadata that came with `counter`."
},
"recheck": {
"type": "boolean",
"description": "Set when the notifier asks the reader to read again with no new counter. The notifier sends\nit after a quiet period, so a reader also finds records whose notification was lost."
},
"closed": {
"type": "boolean",
"description": "Set when the stream closed. The reader reads to the end of the stream, then sees the\noutcome."
},
"closeResult": {
"$ref": "#/definitions/v1Payload",
"description": "The result the stream closed with, when `closed` is set. Optional."
},
"closeFailure": {
"$ref": "#/definitions/v1Failure",
"description": "The failure the stream closed with, when `closed` is set and it closed with one."
},
"resubscribe": {
"type": "boolean",
"description": "Set when the notifier dropped this reader, for example at the notifier's idle timeout. The\nreader attaches again under a new request ID."
}
},
"description": "A wake for one reader of a stream in a Workflow. The server records it on the next Workflow\nTask scheduled event, so it adds no event of its own.\n\nA wake says that the stream moved, not what it holds. The reader reads the stream itself. Wakes\nof one reader are folded, so the reader sees the latest one, with the highest `counter`."
},
"v1StructuredCalendarSpec": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -22620,6 +22666,14 @@
"type": "integer",
"format": "int32",
"title": "Starting at 1, how many attempts there have been to complete this task"
},
"streamWakes": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/v1StreamWake"
},
"description": "Wakes for the Workflow's stream readers that arrived since the previous Workflow Task was\nscheduled, at most one entry per reader: the latest one. Recorded here rather than as\nevents of their own, so a burst of wakes costs one task and replay sees the same wakes the\nWorkflow saw. Bounded by `history.maximumStreamWakeSources` readers per execution, and it\ncounts toward the History size limit."
}
}
},
Expand Down
58 changes: 58 additions & 0 deletions crates/protos/protos/api_upstream/openapi/openapiv3.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17680,6 +17680,54 @@ components:
type:
type: string
description: The type of the driver, required.
StreamWake:
type: object
properties:
subscriptionId:
type: string
description: The `subscription_id` of the `WorkflowWakeTarget` this wake is for.
position:
type: string
description: The position that came with `counter`, in the producer's terms.
counter:
type: string
description: |-
The highest counter the notifier has seen. A reader that armed its read at a counter wakes
when this value is above it. Zero when the notifier has seen no notification.
metadata:
type: object
additionalProperties:
type: string
description: The metadata that came with `counter`.
recheck:
type: boolean
description: |-
Set when the notifier asks the reader to read again with no new counter. The notifier sends
it after a quiet period, so a reader also finds records whose notification was lost.
closed:
type: boolean
description: |-
Set when the stream closed. The reader reads to the end of the stream, then sees the
outcome.
closeResult:
allOf:
- $ref: '#/components/schemas/Payload'
description: The result the stream closed with, when `closed` is set. Optional.
closeFailure:
allOf:
- $ref: '#/components/schemas/Failure'
description: The failure the stream closed with, when `closed` is set and it closed with one.
resubscribe:
type: boolean
description: |-
Set when the notifier dropped this reader, for example at the notifier's idle timeout. The
reader attaches again under a new request ID.
description: |-
A wake for one reader of a stream in a Workflow. The server records it on the next Workflow
Task scheduled event, so it adds no event of its own.

A wake says that the stream moved, not what it holds. The reader reads the stream itself. Wakes
of one reader are folded, so the reader sees the latest one, with the highest `counter`.
StructuredCalendarSpec:
type: object
properties:
Expand Down Expand Up @@ -21255,6 +21303,16 @@ components:
type: integer
description: Starting at 1, how many attempts there have been to complete this task
format: int32
streamWakes:
type: array
items:
$ref: '#/components/schemas/StreamWake'
description: |-
Wakes for the Workflow's stream readers that arrived since the previous Workflow Task was
scheduled, at most one entry per reader: the latest one. Recorded here rather than as
events of their own, so a burst of wakes costs one task and replay sees the same wakes the
Workflow saw. Bounded by `history.maximumStreamWakeSources` readers per execution, and it
counts toward the History size limit.
WorkflowTaskStartedEventAttributes:
type: object
properties:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
syntax = "proto3";

package temporal.api.enums.v1;

option go_package = "go.temporal.io/api/enums/v1;enums";
option java_package = "io.temporal.api.enums.v1";
option java_multiple_files = true;
option java_outer_classname = "StreamProto";
option ruby_package = "Temporalio::Api::Enums::V1";
option csharp_namespace = "Temporalio.Api.Enums.V1";

// What owns a stream. The owner decides when the stream closes.
enum StreamOwnerKind {
STREAM_OWNER_KIND_UNSPECIFIED = 0;
// A Workflow owns the stream. The Workflow closes the stream itself, with a result or a
// failure. If the Workflow's run chain ends with no close and callbacks are attached, the
// stream notifier closes the stream with a failure within `streamnotifier.ownerCheckInterval`.
STREAM_OWNER_KIND_WORKFLOW = 1;
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import "temporal/api/workflow/v1/message.proto";
import "temporal/api/sdk/v1/task_complete_metadata.proto";
import "temporal/api/sdk/v1/user_metadata.proto";
import "temporal/api/sdk/v1/event_group_marker.proto";
import "temporal/api/stream/v1/message.proto";

// Always the first event in workflow history
message WorkflowExecutionStartedEventAttributes {
Expand Down Expand Up @@ -302,6 +303,12 @@ message WorkflowTaskScheduledEventAttributes {
google.protobuf.Duration start_to_close_timeout = 2;
// Starting at 1, how many attempts there have been to complete this task
int32 attempt = 3;
// Wakes for the Workflow's stream readers that arrived since the previous Workflow Task was
// scheduled, at most one entry per reader: the latest one. Recorded here rather than as
// events of their own, so a burst of wakes costs one task and replay sees the same wakes the
// Workflow saw. Bounded by `history.maximumStreamWakeSources` readers per execution, and it
// counts toward the History size limit.
repeated temporal.api.stream.v1.StreamWake stream_wakes = 5;
}

message WorkflowTaskStartedEventAttributes {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
syntax = "proto3";

package temporal.api.stream.v1;

option go_package = "go.temporal.io/api/stream/v1;stream";
option java_package = "io.temporal.api.stream.v1";
option java_multiple_files = true;
option java_outer_classname = "MessageProto";
option ruby_package = "Temporalio::Api::Stream::V1";
option csharp_namespace = "Temporalio.Api.Stream.V1";

import "google/protobuf/timestamp.proto";
import "temporal/api/common/v1/message.proto";
import "temporal/api/enums/v1/common.proto";
import "temporal/api/enums/v1/stream.proto";
import "temporal/api/failure/v1/message.proto";

// Names one stream within a namespace: its owner and its topic.
message StreamReference {
temporal.api.enums.v1.StreamOwnerKind owner_kind = 1;
// The owning Workflow, for `STREAM_OWNER_KIND_WORKFLOW`.
string workflow_id = 2;
// The id of the first run in the owning Workflow's run chain. The stream notifier is keyed by
// it, so one notifier serves the stream across the chain's runs, and a stream that reuses the
// Workflow id in a new chain gets a notifier of its own. An empty run id is refused.
string run_id = 3;
// The stream's topic within its owner.
string topic = 4;
}

// A callback attached to a stream notifier, as DescribeStreamNotifier reports it.
message StreamCallbackInfo {
// The request ID the callback was attached with.
string request_id = 1;
// The callback. Its header keeps its keys, but every value is empty, since the values hold
// the caller's credentials.
temporal.api.common.v1.Callback callback = 2;
// The state of the callback's completion delivery.
temporal.api.enums.v1.CallbackState state = 3;
// The highest counter delivered to the callback as progress.
int64 delivered_counter = 4;
// Whether the receiver refused progress, so the callback only gets the completion.
bool progress_disabled = 5;
// The number of attempts made for the delivery in flight, starting at 1.
int32 attempt = 6;
// The time the last attempt completed.
google.protobuf.Timestamp last_attempt_complete_time = 7;
// The failure of the last attempt, if it failed.
temporal.api.failure.v1.Failure last_attempt_failure = 8;
// The Workflow the callback wakes, when it was attached with a `workflow_target` and not a
// Nexus callback. Then `callback` is unset.
WorkflowWakeTarget workflow_target = 9;
}

// A Workflow that a stream notifier wakes when the stream moves or closes. The Workflow is in the
// namespace of the request that attached it.
message WorkflowWakeTarget {
string workflow_id = 1;
string run_id = 2;
// Names one reader of the stream in that run. The Workflow gets the wakes of each reader as
// their own `StreamWake`. At most 256 bytes.
string subscription_id = 3;
}

// A wake for one reader of a stream in a Workflow. The server records it on the next Workflow
// Task scheduled event, so it adds no event of its own.
//
// A wake says that the stream moved, not what it holds. The reader reads the stream itself. Wakes
// of one reader are folded, so the reader sees the latest one, with the highest `counter`.
message StreamWake {
// The `subscription_id` of the `WorkflowWakeTarget` this wake is for.
string subscription_id = 1;
// The position that came with `counter`, in the producer's terms.
string position = 2;
// The highest counter the notifier has seen. A reader that armed its read at a counter wakes
// when this value is above it. Zero when the notifier has seen no notification.
int64 counter = 3;
// The metadata that came with `counter`.
map<string, string> metadata = 4;
// Set when the notifier asks the reader to read again with no new counter. The notifier sends
// it after a quiet period, so a reader also finds records whose notification was lost.
bool recheck = 5;
// Set when the stream closed. The reader reads to the end of the stream, then sees the
// outcome.
bool closed = 6;
// The result the stream closed with, when `closed` is set. Optional.
temporal.api.common.v1.Payload close_result = 7;
// The failure the stream closed with, when `closed` is set and it closed with one.
temporal.api.failure.v1.Failure close_failure = 8;
// Set when the notifier dropped this reader, for example at the notifier's idle timeout. The
// reader attaches again under a new request ID.
bool resubscribe = 9;
}
Loading
Loading