diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9dd86b713..8cd343294 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,7 +14,7 @@ repos: - id: check-yaml exclude: (^examples/|^cmd/hatchet-cli/cli/templates/) - repo: https://github.com/golangci/golangci-lint - rev: v2.7.2 + rev: v2.8.0 hooks: - id: golangci-lint args: ["--config=.golangci.yml"] diff --git a/api-contracts/v1/dispatcher.proto b/api-contracts/v1/dispatcher.proto index 6c92f90ad..9b5a21253 100644 --- a/api-contracts/v1/dispatcher.proto +++ b/api-contracts/v1/dispatcher.proto @@ -5,15 +5,92 @@ option go_package = "github.com/hatchet-dev/hatchet/internal/services/shared/pro package v1; import "v1/shared/condition.proto"; +import "v1/shared/trigger.proto"; service V1Dispatcher { + rpc DurableTask(stream DurableTaskRequest) returns (stream DurableTaskResponse) {} + + // NOTE: deprecated after DurableEventLog is implemented rpc RegisterDurableEvent(RegisterDurableEventRequest) returns (RegisterDurableEventResponse) {} - rpc ListenForDurableEvent(stream ListenForDurableEventRequest) returns (stream DurableEvent) {} - } +message DurableTaskRequestRegisterWorker { + string worker_id = 1; +} +message DurableTaskResponseRegisterWorker { + string worker_id = 1; +} + +enum DurableTaskEventKind { + DURABLE_TASK_TRIGGER_KIND_UNSPECIFIED = 0; + DURABLE_TASK_TRIGGER_KIND_RUN = 1; + DURABLE_TASK_TRIGGER_KIND_WAIT_FOR = 2; + DURABLE_TASK_TRIGGER_KIND_MEMO = 3; +} + +message DurableTaskEventRequest { + // The invocation_count is a monotonically increasing count that uniquely identifies an "attempt" + // at running a durable task. Each time the task is started, it gets a new invocation count (which has) + // incremented by one since the previous invocation. This allows the server (and the worker) to have a way of + // differentiating between different attempts of the same task running in different places, to prevent race conditions + // and other problems from duplication. It also allows for older invocations to be evicted cleanly + int64 invocation_count = 1; + string durable_task_external_id = 2; + DurableTaskEventKind kind = 3; + optional bytes payload = 4; + optional DurableEventListenerConditions wait_for_conditions = 5; + + // Fields for DURABLE_TASK_TRIGGER_KIND_RUN (spawning child workflows) + optional TriggerWorkflowRequest trigger_opts = 6; +} + +message DurableTaskEventAckResponse { + int64 invocation_count = 1; + string durable_task_external_id = 2; + int64 node_id = 3; +} + +message DurableTaskCallbackCompletedResponse { + string durable_task_external_id = 1; + int64 node_id = 2; + bytes payload = 3; +} + +message DurableTaskEvictInvocationRequest { + int64 invocation_count = 1; + string durable_task_external_id = 2; +} + +message DurableTaskAwaitedCallback { + string durable_task_external_id = 1; + int64 node_id = 2; +} + +message DurableTaskWorkerStatusRequest { + string worker_id = 1; + int64 node_id = 2; + int64 branch_id = 3; + repeated DurableTaskAwaitedCallback waiting_callbacks = 4; +} + +message DurableTaskRequest { + oneof message { + DurableTaskRequestRegisterWorker register_worker = 1; + DurableTaskEventRequest event = 2; + DurableTaskEvictInvocationRequest evict_invocation = 3; + DurableTaskWorkerStatusRequest worker_status = 4; + } +} + +message DurableTaskResponse { + oneof message { + DurableTaskResponseRegisterWorker register_worker = 1; + DurableTaskEventAckResponse trigger_ack = 2; + DurableTaskCallbackCompletedResponse callback_completed = 3; + } +} message RegisterDurableEventRequest { string task_id = 1; // external uuid for the task run diff --git a/api-contracts/v1/shared/trigger.proto b/api-contracts/v1/shared/trigger.proto new file mode 100644 index 000000000..8be46eb75 --- /dev/null +++ b/api-contracts/v1/shared/trigger.proto @@ -0,0 +1,38 @@ +syntax = "proto3"; + +option go_package = "github.com/hatchet-dev/hatchet/internal/services/shared/proto/v1"; + +package v1; + +message TriggerWorkflowRequest { + string name = 1; + + // (optional) the input data for the workflow + string input = 2; + + // (optional) the parent workflow run id + optional string parent_id = 3; + + // (optional) the parent task external run id + optional string parent_task_run_external_id = 4; + + // (optional) the index of the child workflow. if this is set, matches on the index or the + // child key will return an existing workflow run if the parent id, parent task run id, and + // child index/key match an existing workflow run. + optional int32 child_index = 5; + + // (optional) the key for the child. if this is set, matches on the index or the + // child key will return an existing workflow run if the parent id, parent task run id, and + // child index/key match an existing workflow run. + optional string child_key = 6; + + // (optional) additional metadata for the workflow + optional string additional_metadata = 7; + + // (optional) desired worker id for the workflow run, + // requires the workflow definition to have a sticky strategy + optional string desired_worker_id = 8; + + // (optional) override for the priority of the workflow tasks, will set all tasks to this priority + optional int32 priority = 9; +} diff --git a/api-contracts/workflows/workflows.proto b/api-contracts/workflows/workflows.proto index 20106468c..5d6674858 100644 --- a/api-contracts/workflows/workflows.proto +++ b/api-contracts/workflows/workflows.proto @@ -3,12 +3,13 @@ syntax = "proto3"; option go_package = "github.com/hatchet-dev/hatchet/internal/services/admin/contracts"; import "google/protobuf/timestamp.proto"; +import "v1/shared/trigger.proto"; // WorkflowService represents a set of RPCs for managing workflows. service WorkflowService { rpc PutWorkflow(PutWorkflowRequest) returns (WorkflowVersion); rpc ScheduleWorkflow(ScheduleWorkflowRequest) returns (WorkflowVersion); - rpc TriggerWorkflow(TriggerWorkflowRequest) returns (TriggerWorkflowResponse); + rpc TriggerWorkflow(v1.TriggerWorkflowRequest) returns (TriggerWorkflowResponse); rpc BulkTriggerWorkflow(BulkTriggerWorkflowRequest) returns (BulkTriggerWorkflowResponse); rpc PutRateLimit(PutRateLimitRequest) returns (PutRateLimitResponse); } @@ -192,46 +193,13 @@ message WorkflowTriggerCronRef { } message BulkTriggerWorkflowRequest { - repeated TriggerWorkflowRequest workflows = 1; + repeated v1.TriggerWorkflowRequest workflows = 1; } message BulkTriggerWorkflowResponse { repeated string workflow_run_ids = 1; } -message TriggerWorkflowRequest { - string name = 1; - - // (optional) the input data for the workflow - string input = 2; - - // (optional) the parent workflow run id - optional string parent_id = 3; - - // (optional) the parent task external run id - optional string parent_task_run_external_id = 4; - - // (optional) the index of the child workflow. if this is set, matches on the index or the - // child key will return an existing workflow run if the parent id, parent task run id, and - // child index/key match an existing workflow run. - optional int32 child_index = 5; - - // (optional) the key for the child. if this is set, matches on the index or the - // child key will return an existing workflow run if the parent id, parent task run id, and - // child index/key match an existing workflow run. - optional string child_key = 6; - - // (optional) additional metadata for the workflow - optional string additional_metadata = 7; - - // (optional) desired worker id for the workflow run, - // requires the workflow definition to have a sticky strategy - optional string desired_worker_id = 8; - - // (optional) override for the priority of the workflow tasks, will set all tasks to this priority - optional int32 priority = 9; -} - message TriggerWorkflowResponse { string workflow_run_id = 1; } diff --git a/cmd/hatchet-engine/engine/run.go b/cmd/hatchet-engine/engine/run.go index a71f3e304..a33952c37 100644 --- a/cmd/hatchet-engine/engine/run.go +++ b/cmd/hatchet-engine/engine/run.go @@ -377,12 +377,15 @@ func runV0Config(ctx context.Context, sc *server.ServerConfig) ([]Teardown, erro dispatcherv1.WithRepository(sc.V1), dispatcherv1.WithMessageQueue(sc.MessageQueueV1), dispatcherv1.WithLogger(sc.Logger), + dispatcherv1.WithDispatcherId(d.DispatcherId()), ) if err != nil { return nil, fmt.Errorf("could not create dispatcher (v1): %w", err) } + d.SetDurableCallbackHandler(dv1.DeliverCallbackCompletion) + // create the event ingestor ei, err := ingestor.NewIngestor( ingestor.WithMessageQueueV1(sc.MessageQueueV1), @@ -815,12 +818,15 @@ func runV1Config(ctx context.Context, sc *server.ServerConfig) ([]Teardown, erro dispatcherv1.WithRepository(sc.V1), dispatcherv1.WithMessageQueue(sc.MessageQueueV1), dispatcherv1.WithLogger(sc.Logger), + dispatcherv1.WithDispatcherId(d.DispatcherId()), ) if err != nil { return nil, fmt.Errorf("could not create dispatcher (v1): %w", err) } + d.SetDurableCallbackHandler(dv1.DeliverCallbackCompletion) + // create the event ingestor ei, err := ingestor.NewIngestor( ingestor.WithMessageQueueV1(sc.MessageQueueV1), diff --git a/cmd/hatchet-migrate/migrate/migrations/20260204190149_v1_0_77.sql b/cmd/hatchet-migrate/migrate/migrations/20260204190149_v1_0_77.sql deleted file mode 100644 index 0880cf8f4..000000000 --- a/cmd/hatchet-migrate/migrate/migrations/20260204190149_v1_0_77.sql +++ /dev/null @@ -1,9 +0,0 @@ --- +goose Up --- +goose StatementBegin -ALTER TYPE v1_payload_type ADD VALUE IF NOT EXISTS 'DURABLE_EVENT_LOG_ENTRY_DATA'; --- +goose StatementEnd - --- +goose Down --- +goose StatementBegin --- Intentionally no down --- +goose StatementEnd diff --git a/cmd/hatchet-migrate/migrate/migrations/20260204174818_v1_0_76.sql b/cmd/hatchet-migrate/migrate/migrations/20260216174818_v1_0_78.sql similarity index 82% rename from cmd/hatchet-migrate/migrate/migrations/20260204174818_v1_0_76.sql rename to cmd/hatchet-migrate/migrate/migrations/20260216174818_v1_0_78.sql index c64a5131e..ba071f4d2 100644 --- a/cmd/hatchet-migrate/migrate/migrations/20260204174818_v1_0_76.sql +++ b/cmd/hatchet-migrate/migrate/migrations/20260216174818_v1_0_78.sql @@ -5,10 +5,15 @@ -- -- Important: writers to v1_durable_event_log_entry should lock this row to increment the sequence value. CREATE TABLE v1_durable_event_log_file ( + tenant_id UUID NOT NULL, -- The id and inserted_at of the durable task which created this entry durable_task_id BIGINT NOT NULL, durable_task_inserted_at TIMESTAMPTZ NOT NULL, + latest_inserted_at TIMESTAMPTZ NOT NULL, + + latest_invocation_count BIGINT NOT NULL, + -- A monotonically increasing node id for this durable event log scoped to the durable task. -- Starts at 0 and increments by 1 for each new entry. latest_node_id BIGINT NOT NULL, @@ -16,19 +21,21 @@ CREATE TABLE v1_durable_event_log_file ( latest_branch_id BIGINT NOT NULL, -- The parent node id which should be linked to the first node in a new branch to its parent node. latest_branch_first_parent_node_id BIGINT NOT NULL, + CONSTRAINT v1_durable_event_log_file_pkey PRIMARY KEY (durable_task_id, durable_task_inserted_at) ) PARTITION BY RANGE(durable_task_inserted_at); SELECT create_v1_range_partition('v1_durable_event_log_file', NOW()::DATE); SELECT create_v1_range_partition('v1_durable_event_log_file', (NOW() + INTERVAL '1 day')::DATE); -CREATE TYPE v1_durable_event_log_entry_kind AS ENUM ( - 'RUN_TRIGGERED', - 'WAIT_FOR_STARTED', - 'MEMO_STARTED' +CREATE TYPE v1_durable_event_log_kind AS ENUM ( + 'RUN', + 'WAIT_FOR', + 'MEMO' ); CREATE TABLE v1_durable_event_log_entry ( + tenant_id UUID NOT NULL, -- need an external id for consistency with the payload store logic (unfortunately) external_id UUID NOT NULL, -- The id and inserted_at of the durable task which created this entry @@ -39,7 +46,8 @@ CREATE TABLE v1_durable_event_log_entry ( durable_task_id BIGINT NOT NULL, durable_task_inserted_at TIMESTAMPTZ NOT NULL, - kind v1_durable_event_log_entry_kind, + + kind v1_durable_event_log_kind NOT NULL, -- The node number in the durable event log. This represents a monotonically increasing -- sequence value generated from v1_durable_event_log_file.latest_node_id node_id BIGINT NOT NULL, @@ -59,20 +67,13 @@ CREATE TABLE v1_durable_event_log_entry ( -- Definite: we'll query directly for the node_id when a durable task is replaying its log -- Possible: we may want to query a range of node_ids for a durable task -- Possible: we may want to query a range of inserted_ats for a durable task + CONSTRAINT v1_durable_event_log_entry_pkey PRIMARY KEY (durable_task_id, durable_task_inserted_at, node_id) ) PARTITION BY RANGE(durable_task_inserted_at); SELECT create_v1_range_partition('v1_durable_event_log_entry', NOW()::DATE); SELECT create_v1_range_partition('v1_durable_event_log_entry', (NOW() + INTERVAL '1 day')::DATE); -CREATE TYPE v1_durable_event_log_callback_kind AS ENUM ( - 'RUN_COMPLETED', - -- WAIT_FOR_COMPLETED can represent a durable sleep, an event, or some boolean combination of - -- these. - 'WAIT_FOR_COMPLETED', - 'MEMO_COMPLETED' -); - -- v1_durable_event_log_callback stores callbacks that complete a durable event log entry. This needs to be stateful -- so that it persists across worker restarts for the same durable task. -- @@ -84,6 +85,8 @@ CREATE TYPE v1_durable_event_log_callback_kind AS ENUM ( -- and direct queries from the engine side to mark a callback as satisfied when we've satisfied a v1_match. Because -- of this, we likely need to add a `callback_key` field to the v1_match table. CREATE TABLE v1_durable_event_log_callback ( + tenant_id UUID NOT NULL, + external_id UUID NOT NULL, -- The inserted_at time of this callback from a DB clock perspective. -- Important: for consistency, this should always be auto-generated via the CURRENT_TIMESTAMP! @@ -92,24 +95,33 @@ CREATE TABLE v1_durable_event_log_callback ( durable_task_id BIGINT NOT NULL, durable_task_inserted_at TIMESTAMPTZ NOT NULL, - kind v1_durable_event_log_callback_kind, - -- A unique, generated key for this callback. This key will change dependent on the callback kind. - -- Important: this key should be easily queryable directly from the durable log writers but also the controllers - -- that are checking if callbacks are satisfied. - key TEXT NOT NULL, + kind v1_durable_event_log_kind NOT NULL, + -- The associated log node id that this callback references. node_id BIGINT NOT NULL, -- Whether this callback has been seen by the engine or not. Note that is_satisfied _may_ change multiple -- times through the lifecycle of a callback, and readers should not assume that once it's true it will always be true. is_satisfied BOOLEAN NOT NULL DEFAULT FALSE, + -- Access patterns: -- Definite: we'll query directly for the key when a worker is checking if a callback is satisfied -- Definite: we'll query directly for the key when a v1_match has been satisfied and we need to mark the callback as satisfied - CONSTRAINT v1_durable_event_log_callback_pkey PRIMARY KEY (durable_task_id, durable_task_inserted_at, key) + CONSTRAINT v1_durable_event_log_callback_pkey PRIMARY KEY (durable_task_id, durable_task_inserted_at, node_id) ) PARTITION BY RANGE(durable_task_inserted_at); SELECT create_v1_range_partition('v1_durable_event_log_callback', NOW()::DATE); SELECT create_v1_range_partition('v1_durable_event_log_callback', (NOW() + INTERVAL '1 day')::DATE); + +ALTER TABLE v1_match + ADD COLUMN durable_event_log_callback_durable_task_external_id UUID, + ADD COLUMN durable_event_log_callback_node_id BIGINT, + ADD COLUMN durable_event_log_callback_durable_task_id BIGINT, + ADD COLUMN durable_event_log_callback_durable_task_inserted_at TIMESTAMPTZ; + +ALTER TYPE v1_payload_type ADD VALUE IF NOT EXISTS 'DURABLE_EVENT_LOG_ENTRY_DATA'; +ALTER TYPE v1_payload_type ADD VALUE IF NOT EXISTS 'DURABLE_EVENT_LOG_CALLBACK_RESULT_DATA'; + +ALTER TABLE "Worker" ADD COLUMN "durableTaskDispatcherId" UUID; -- +goose StatementEnd -- +goose Down @@ -117,6 +129,13 @@ SELECT create_v1_range_partition('v1_durable_event_log_callback', (NOW() + INTER DROP TABLE v1_durable_event_log_callback; DROP TABLE v1_durable_event_log_entry; DROP TABLE v1_durable_event_log_file; -DROP TYPE v1_durable_event_log_entry_kind; -DROP TYPE v1_durable_event_log_callback_kind; +DROP TYPE v1_durable_event_log_kind; + +ALTER TABLE v1_match + DROP COLUMN durable_event_log_callback_durable_task_external_id, + DROP COLUMN durable_event_log_callback_node_id, + DROP COLUMN durable_event_log_callback_durable_task_id, + DROP COLUMN durable_event_log_callback_durable_task_inserted_at; + +ALTER TABLE "Worker" DROP COLUMN "durableTaskDispatcherId"; -- +goose StatementEnd diff --git a/examples/python/dependency_injection/test_dependency_injection.py b/examples/python/dependency_injection/test_dependency_injection.py index 8c991d6f7..f067ad245 100644 --- a/examples/python/dependency_injection/test_dependency_injection.py +++ b/examples/python/dependency_injection/test_dependency_injection.py @@ -4,12 +4,9 @@ from examples.dependency_injection.worker import ( ASYNC_DEPENDENCY_VALUE, SYNC_DEPENDENCY_VALUE, Output, - async_dep, async_task_with_dependencies, di_workflow, durable_async_task_with_dependencies, - durable_sync_task_with_dependencies, - sync_dep, sync_task_with_dependencies, ) from hatchet_sdk import EmptyModel @@ -22,7 +19,6 @@ from hatchet_sdk.runnables.workflow import Standalone async_task_with_dependencies, sync_task_with_dependencies, durable_async_task_with_dependencies, - durable_sync_task_with_dependencies, ], ) @pytest.mark.asyncio(loop_scope="session") @@ -40,7 +36,7 @@ async def test_di_standalones( async def test_di_workflows() -> None: result = await di_workflow.aio_run() - assert len(result) == 4 + assert len(result) == 3 for output in result.values(): parsed = Output.model_validate(output) diff --git a/examples/python/dependency_injection/worker.py b/examples/python/dependency_injection/worker.py index 1b421a24c..0eecff0fd 100644 --- a/examples/python/dependency_injection/worker.py +++ b/examples/python/dependency_injection/worker.py @@ -153,27 +153,6 @@ async def durable_async_task_with_dependencies( ) -@hatchet.durable_task() -def durable_sync_task_with_dependencies( - _i: EmptyModel, - ctx: DurableContext, - async_dep: Annotated[str, Depends(async_dep)], - sync_dep: Annotated[str, Depends(sync_dep)], - async_cm_dep: Annotated[str, Depends(async_cm_dep)], - sync_cm_dep: Annotated[str, Depends(sync_cm_dep)], - chained_dep: Annotated[str, Depends(chained_dep)], - chained_async_dep: Annotated[str, Depends(chained_async_dep)], -) -> Output: - return Output( - sync_dep=sync_dep, - async_dep=async_dep, - async_cm_dep=async_cm_dep, - sync_cm_dep=sync_cm_dep, - chained_dep=chained_dep, - chained_async_dep=chained_async_dep, - ) - - di_workflow = hatchet.workflow( name="dependency-injection-workflow", ) @@ -242,27 +221,6 @@ async def wf_durable_async_task_with_dependencies( ) -@di_workflow.durable_task() -def wf_durable_sync_task_with_dependencies( - _i: EmptyModel, - ctx: DurableContext, - async_dep: Annotated[str, Depends(async_dep)], - sync_dep: Annotated[str, Depends(sync_dep)], - async_cm_dep: Annotated[str, Depends(async_cm_dep)], - sync_cm_dep: Annotated[str, Depends(sync_cm_dep)], - chained_dep: Annotated[str, Depends(chained_dep)], - chained_async_dep: Annotated[str, Depends(chained_async_dep)], -) -> Output: - return Output( - sync_dep=sync_dep, - async_dep=async_dep, - async_cm_dep=async_cm_dep, - sync_cm_dep=sync_cm_dep, - chained_dep=chained_dep, - chained_async_dep=chained_async_dep, - ) - - def main() -> None: worker = hatchet.worker( "dependency-injection-worker", @@ -270,7 +228,6 @@ def main() -> None: async_task_with_dependencies, sync_task_with_dependencies, durable_async_task_with_dependencies, - durable_sync_task_with_dependencies, di_workflow, ], ) diff --git a/examples/python/durable/worker.py b/examples/python/durable/worker.py index eb334b066..f397720b4 100644 --- a/examples/python/durable/worker.py +++ b/examples/python/durable/worker.py @@ -1,6 +1,7 @@ import asyncio import time from datetime import timedelta +from typing import Any from uuid import uuid4 from hatchet_sdk import ( @@ -15,6 +16,40 @@ from hatchet_sdk import ( hatchet = Hatchet(debug=True) + +dag_child_workflow = hatchet.workflow(name="dag-child-workflow") + + +@dag_child_workflow.task() +async def dag_child_1(input: EmptyModel, ctx: Context) -> dict[str, str]: + await asyncio.sleep(1) + return {"result": "child1"} + + +@dag_child_workflow.task() +async def dag_child_2(input: EmptyModel, ctx: Context) -> dict[str, str]: + await asyncio.sleep(5) + return {"result": "child2"} + + +@hatchet.durable_task() +async def durable_spawn_dag(input: EmptyModel, ctx: DurableContext) -> dict[str, Any]: + sleep_start = time.time() + sleep_result = await ctx.aio_sleep_for(timedelta(seconds=1)) + sleep_duration = time.time() - sleep_start + + spawn_start = time.time() + spawn_result = await dag_child_workflow.aio_run() + spawn_duration = time.time() - spawn_start + + return { + "sleep_duration": sleep_duration, + "sleep_result": sleep_result, + "spawn_duration": spawn_duration, + "spawn_result": spawn_result, + } + + # > Create a durable workflow durable_workflow = hatchet.workflow(name="DurableWorkflow") @@ -142,10 +177,49 @@ async def wait_for_sleep_twice( return {"runtime": -1} +@hatchet.task() +def spawn_child_task(input: EmptyModel, ctx: Context) -> dict[str, str]: + return {"message": "hello from child"} + + +@hatchet.durable_task() +async def durable_with_spawn(input: EmptyModel, ctx: DurableContext) -> dict[str, Any]: + child_result = await spawn_child_task.aio_run() + return {"child_output": child_result} + + +@hatchet.durable_task() +async def durable_sleep_event_spawn( + input: EmptyModel, ctx: DurableContext +) -> dict[str, Any]: + start = time.time() + + await ctx.aio_sleep_for(timedelta(seconds=SLEEP_TIME)) + + await ctx.aio_wait_for( + "event", + UserEventCondition(event_key=EVENT_KEY, expression="true"), + ) + + child_result = await spawn_child_task.aio_run() + + return { + "runtime": int(time.time() - start), + "child_output": child_result, + } + + def main() -> None: worker = hatchet.worker( "durable-worker", - workflows=[durable_workflow, ephemeral_workflow, wait_for_sleep_twice], + workflows=[ + durable_workflow, + ephemeral_workflow, + wait_for_sleep_twice, + spawn_child_task, + durable_with_spawn, + durable_sleep_event_spawn, + ], ) worker.start() diff --git a/examples/python/simple/worker.py b/examples/python/simple/worker.py index 2bd036166..b882e18ac 100644 --- a/examples/python/simple/worker.py +++ b/examples/python/simple/worker.py @@ -11,7 +11,8 @@ def simple(input: EmptyModel, ctx: Context) -> dict[str, str]: @hatchet.durable_task() -def simple_durable(input: EmptyModel, ctx: Context) -> dict[str, str]: +async def simple_durable(input: EmptyModel, ctx: Context) -> dict[str, str]: + # durable tasks should be async return {"result": "Hello, world!"} diff --git a/examples/python/worker.py b/examples/python/worker.py index 6265e26f1..fffd8337f 100644 --- a/examples/python/worker.py +++ b/examples/python/worker.py @@ -27,11 +27,18 @@ from examples.dependency_injection.worker import ( async_task_with_dependencies, di_workflow, durable_async_task_with_dependencies, - durable_sync_task_with_dependencies, sync_task_with_dependencies, ) from examples.dict_input.worker import say_hello_unsafely -from examples.durable.worker import durable_workflow, wait_for_sleep_twice +from examples.durable.worker import ( + durable_sleep_event_spawn, + durable_with_spawn, + durable_workflow, + spawn_child_task, + wait_for_sleep_twice, + dag_child_workflow, + durable_spawn_dag, +) from examples.events.worker import event_workflow from examples.fanout.worker import child_wf, parent_wf from examples.fanout_sync.worker import sync_fanout_child, sync_fanout_parent @@ -103,13 +110,17 @@ def main() -> None: return_exceptions_task, exception_parsing_workflow, wait_for_sleep_twice, + spawn_child_task, + durable_with_spawn, + durable_sleep_event_spawn, async_task_with_dependencies, sync_task_with_dependencies, durable_async_task_with_dependencies, - durable_sync_task_with_dependencies, say_hello, say_hello_unsafely, serde_workflow, + durable_spawn_dag, + dag_child_workflow, ], lifespan=lifespan, ) diff --git a/hack/proto/proto.sh b/hack/proto/proto.sh index f55e6faa1..8d27ae5cf 100644 --- a/hack/proto/proto.sh +++ b/hack/proto/proto.sh @@ -12,7 +12,8 @@ protoc --proto_path=api-contracts \ --go_opt=module=github.com/hatchet-dev/hatchet/internal/services/shared/proto/v1 \ --go-grpc_out=./internal/services/shared/proto/v1 \ --go-grpc_opt=module=github.com/hatchet-dev/hatchet/internal/services/shared/proto/v1 \ - v1/shared/condition.proto + v1/shared/condition.proto \ + v1/shared/trigger.proto protoc --proto_path=api-contracts \ --go_out=./internal/services/shared/proto/v1 \ @@ -36,6 +37,6 @@ protoc --proto_path=api-contracts/events --go_out=./internal/services/ingestor/c --go-grpc_out=./internal/services/ingestor/contracts --go-grpc_opt=paths=source_relative \ events.proto -protoc --proto_path=api-contracts/workflows --go_out=./internal/services/admin/contracts --go_opt=paths=source_relative \ +protoc --proto_path=api-contracts --go_out=./internal/services/admin/contracts --go_opt=paths=source_relative \ --go-grpc_out=./internal/services/admin/contracts --go-grpc_opt=paths=source_relative \ - workflows.proto + workflows/workflows.proto diff --git a/internal/msgqueue/message_ids.go b/internal/msgqueue/message_ids.go index 115b347b3..a9cb19ef2 100644 --- a/internal/msgqueue/message_ids.go +++ b/internal/msgqueue/message_ids.go @@ -3,6 +3,7 @@ package msgqueue // Message ID constants for tenant messages const ( MsgIDCancelTasks = "cancel-tasks" + MsgIDDurableCallbackCompleted = "durable-callback-completed" MsgIDCELEvaluationFailure = "cel-evaluation-failure" MsgIDCheckTenantQueue = "check-tenant-queue" MsgIDNewWorker = "new-worker" diff --git a/internal/services/admin/admin.go b/internal/services/admin/admin.go index f3ec8432f..094c45f64 100644 --- a/internal/services/admin/admin.go +++ b/internal/services/admin/admin.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/hatchet-dev/hatchet/internal/msgqueue" - "github.com/hatchet-dev/hatchet/internal/services/admin/contracts" + contracts "github.com/hatchet-dev/hatchet/internal/services/admin/contracts/workflows" "github.com/hatchet-dev/hatchet/internal/services/controllers/task/trigger" "github.com/hatchet-dev/hatchet/internal/services/dispatcher" scheduler "github.com/hatchet-dev/hatchet/internal/services/scheduler/v1" diff --git a/internal/services/admin/contracts/workflows.pb.go b/internal/services/admin/contracts/workflows/workflows.pb.go similarity index 56% rename from internal/services/admin/contracts/workflows.pb.go rename to internal/services/admin/contracts/workflows/workflows.pb.go index 88c022d1a..2f92f3bb6 100644 --- a/internal/services/admin/contracts/workflows.pb.go +++ b/internal/services/admin/contracts/workflows/workflows.pb.go @@ -2,11 +2,12 @@ // versions: // protoc-gen-go v1.28.1 // protoc v5.29.3 -// source: workflows.proto +// source: workflows/workflows.proto package contracts import ( + v1 "github.com/hatchet-dev/hatchet/internal/services/shared/proto/v1" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" timestamppb "google.golang.org/protobuf/types/known/timestamppb" @@ -51,11 +52,11 @@ func (x StickyStrategy) String() string { } func (StickyStrategy) Descriptor() protoreflect.EnumDescriptor { - return file_workflows_proto_enumTypes[0].Descriptor() + return file_workflows_workflows_proto_enumTypes[0].Descriptor() } func (StickyStrategy) Type() protoreflect.EnumType { - return &file_workflows_proto_enumTypes[0] + return &file_workflows_workflows_proto_enumTypes[0] } func (x StickyStrategy) Number() protoreflect.EnumNumber { @@ -64,7 +65,7 @@ func (x StickyStrategy) Number() protoreflect.EnumNumber { // Deprecated: Use StickyStrategy.Descriptor instead. func (StickyStrategy) EnumDescriptor() ([]byte, []int) { - return file_workflows_proto_rawDescGZIP(), []int{0} + return file_workflows_workflows_proto_rawDescGZIP(), []int{0} } type WorkflowKind int32 @@ -100,11 +101,11 @@ func (x WorkflowKind) String() string { } func (WorkflowKind) Descriptor() protoreflect.EnumDescriptor { - return file_workflows_proto_enumTypes[1].Descriptor() + return file_workflows_workflows_proto_enumTypes[1].Descriptor() } func (WorkflowKind) Type() protoreflect.EnumType { - return &file_workflows_proto_enumTypes[1] + return &file_workflows_workflows_proto_enumTypes[1] } func (x WorkflowKind) Number() protoreflect.EnumNumber { @@ -113,7 +114,7 @@ func (x WorkflowKind) Number() protoreflect.EnumNumber { // Deprecated: Use WorkflowKind.Descriptor instead. func (WorkflowKind) EnumDescriptor() ([]byte, []int) { - return file_workflows_proto_rawDescGZIP(), []int{1} + return file_workflows_workflows_proto_rawDescGZIP(), []int{1} } type ConcurrencyLimitStrategy int32 @@ -155,11 +156,11 @@ func (x ConcurrencyLimitStrategy) String() string { } func (ConcurrencyLimitStrategy) Descriptor() protoreflect.EnumDescriptor { - return file_workflows_proto_enumTypes[2].Descriptor() + return file_workflows_workflows_proto_enumTypes[2].Descriptor() } func (ConcurrencyLimitStrategy) Type() protoreflect.EnumType { - return &file_workflows_proto_enumTypes[2] + return &file_workflows_workflows_proto_enumTypes[2] } func (x ConcurrencyLimitStrategy) Number() protoreflect.EnumNumber { @@ -168,7 +169,7 @@ func (x ConcurrencyLimitStrategy) Number() protoreflect.EnumNumber { // Deprecated: Use ConcurrencyLimitStrategy.Descriptor instead. func (ConcurrencyLimitStrategy) EnumDescriptor() ([]byte, []int) { - return file_workflows_proto_rawDescGZIP(), []int{2} + return file_workflows_workflows_proto_rawDescGZIP(), []int{2} } type WorkerLabelComparator int32 @@ -213,11 +214,11 @@ func (x WorkerLabelComparator) String() string { } func (WorkerLabelComparator) Descriptor() protoreflect.EnumDescriptor { - return file_workflows_proto_enumTypes[3].Descriptor() + return file_workflows_workflows_proto_enumTypes[3].Descriptor() } func (WorkerLabelComparator) Type() protoreflect.EnumType { - return &file_workflows_proto_enumTypes[3] + return &file_workflows_workflows_proto_enumTypes[3] } func (x WorkerLabelComparator) Number() protoreflect.EnumNumber { @@ -226,7 +227,7 @@ func (x WorkerLabelComparator) Number() protoreflect.EnumNumber { // Deprecated: Use WorkerLabelComparator.Descriptor instead. func (WorkerLabelComparator) EnumDescriptor() ([]byte, []int) { - return file_workflows_proto_rawDescGZIP(), []int{3} + return file_workflows_workflows_proto_rawDescGZIP(), []int{3} } type RateLimitDuration int32 @@ -274,11 +275,11 @@ func (x RateLimitDuration) String() string { } func (RateLimitDuration) Descriptor() protoreflect.EnumDescriptor { - return file_workflows_proto_enumTypes[4].Descriptor() + return file_workflows_workflows_proto_enumTypes[4].Descriptor() } func (RateLimitDuration) Type() protoreflect.EnumType { - return &file_workflows_proto_enumTypes[4] + return &file_workflows_workflows_proto_enumTypes[4] } func (x RateLimitDuration) Number() protoreflect.EnumNumber { @@ -287,7 +288,7 @@ func (x RateLimitDuration) Number() protoreflect.EnumNumber { // Deprecated: Use RateLimitDuration.Descriptor instead. func (RateLimitDuration) EnumDescriptor() ([]byte, []int) { - return file_workflows_proto_rawDescGZIP(), []int{4} + return file_workflows_workflows_proto_rawDescGZIP(), []int{4} } type PutWorkflowRequest struct { @@ -301,7 +302,7 @@ type PutWorkflowRequest struct { func (x *PutWorkflowRequest) Reset() { *x = PutWorkflowRequest{} if protoimpl.UnsafeEnabled { - mi := &file_workflows_proto_msgTypes[0] + mi := &file_workflows_workflows_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -314,7 +315,7 @@ func (x *PutWorkflowRequest) String() string { func (*PutWorkflowRequest) ProtoMessage() {} func (x *PutWorkflowRequest) ProtoReflect() protoreflect.Message { - mi := &file_workflows_proto_msgTypes[0] + mi := &file_workflows_workflows_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -327,7 +328,7 @@ func (x *PutWorkflowRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PutWorkflowRequest.ProtoReflect.Descriptor instead. func (*PutWorkflowRequest) Descriptor() ([]byte, []int) { - return file_workflows_proto_rawDescGZIP(), []int{0} + return file_workflows_workflows_proto_rawDescGZIP(), []int{0} } func (x *PutWorkflowRequest) GetOpts() *CreateWorkflowVersionOpts { @@ -362,7 +363,7 @@ type CreateWorkflowVersionOpts struct { func (x *CreateWorkflowVersionOpts) Reset() { *x = CreateWorkflowVersionOpts{} if protoimpl.UnsafeEnabled { - mi := &file_workflows_proto_msgTypes[1] + mi := &file_workflows_workflows_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -375,7 +376,7 @@ func (x *CreateWorkflowVersionOpts) String() string { func (*CreateWorkflowVersionOpts) ProtoMessage() {} func (x *CreateWorkflowVersionOpts) ProtoReflect() protoreflect.Message { - mi := &file_workflows_proto_msgTypes[1] + mi := &file_workflows_workflows_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -388,7 +389,7 @@ func (x *CreateWorkflowVersionOpts) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateWorkflowVersionOpts.ProtoReflect.Descriptor instead. func (*CreateWorkflowVersionOpts) Descriptor() ([]byte, []int) { - return file_workflows_proto_rawDescGZIP(), []int{1} + return file_workflows_workflows_proto_rawDescGZIP(), []int{1} } func (x *CreateWorkflowVersionOpts) GetName() string { @@ -503,7 +504,7 @@ type WorkflowConcurrencyOpts struct { func (x *WorkflowConcurrencyOpts) Reset() { *x = WorkflowConcurrencyOpts{} if protoimpl.UnsafeEnabled { - mi := &file_workflows_proto_msgTypes[2] + mi := &file_workflows_workflows_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -516,7 +517,7 @@ func (x *WorkflowConcurrencyOpts) String() string { func (*WorkflowConcurrencyOpts) ProtoMessage() {} func (x *WorkflowConcurrencyOpts) ProtoReflect() protoreflect.Message { - mi := &file_workflows_proto_msgTypes[2] + mi := &file_workflows_workflows_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -529,7 +530,7 @@ func (x *WorkflowConcurrencyOpts) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkflowConcurrencyOpts.ProtoReflect.Descriptor instead. func (*WorkflowConcurrencyOpts) Descriptor() ([]byte, []int) { - return file_workflows_proto_rawDescGZIP(), []int{2} + return file_workflows_workflows_proto_rawDescGZIP(), []int{2} } func (x *WorkflowConcurrencyOpts) GetAction() string { @@ -574,7 +575,7 @@ type CreateWorkflowJobOpts struct { func (x *CreateWorkflowJobOpts) Reset() { *x = CreateWorkflowJobOpts{} if protoimpl.UnsafeEnabled { - mi := &file_workflows_proto_msgTypes[3] + mi := &file_workflows_workflows_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -587,7 +588,7 @@ func (x *CreateWorkflowJobOpts) String() string { func (*CreateWorkflowJobOpts) ProtoMessage() {} func (x *CreateWorkflowJobOpts) ProtoReflect() protoreflect.Message { - mi := &file_workflows_proto_msgTypes[3] + mi := &file_workflows_workflows_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -600,7 +601,7 @@ func (x *CreateWorkflowJobOpts) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateWorkflowJobOpts.ProtoReflect.Descriptor instead. func (*CreateWorkflowJobOpts) Descriptor() ([]byte, []int) { - return file_workflows_proto_rawDescGZIP(), []int{3} + return file_workflows_workflows_proto_rawDescGZIP(), []int{3} } func (x *CreateWorkflowJobOpts) GetName() string { @@ -651,7 +652,7 @@ type DesiredWorkerLabels struct { func (x *DesiredWorkerLabels) Reset() { *x = DesiredWorkerLabels{} if protoimpl.UnsafeEnabled { - mi := &file_workflows_proto_msgTypes[4] + mi := &file_workflows_workflows_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -664,7 +665,7 @@ func (x *DesiredWorkerLabels) String() string { func (*DesiredWorkerLabels) ProtoMessage() {} func (x *DesiredWorkerLabels) ProtoReflect() protoreflect.Message { - mi := &file_workflows_proto_msgTypes[4] + mi := &file_workflows_workflows_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -677,7 +678,7 @@ func (x *DesiredWorkerLabels) ProtoReflect() protoreflect.Message { // Deprecated: Use DesiredWorkerLabels.ProtoReflect.Descriptor instead. func (*DesiredWorkerLabels) Descriptor() ([]byte, []int) { - return file_workflows_proto_rawDescGZIP(), []int{4} + return file_workflows_workflows_proto_rawDescGZIP(), []int{4} } func (x *DesiredWorkerLabels) GetStrValue() string { @@ -737,7 +738,7 @@ type CreateWorkflowStepOpts struct { func (x *CreateWorkflowStepOpts) Reset() { *x = CreateWorkflowStepOpts{} if protoimpl.UnsafeEnabled { - mi := &file_workflows_proto_msgTypes[5] + mi := &file_workflows_workflows_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -750,7 +751,7 @@ func (x *CreateWorkflowStepOpts) String() string { func (*CreateWorkflowStepOpts) ProtoMessage() {} func (x *CreateWorkflowStepOpts) ProtoReflect() protoreflect.Message { - mi := &file_workflows_proto_msgTypes[5] + mi := &file_workflows_workflows_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -763,7 +764,7 @@ func (x *CreateWorkflowStepOpts) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateWorkflowStepOpts.ProtoReflect.Descriptor instead. func (*CreateWorkflowStepOpts) Descriptor() ([]byte, []int) { - return file_workflows_proto_rawDescGZIP(), []int{5} + return file_workflows_workflows_proto_rawDescGZIP(), []int{5} } func (x *CreateWorkflowStepOpts) GetReadableId() string { @@ -859,7 +860,7 @@ type CreateStepRateLimit struct { func (x *CreateStepRateLimit) Reset() { *x = CreateStepRateLimit{} if protoimpl.UnsafeEnabled { - mi := &file_workflows_proto_msgTypes[6] + mi := &file_workflows_workflows_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -872,7 +873,7 @@ func (x *CreateStepRateLimit) String() string { func (*CreateStepRateLimit) ProtoMessage() {} func (x *CreateStepRateLimit) ProtoReflect() protoreflect.Message { - mi := &file_workflows_proto_msgTypes[6] + mi := &file_workflows_workflows_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -885,7 +886,7 @@ func (x *CreateStepRateLimit) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateStepRateLimit.ProtoReflect.Descriptor instead. func (*CreateStepRateLimit) Descriptor() ([]byte, []int) { - return file_workflows_proto_rawDescGZIP(), []int{6} + return file_workflows_workflows_proto_rawDescGZIP(), []int{6} } func (x *CreateStepRateLimit) GetKey() string { @@ -940,7 +941,7 @@ type ListWorkflowsRequest struct { func (x *ListWorkflowsRequest) Reset() { *x = ListWorkflowsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_workflows_proto_msgTypes[7] + mi := &file_workflows_workflows_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -953,7 +954,7 @@ func (x *ListWorkflowsRequest) String() string { func (*ListWorkflowsRequest) ProtoMessage() {} func (x *ListWorkflowsRequest) ProtoReflect() protoreflect.Message { - mi := &file_workflows_proto_msgTypes[7] + mi := &file_workflows_workflows_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -966,7 +967,7 @@ func (x *ListWorkflowsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorkflowsRequest.ProtoReflect.Descriptor instead. func (*ListWorkflowsRequest) Descriptor() ([]byte, []int) { - return file_workflows_proto_rawDescGZIP(), []int{7} + return file_workflows_workflows_proto_rawDescGZIP(), []int{7} } type ScheduleWorkflowRequest struct { @@ -997,7 +998,7 @@ type ScheduleWorkflowRequest struct { func (x *ScheduleWorkflowRequest) Reset() { *x = ScheduleWorkflowRequest{} if protoimpl.UnsafeEnabled { - mi := &file_workflows_proto_msgTypes[8] + mi := &file_workflows_workflows_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1010,7 +1011,7 @@ func (x *ScheduleWorkflowRequest) String() string { func (*ScheduleWorkflowRequest) ProtoMessage() {} func (x *ScheduleWorkflowRequest) ProtoReflect() protoreflect.Message { - mi := &file_workflows_proto_msgTypes[8] + mi := &file_workflows_workflows_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1023,7 +1024,7 @@ func (x *ScheduleWorkflowRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ScheduleWorkflowRequest.ProtoReflect.Descriptor instead. func (*ScheduleWorkflowRequest) Descriptor() ([]byte, []int) { - return file_workflows_proto_rawDescGZIP(), []int{8} + return file_workflows_workflows_proto_rawDescGZIP(), []int{8} } func (x *ScheduleWorkflowRequest) GetName() string { @@ -1102,7 +1103,7 @@ type ScheduledWorkflow struct { func (x *ScheduledWorkflow) Reset() { *x = ScheduledWorkflow{} if protoimpl.UnsafeEnabled { - mi := &file_workflows_proto_msgTypes[9] + mi := &file_workflows_workflows_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1115,7 +1116,7 @@ func (x *ScheduledWorkflow) String() string { func (*ScheduledWorkflow) ProtoMessage() {} func (x *ScheduledWorkflow) ProtoReflect() protoreflect.Message { - mi := &file_workflows_proto_msgTypes[9] + mi := &file_workflows_workflows_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1128,7 +1129,7 @@ func (x *ScheduledWorkflow) ProtoReflect() protoreflect.Message { // Deprecated: Use ScheduledWorkflow.ProtoReflect.Descriptor instead. func (*ScheduledWorkflow) Descriptor() ([]byte, []int) { - return file_workflows_proto_rawDescGZIP(), []int{9} + return file_workflows_workflows_proto_rawDescGZIP(), []int{9} } func (x *ScheduledWorkflow) GetId() string { @@ -1163,7 +1164,7 @@ type WorkflowVersion struct { func (x *WorkflowVersion) Reset() { *x = WorkflowVersion{} if protoimpl.UnsafeEnabled { - mi := &file_workflows_proto_msgTypes[10] + mi := &file_workflows_workflows_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1176,7 +1177,7 @@ func (x *WorkflowVersion) String() string { func (*WorkflowVersion) ProtoMessage() {} func (x *WorkflowVersion) ProtoReflect() protoreflect.Message { - mi := &file_workflows_proto_msgTypes[10] + mi := &file_workflows_workflows_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1189,7 +1190,7 @@ func (x *WorkflowVersion) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkflowVersion.ProtoReflect.Descriptor instead. func (*WorkflowVersion) Descriptor() ([]byte, []int) { - return file_workflows_proto_rawDescGZIP(), []int{10} + return file_workflows_workflows_proto_rawDescGZIP(), []int{10} } func (x *WorkflowVersion) GetId() string { @@ -1254,7 +1255,7 @@ type WorkflowTriggerEventRef struct { func (x *WorkflowTriggerEventRef) Reset() { *x = WorkflowTriggerEventRef{} if protoimpl.UnsafeEnabled { - mi := &file_workflows_proto_msgTypes[11] + mi := &file_workflows_workflows_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1267,7 +1268,7 @@ func (x *WorkflowTriggerEventRef) String() string { func (*WorkflowTriggerEventRef) ProtoMessage() {} func (x *WorkflowTriggerEventRef) ProtoReflect() protoreflect.Message { - mi := &file_workflows_proto_msgTypes[11] + mi := &file_workflows_workflows_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1280,7 +1281,7 @@ func (x *WorkflowTriggerEventRef) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkflowTriggerEventRef.ProtoReflect.Descriptor instead. func (*WorkflowTriggerEventRef) Descriptor() ([]byte, []int) { - return file_workflows_proto_rawDescGZIP(), []int{11} + return file_workflows_workflows_proto_rawDescGZIP(), []int{11} } func (x *WorkflowTriggerEventRef) GetParentId() string { @@ -1310,7 +1311,7 @@ type WorkflowTriggerCronRef struct { func (x *WorkflowTriggerCronRef) Reset() { *x = WorkflowTriggerCronRef{} if protoimpl.UnsafeEnabled { - mi := &file_workflows_proto_msgTypes[12] + mi := &file_workflows_workflows_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1323,7 +1324,7 @@ func (x *WorkflowTriggerCronRef) String() string { func (*WorkflowTriggerCronRef) ProtoMessage() {} func (x *WorkflowTriggerCronRef) ProtoReflect() protoreflect.Message { - mi := &file_workflows_proto_msgTypes[12] + mi := &file_workflows_workflows_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1336,7 +1337,7 @@ func (x *WorkflowTriggerCronRef) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkflowTriggerCronRef.ProtoReflect.Descriptor instead. func (*WorkflowTriggerCronRef) Descriptor() ([]byte, []int) { - return file_workflows_proto_rawDescGZIP(), []int{12} + return file_workflows_workflows_proto_rawDescGZIP(), []int{12} } func (x *WorkflowTriggerCronRef) GetParentId() string { @@ -1358,13 +1359,13 @@ type BulkTriggerWorkflowRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Workflows []*TriggerWorkflowRequest `protobuf:"bytes,1,rep,name=workflows,proto3" json:"workflows,omitempty"` + Workflows []*v1.TriggerWorkflowRequest `protobuf:"bytes,1,rep,name=workflows,proto3" json:"workflows,omitempty"` } func (x *BulkTriggerWorkflowRequest) Reset() { *x = BulkTriggerWorkflowRequest{} if protoimpl.UnsafeEnabled { - mi := &file_workflows_proto_msgTypes[13] + mi := &file_workflows_workflows_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1377,7 +1378,7 @@ func (x *BulkTriggerWorkflowRequest) String() string { func (*BulkTriggerWorkflowRequest) ProtoMessage() {} func (x *BulkTriggerWorkflowRequest) ProtoReflect() protoreflect.Message { - mi := &file_workflows_proto_msgTypes[13] + mi := &file_workflows_workflows_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1390,10 +1391,10 @@ func (x *BulkTriggerWorkflowRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use BulkTriggerWorkflowRequest.ProtoReflect.Descriptor instead. func (*BulkTriggerWorkflowRequest) Descriptor() ([]byte, []int) { - return file_workflows_proto_rawDescGZIP(), []int{13} + return file_workflows_workflows_proto_rawDescGZIP(), []int{13} } -func (x *BulkTriggerWorkflowRequest) GetWorkflows() []*TriggerWorkflowRequest { +func (x *BulkTriggerWorkflowRequest) GetWorkflows() []*v1.TriggerWorkflowRequest { if x != nil { return x.Workflows } @@ -1411,7 +1412,7 @@ type BulkTriggerWorkflowResponse struct { func (x *BulkTriggerWorkflowResponse) Reset() { *x = BulkTriggerWorkflowResponse{} if protoimpl.UnsafeEnabled { - mi := &file_workflows_proto_msgTypes[14] + mi := &file_workflows_workflows_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1424,7 +1425,7 @@ func (x *BulkTriggerWorkflowResponse) String() string { func (*BulkTriggerWorkflowResponse) ProtoMessage() {} func (x *BulkTriggerWorkflowResponse) ProtoReflect() protoreflect.Message { - mi := &file_workflows_proto_msgTypes[14] + mi := &file_workflows_workflows_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1437,7 +1438,7 @@ func (x *BulkTriggerWorkflowResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use BulkTriggerWorkflowResponse.ProtoReflect.Descriptor instead. func (*BulkTriggerWorkflowResponse) Descriptor() ([]byte, []int) { - return file_workflows_proto_rawDescGZIP(), []int{14} + return file_workflows_workflows_proto_rawDescGZIP(), []int{14} } func (x *BulkTriggerWorkflowResponse) GetWorkflowRunIds() []string { @@ -1447,130 +1448,6 @@ func (x *BulkTriggerWorkflowResponse) GetWorkflowRunIds() []string { return nil } -type TriggerWorkflowRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // (optional) the input data for the workflow - Input string `protobuf:"bytes,2,opt,name=input,proto3" json:"input,omitempty"` - // (optional) the parent workflow run id - ParentId *string `protobuf:"bytes,3,opt,name=parent_id,json=parentId,proto3,oneof" json:"parent_id,omitempty"` - // (optional) the parent task external run id - ParentTaskRunExternalId *string `protobuf:"bytes,4,opt,name=parent_task_run_external_id,json=parentTaskRunExternalId,proto3,oneof" json:"parent_task_run_external_id,omitempty"` - // (optional) the index of the child workflow. if this is set, matches on the index or the - // child key will return an existing workflow run if the parent id, parent task run id, and - // child index/key match an existing workflow run. - ChildIndex *int32 `protobuf:"varint,5,opt,name=child_index,json=childIndex,proto3,oneof" json:"child_index,omitempty"` - // (optional) the key for the child. if this is set, matches on the index or the - // child key will return an existing workflow run if the parent id, parent task run id, and - // child index/key match an existing workflow run. - ChildKey *string `protobuf:"bytes,6,opt,name=child_key,json=childKey,proto3,oneof" json:"child_key,omitempty"` - // (optional) additional metadata for the workflow - AdditionalMetadata *string `protobuf:"bytes,7,opt,name=additional_metadata,json=additionalMetadata,proto3,oneof" json:"additional_metadata,omitempty"` - // (optional) desired worker id for the workflow run, - // requires the workflow definition to have a sticky strategy - DesiredWorkerId *string `protobuf:"bytes,8,opt,name=desired_worker_id,json=desiredWorkerId,proto3,oneof" json:"desired_worker_id,omitempty"` - // (optional) override for the priority of the workflow tasks, will set all tasks to this priority - Priority *int32 `protobuf:"varint,9,opt,name=priority,proto3,oneof" json:"priority,omitempty"` -} - -func (x *TriggerWorkflowRequest) Reset() { - *x = TriggerWorkflowRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_workflows_proto_msgTypes[15] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TriggerWorkflowRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TriggerWorkflowRequest) ProtoMessage() {} - -func (x *TriggerWorkflowRequest) ProtoReflect() protoreflect.Message { - mi := &file_workflows_proto_msgTypes[15] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TriggerWorkflowRequest.ProtoReflect.Descriptor instead. -func (*TriggerWorkflowRequest) Descriptor() ([]byte, []int) { - return file_workflows_proto_rawDescGZIP(), []int{15} -} - -func (x *TriggerWorkflowRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *TriggerWorkflowRequest) GetInput() string { - if x != nil { - return x.Input - } - return "" -} - -func (x *TriggerWorkflowRequest) GetParentId() string { - if x != nil && x.ParentId != nil { - return *x.ParentId - } - return "" -} - -func (x *TriggerWorkflowRequest) GetParentTaskRunExternalId() string { - if x != nil && x.ParentTaskRunExternalId != nil { - return *x.ParentTaskRunExternalId - } - return "" -} - -func (x *TriggerWorkflowRequest) GetChildIndex() int32 { - if x != nil && x.ChildIndex != nil { - return *x.ChildIndex - } - return 0 -} - -func (x *TriggerWorkflowRequest) GetChildKey() string { - if x != nil && x.ChildKey != nil { - return *x.ChildKey - } - return "" -} - -func (x *TriggerWorkflowRequest) GetAdditionalMetadata() string { - if x != nil && x.AdditionalMetadata != nil { - return *x.AdditionalMetadata - } - return "" -} - -func (x *TriggerWorkflowRequest) GetDesiredWorkerId() string { - if x != nil && x.DesiredWorkerId != nil { - return *x.DesiredWorkerId - } - return "" -} - -func (x *TriggerWorkflowRequest) GetPriority() int32 { - if x != nil && x.Priority != nil { - return *x.Priority - } - return 0 -} - type TriggerWorkflowResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1582,7 +1459,7 @@ type TriggerWorkflowResponse struct { func (x *TriggerWorkflowResponse) Reset() { *x = TriggerWorkflowResponse{} if protoimpl.UnsafeEnabled { - mi := &file_workflows_proto_msgTypes[16] + mi := &file_workflows_workflows_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1595,7 +1472,7 @@ func (x *TriggerWorkflowResponse) String() string { func (*TriggerWorkflowResponse) ProtoMessage() {} func (x *TriggerWorkflowResponse) ProtoReflect() protoreflect.Message { - mi := &file_workflows_proto_msgTypes[16] + mi := &file_workflows_workflows_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1608,7 +1485,7 @@ func (x *TriggerWorkflowResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use TriggerWorkflowResponse.ProtoReflect.Descriptor instead. func (*TriggerWorkflowResponse) Descriptor() ([]byte, []int) { - return file_workflows_proto_rawDescGZIP(), []int{16} + return file_workflows_workflows_proto_rawDescGZIP(), []int{15} } func (x *TriggerWorkflowResponse) GetWorkflowRunId() string { @@ -1634,7 +1511,7 @@ type PutRateLimitRequest struct { func (x *PutRateLimitRequest) Reset() { *x = PutRateLimitRequest{} if protoimpl.UnsafeEnabled { - mi := &file_workflows_proto_msgTypes[17] + mi := &file_workflows_workflows_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1647,7 +1524,7 @@ func (x *PutRateLimitRequest) String() string { func (*PutRateLimitRequest) ProtoMessage() {} func (x *PutRateLimitRequest) ProtoReflect() protoreflect.Message { - mi := &file_workflows_proto_msgTypes[17] + mi := &file_workflows_workflows_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1660,7 +1537,7 @@ func (x *PutRateLimitRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PutRateLimitRequest.ProtoReflect.Descriptor instead. func (*PutRateLimitRequest) Descriptor() ([]byte, []int) { - return file_workflows_proto_rawDescGZIP(), []int{17} + return file_workflows_workflows_proto_rawDescGZIP(), []int{16} } func (x *PutRateLimitRequest) GetKey() string { @@ -1693,7 +1570,7 @@ type PutRateLimitResponse struct { func (x *PutRateLimitResponse) Reset() { *x = PutRateLimitResponse{} if protoimpl.UnsafeEnabled { - mi := &file_workflows_proto_msgTypes[18] + mi := &file_workflows_workflows_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1706,7 +1583,7 @@ func (x *PutRateLimitResponse) String() string { func (*PutRateLimitResponse) ProtoMessage() {} func (x *PutRateLimitResponse) ProtoReflect() protoreflect.Message { - mi := &file_workflows_proto_msgTypes[18] + mi := &file_workflows_workflows_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1719,358 +1596,329 @@ func (x *PutRateLimitResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PutRateLimitResponse.ProtoReflect.Descriptor instead. func (*PutRateLimitResponse) Descriptor() ([]byte, []int) { - return file_workflows_proto_rawDescGZIP(), []int{18} + return file_workflows_workflows_proto_rawDescGZIP(), []int{17} } -var File_workflows_proto protoreflect.FileDescriptor +var File_workflows_workflows_proto protoreflect.FileDescriptor -var file_workflows_proto_rawDesc = []byte{ - 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0x44, 0x0a, 0x12, 0x50, 0x75, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, - 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6f, 0x70, 0x74, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, - 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4f, 0x70, - 0x74, 0x73, 0x52, 0x04, 0x6f, 0x70, 0x74, 0x73, 0x22, 0xe7, 0x05, 0x0a, 0x19, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, - 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x12, 0x23, 0x0a, - 0x0d, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x18, 0x05, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x72, 0x6f, 0x6e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, - 0x72, 0x73, 0x12, 0x49, 0x0a, 0x12, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x5f, - 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x11, 0x73, 0x63, 0x68, 0x65, - 0x64, 0x75, 0x6c, 0x65, 0x64, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x12, 0x2a, 0x0a, - 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4a, 0x6f, 0x62, 0x4f, - 0x70, 0x74, 0x73, 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x12, 0x3a, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, - 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x4f, 0x70, 0x74, 0x73, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x2e, 0x0a, 0x10, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, - 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x48, - 0x00, 0x52, 0x0f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, - 0x75, 0x74, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x0a, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, - 0x70, 0x75, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x09, 0x63, 0x72, 0x6f, - 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x88, 0x01, 0x01, 0x12, 0x41, 0x0a, 0x0e, 0x6f, 0x6e, 0x5f, - 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x6a, 0x6f, 0x62, 0x18, 0x0b, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x16, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x4a, 0x6f, 0x62, 0x4f, 0x70, 0x74, 0x73, 0x48, 0x02, 0x52, 0x0c, 0x6f, 0x6e, 0x46, - 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x4a, 0x6f, 0x62, 0x88, 0x01, 0x01, 0x12, 0x2c, 0x0a, 0x06, - 0x73, 0x74, 0x69, 0x63, 0x6b, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0f, 0x2e, 0x53, - 0x74, 0x69, 0x63, 0x6b, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x48, 0x03, 0x52, - 0x06, 0x73, 0x74, 0x69, 0x63, 0x6b, 0x79, 0x88, 0x01, 0x01, 0x12, 0x26, 0x0a, 0x04, 0x6b, 0x69, - 0x6e, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, - 0x6c, 0x6f, 0x77, 0x4b, 0x69, 0x6e, 0x64, 0x48, 0x04, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x88, - 0x01, 0x01, 0x12, 0x2e, 0x0a, 0x10, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x72, - 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x05, 0x48, 0x05, 0x52, 0x0f, - 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x88, - 0x01, 0x01, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x63, 0x72, 0x6f, 0x6e, - 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x6f, 0x6e, 0x5f, 0x66, 0x61, - 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x6a, 0x6f, 0x62, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, - 0x69, 0x63, 0x6b, 0x79, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x42, 0x13, 0x0a, - 0x11, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, - 0x74, 0x79, 0x22, 0xfc, 0x01, 0x0a, 0x17, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x43, - 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x4f, 0x70, 0x74, 0x73, 0x12, 0x1b, - 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, - 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x08, 0x6d, - 0x61, 0x78, 0x5f, 0x72, 0x75, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x48, 0x01, 0x52, - 0x07, 0x6d, 0x61, 0x78, 0x52, 0x75, 0x6e, 0x73, 0x88, 0x01, 0x01, 0x12, 0x45, 0x0a, 0x0e, 0x6c, - 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x48, 0x02, - 0x52, 0x0d, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x88, - 0x01, 0x01, 0x12, 0x23, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x75, 0x6e, 0x73, 0x42, - 0x11, 0x0a, 0x0f, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, - 0x67, 0x79, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x22, 0x82, 0x01, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, - 0x66, 0x6c, 0x6f, 0x77, 0x4a, 0x6f, 0x62, 0x4f, 0x70, 0x74, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, - 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, - 0x77, 0x53, 0x74, 0x65, 0x70, 0x4f, 0x70, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, - 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x97, 0x02, 0x0a, 0x13, 0x44, 0x65, 0x73, 0x69, 0x72, - 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x20, - 0x0a, 0x09, 0x73, 0x74, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x48, 0x00, 0x52, 0x08, 0x73, 0x74, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x88, 0x01, 0x01, - 0x12, 0x20, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x05, 0x48, 0x01, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x88, - 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x48, 0x02, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, - 0x88, 0x01, 0x01, 0x12, 0x3b, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, - 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, - 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x48, - 0x03, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, - 0x12, 0x1b, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, - 0x48, 0x04, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, - 0x0a, 0x5f, 0x73, 0x74, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, - 0x69, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x72, 0x65, - 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, - 0x72, 0x61, 0x74, 0x6f, 0x72, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, - 0x22, 0xbe, 0x04, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, - 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x65, 0x70, 0x4f, 0x70, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x72, - 0x65, 0x61, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x72, 0x65, 0x61, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x16, - 0x0a, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, - 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, - 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x12, 0x18, 0x0a, - 0x07, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, - 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x12, 0x35, 0x0a, 0x0b, 0x72, 0x61, 0x74, 0x65, 0x5f, - 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, - 0x69, 0x74, 0x52, 0x0a, 0x72, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x12, 0x4e, - 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, - 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, - 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x65, 0x70, 0x4f, 0x70, 0x74, 0x73, 0x2e, 0x57, - 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x2a, - 0x0a, 0x0e, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, - 0x18, 0x0a, 0x20, 0x01, 0x28, 0x02, 0x48, 0x00, 0x52, 0x0d, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, - 0x66, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x33, 0x0a, 0x13, 0x62, 0x61, - 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, - 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x48, 0x01, 0x52, 0x11, 0x62, 0x61, 0x63, 0x6b, 0x6f, - 0x66, 0x66, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x88, 0x01, 0x01, 0x1a, - 0x55, 0x0a, 0x11, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x44, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x57, - 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x6f, - 0x66, 0x66, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x62, 0x61, - 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, - 0x73, 0x22, 0xb5, 0x02, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, - 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x19, 0x0a, 0x05, 0x75, - 0x6e, 0x69, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, 0x05, 0x75, 0x6e, - 0x69, 0x74, 0x73, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x65, 0x78, - 0x70, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x45, - 0x78, 0x70, 0x72, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x0a, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x5f, - 0x65, 0x78, 0x70, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x09, 0x75, 0x6e, - 0x69, 0x74, 0x73, 0x45, 0x78, 0x70, 0x72, 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a, 0x11, 0x6c, 0x69, - 0x6d, 0x69, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x0f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x73, 0x45, 0x78, 0x70, 0x72, 0x88, 0x01, 0x01, 0x12, 0x33, 0x0a, 0x08, 0x64, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, - 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x48, 0x04, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, - 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6b, - 0x65, 0x79, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x75, 0x6e, 0x69, 0x74, - 0x73, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, - 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x42, 0x0b, 0x0a, 0x09, - 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x16, 0x0a, 0x14, 0x4c, 0x69, 0x73, - 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x22, 0xf2, 0x03, 0x0a, 0x17, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x57, 0x6f, - 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x38, 0x0a, 0x09, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x09, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x69, - 0x6e, 0x70, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, - 0x74, 0x12, 0x20, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, - 0x88, 0x01, 0x01, 0x12, 0x41, 0x0a, 0x1b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x61, - 0x73, 0x6b, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, - 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x17, 0x70, 0x61, 0x72, 0x65, - 0x6e, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x75, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, - 0x6c, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x0b, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, - 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x48, 0x02, 0x52, 0x0a, 0x63, - 0x68, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, - 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x48, - 0x03, 0x52, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x4b, 0x65, 0x79, 0x88, 0x01, 0x01, 0x12, 0x34, - 0x0a, 0x13, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x6d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x48, 0x04, 0x52, 0x12, 0x61, - 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x48, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, - 0x74, 0x79, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, - 0x5f, 0x69, 0x64, 0x42, 0x1e, 0x0a, 0x1c, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x74, - 0x61, 0x73, 0x6b, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x5f, 0x69, 0x64, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x6e, - 0x64, 0x65, 0x78, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x6b, 0x65, - 0x79, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, - 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x70, 0x72, - 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x5e, 0x0a, 0x11, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, - 0x6c, 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x74, - 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x72, 0x69, - 0x67, 0x67, 0x65, 0x72, 0x41, 0x74, 0x22, 0xad, 0x02, 0x0a, 0x0f, 0x57, 0x6f, 0x72, 0x6b, 0x66, - 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, - 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, - 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x72, - 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, - 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x64, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x49, - 0x64, 0x12, 0x43, 0x0a, 0x13, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x5f, 0x77, - 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, - 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x52, 0x12, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x57, 0x6f, 0x72, - 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x22, 0x53, 0x0a, 0x17, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, - 0x66, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1b, - 0x0a, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x22, 0x49, 0x0a, 0x16, 0x57, - 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x43, 0x72, - 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, - 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x22, 0x53, 0x0a, 0x1a, 0x42, 0x75, 0x6c, 0x6b, 0x54, 0x72, - 0x69, 0x67, 0x67, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x35, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, - 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x22, 0x47, 0x0a, 0x1b, 0x42, - 0x75, 0x6c, 0x6b, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x77, 0x6f, - 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x75, - 0x6e, 0x49, 0x64, 0x73, 0x22, 0xfe, 0x03, 0x0a, 0x16, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, - 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x20, 0x0a, 0x09, 0x70, 0x61, 0x72, - 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, - 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x41, 0x0a, 0x1b, 0x70, - 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x65, - 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x48, 0x01, 0x52, 0x17, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x75, - 0x6e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x24, - 0x0a, 0x0b, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x05, 0x48, 0x02, 0x52, 0x0a, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, - 0x78, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x6b, 0x65, - 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, - 0x4b, 0x65, 0x79, 0x88, 0x01, 0x01, 0x12, 0x34, 0x0a, 0x13, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x09, 0x48, 0x04, 0x52, 0x12, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, - 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a, 0x11, - 0x64, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, - 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x48, 0x05, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x69, 0x72, - 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, - 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x48, - 0x06, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x88, 0x01, 0x01, 0x42, 0x0c, - 0x0a, 0x0a, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x42, 0x1e, 0x0a, 0x1c, - 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x72, 0x75, 0x6e, - 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x42, 0x0e, 0x0a, 0x0c, - 0x5f, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x42, 0x0c, 0x0a, 0x0a, - 0x5f, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x61, - 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x64, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x77, - 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x70, 0x72, 0x69, - 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x41, 0x0a, 0x17, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, +var file_workflows_workflows_proto_rawDesc = []byte{ + 0x0a, 0x19, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x2f, 0x77, 0x6f, 0x72, 0x6b, + 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x76, 0x31, + 0x2f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2f, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x44, 0x0a, 0x12, 0x50, 0x75, 0x74, 0x57, 0x6f, 0x72, 0x6b, + 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6f, + 0x70, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x4f, 0x70, 0x74, 0x73, 0x52, 0x04, 0x6f, 0x70, 0x74, 0x73, 0x22, 0xe7, 0x05, 0x0a, 0x19, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x76, 0x65, + 0x6e, 0x74, 0x5f, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, + 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, + 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x72, 0x6f, 0x6e, 0x54, 0x72, 0x69, + 0x67, 0x67, 0x65, 0x72, 0x73, 0x12, 0x49, 0x0a, 0x12, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, + 0x65, 0x64, 0x5f, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x11, 0x73, + 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, + 0x12, 0x2a, 0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4a, + 0x6f, 0x62, 0x4f, 0x70, 0x74, 0x73, 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x12, 0x3a, 0x0a, 0x0b, + 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x18, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x43, 0x6f, 0x6e, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x4f, 0x70, 0x74, 0x73, 0x52, 0x0b, 0x63, 0x6f, 0x6e, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x2e, 0x0a, 0x10, 0x73, 0x63, 0x68, 0x65, + 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x00, 0x52, 0x0f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x69, + 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x0a, 0x63, 0x72, 0x6f, 0x6e, + 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x09, + 0x63, 0x72, 0x6f, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x88, 0x01, 0x01, 0x12, 0x41, 0x0a, 0x0e, + 0x6f, 0x6e, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x6a, 0x6f, 0x62, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, + 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4a, 0x6f, 0x62, 0x4f, 0x70, 0x74, 0x73, 0x48, 0x02, 0x52, 0x0c, + 0x6f, 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x4a, 0x6f, 0x62, 0x88, 0x01, 0x01, 0x12, + 0x2c, 0x0a, 0x06, 0x73, 0x74, 0x69, 0x63, 0x6b, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x0f, 0x2e, 0x53, 0x74, 0x69, 0x63, 0x6b, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, + 0x48, 0x03, 0x52, 0x06, 0x73, 0x74, 0x69, 0x63, 0x6b, 0x79, 0x88, 0x01, 0x01, 0x12, 0x26, 0x0a, + 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x57, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4b, 0x69, 0x6e, 0x64, 0x48, 0x04, 0x52, 0x04, 0x6b, 0x69, + 0x6e, 0x64, 0x88, 0x01, 0x01, 0x12, 0x2e, 0x0a, 0x10, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, + 0x5f, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x05, 0x48, + 0x05, 0x52, 0x0f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, + 0x74, 0x79, 0x88, 0x01, 0x01, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, + 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x63, + 0x72, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x6f, 0x6e, + 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x6a, 0x6f, 0x62, 0x42, 0x09, 0x0a, 0x07, + 0x5f, 0x73, 0x74, 0x69, 0x63, 0x6b, 0x79, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6b, 0x69, 0x6e, 0x64, + 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x72, 0x69, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0xfc, 0x01, 0x0a, 0x17, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, + 0x6f, 0x77, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x4f, 0x70, 0x74, + 0x73, 0x12, 0x1b, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x00, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x1e, + 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x75, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, + 0x48, 0x01, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x52, 0x75, 0x6e, 0x73, 0x88, 0x01, 0x01, 0x12, 0x45, + 0x0a, 0x0e, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x63, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, + 0x79, 0x48, 0x02, 0x52, 0x0d, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, + 0x67, 0x79, 0x88, 0x01, 0x01, 0x12, 0x23, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x0a, 0x65, 0x78, 0x70, + 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x75, + 0x6e, 0x73, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x73, 0x74, 0x72, + 0x61, 0x74, 0x65, 0x67, 0x79, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x82, 0x01, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, + 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4a, 0x6f, 0x62, 0x4f, 0x70, 0x74, 0x73, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x18, 0x04, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, + 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x65, 0x70, 0x4f, 0x70, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, + 0x65, 0x70, 0x73, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x97, 0x02, 0x0a, 0x13, 0x44, 0x65, + 0x73, 0x69, 0x72, 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, + 0x73, 0x12, 0x20, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x73, 0x74, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x48, 0x01, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x48, 0x02, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, + 0x72, 0x65, 0x64, 0x88, 0x01, 0x01, 0x12, 0x3b, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x57, 0x6f, 0x72, + 0x6b, 0x65, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x48, 0x03, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x05, 0x48, 0x04, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, + 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x73, 0x74, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x0c, + 0x0a, 0x0a, 0x5f, 0x69, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x0b, 0x0a, 0x09, + 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x77, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x22, 0xbe, 0x04, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x65, 0x70, 0x4f, 0x70, 0x74, 0x73, 0x12, 0x1f, + 0x0a, 0x0b, 0x72, 0x65, 0x61, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x61, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x64, 0x12, + 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, + 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, + 0x74, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x72, + 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x72, 0x65, + 0x6e, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, + 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x07, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x12, 0x35, 0x0a, 0x0b, 0x72, 0x61, + 0x74, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x14, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, 0x52, 0x61, 0x74, 0x65, + 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x0a, 0x72, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, + 0x73, 0x12, 0x4e, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x6c, 0x61, 0x62, 0x65, + 0x6c, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x65, 0x70, 0x4f, 0x70, 0x74, + 0x73, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, + 0x73, 0x12, 0x2a, 0x0a, 0x0e, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x5f, 0x66, 0x61, 0x63, + 0x74, 0x6f, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x02, 0x48, 0x00, 0x52, 0x0d, 0x62, 0x61, 0x63, + 0x6b, 0x6f, 0x66, 0x66, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x33, 0x0a, + 0x13, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x48, 0x01, 0x52, 0x11, 0x62, 0x61, + 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x88, + 0x01, 0x01, 0x1a, 0x55, 0x0a, 0x11, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x61, 0x62, 0x65, + 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x44, 0x65, 0x73, 0x69, 0x72, + 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x62, 0x61, + 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x42, 0x16, 0x0a, 0x14, + 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x73, 0x22, 0xb5, 0x02, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, + 0x74, 0x65, 0x70, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x19, + 0x0a, 0x05, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, + 0x05, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x08, 0x6b, 0x65, 0x79, + 0x5f, 0x65, 0x78, 0x70, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x07, 0x6b, + 0x65, 0x79, 0x45, 0x78, 0x70, 0x72, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x0a, 0x75, 0x6e, 0x69, + 0x74, 0x73, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, + 0x09, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x45, 0x78, 0x70, 0x72, 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a, + 0x11, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x5f, 0x65, 0x78, + 0x70, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x0f, 0x6c, 0x69, 0x6d, 0x69, + 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x45, 0x78, 0x70, 0x72, 0x88, 0x01, 0x01, 0x12, 0x33, + 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x12, 0x2e, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x44, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x04, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x42, 0x0b, 0x0a, + 0x09, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x75, + 0x6e, 0x69, 0x74, 0x73, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x6c, 0x69, + 0x6d, 0x69, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x42, + 0x0b, 0x0a, 0x09, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x16, 0x0a, 0x14, + 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x22, 0xf2, 0x03, 0x0a, 0x17, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, + 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x14, + 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, + 0x6e, 0x70, 0x75, 0x74, 0x12, 0x20, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, + 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, + 0x74, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x41, 0x0a, 0x1b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, + 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x17, 0x70, + 0x61, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x75, 0x6e, 0x45, 0x78, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x0b, 0x63, 0x68, 0x69, + 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x48, 0x02, + 0x52, 0x0a, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x88, 0x01, 0x01, 0x12, + 0x20, 0x0a, 0x09, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x03, 0x52, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x4b, 0x65, 0x79, 0x88, 0x01, + 0x01, 0x12, 0x34, 0x0a, 0x13, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x48, 0x04, + 0x52, 0x12, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, + 0x69, 0x74, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x48, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x70, 0x61, 0x72, + 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x42, 0x1e, 0x0a, 0x1c, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, + 0x74, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x63, 0x68, 0x69, 0x6c, 0x64, + 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x63, 0x68, 0x69, 0x6c, 0x64, + 0x5f, 0x6b, 0x65, 0x79, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x61, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x0b, 0x0a, 0x09, + 0x5f, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x5e, 0x0a, 0x11, 0x53, 0x63, 0x68, + 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x39, + 0x0a, 0x0a, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, + 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x41, 0x74, 0x22, 0xad, 0x02, 0x0a, 0x0f, 0x57, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x39, 0x0a, + 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x64, 0x41, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, + 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6f, 0x72, + 0x64, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, + 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, + 0x6f, 0x77, 0x49, 0x64, 0x12, 0x43, 0x0a, 0x13, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, + 0x64, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x12, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x57, 0x6f, 0x72, + 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x12, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, + 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x22, 0x53, 0x0a, 0x17, 0x57, 0x6f, 0x72, + 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x66, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, + 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x22, 0x49, + 0x0a, 0x16, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, + 0x72, 0x43, 0x72, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, + 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x72, + 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x22, 0x56, 0x0a, 0x1a, 0x42, 0x75, 0x6c, + 0x6b, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x66, + 0x6c, 0x6f, 0x77, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x76, 0x31, 0x2e, + 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, + 0x73, 0x22, 0x47, 0x0a, 0x1b, 0x42, 0x75, 0x6c, 0x6b, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x26, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x72, 0x75, 0x6e, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x66, - 0x6c, 0x6f, 0x77, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x22, 0x6d, 0x0a, 0x13, 0x50, 0x75, 0x74, 0x52, - 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x2e, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x52, 0x61, 0x74, 0x65, - 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x64, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x16, 0x0a, 0x14, 0x50, 0x75, 0x74, 0x52, 0x61, - 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, - 0x24, 0x0a, 0x0e, 0x53, 0x74, 0x69, 0x63, 0x6b, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, - 0x79, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x4f, 0x46, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x48, - 0x41, 0x52, 0x44, 0x10, 0x01, 0x2a, 0x32, 0x0a, 0x0c, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, - 0x77, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x55, 0x4e, 0x43, 0x54, 0x49, 0x4f, - 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x55, 0x52, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x01, - 0x12, 0x07, 0x0a, 0x03, 0x44, 0x41, 0x47, 0x10, 0x02, 0x2a, 0x7f, 0x0a, 0x18, 0x43, 0x6f, 0x6e, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x53, 0x74, 0x72, - 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x16, 0x0a, 0x12, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x5f, - 0x49, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x00, 0x12, 0x0f, 0x0a, - 0x0b, 0x44, 0x52, 0x4f, 0x50, 0x5f, 0x4e, 0x45, 0x57, 0x45, 0x53, 0x54, 0x10, 0x01, 0x12, 0x10, - 0x0a, 0x0c, 0x51, 0x55, 0x45, 0x55, 0x45, 0x5f, 0x4e, 0x45, 0x57, 0x45, 0x53, 0x54, 0x10, 0x02, - 0x12, 0x15, 0x0a, 0x11, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, - 0x52, 0x4f, 0x42, 0x49, 0x4e, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x41, 0x4e, 0x43, 0x45, - 0x4c, 0x5f, 0x4e, 0x45, 0x57, 0x45, 0x53, 0x54, 0x10, 0x04, 0x2a, 0x85, 0x01, 0x0a, 0x15, 0x57, - 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, - 0x61, 0x74, 0x6f, 0x72, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x51, 0x55, 0x41, 0x4c, 0x10, 0x00, 0x12, - 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x54, 0x5f, 0x45, 0x51, 0x55, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x10, - 0x0a, 0x0c, 0x47, 0x52, 0x45, 0x41, 0x54, 0x45, 0x52, 0x5f, 0x54, 0x48, 0x41, 0x4e, 0x10, 0x02, - 0x12, 0x19, 0x0a, 0x15, 0x47, 0x52, 0x45, 0x41, 0x54, 0x45, 0x52, 0x5f, 0x54, 0x48, 0x41, 0x4e, - 0x5f, 0x4f, 0x52, 0x5f, 0x45, 0x51, 0x55, 0x41, 0x4c, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x4c, - 0x45, 0x53, 0x53, 0x5f, 0x54, 0x48, 0x41, 0x4e, 0x10, 0x04, 0x12, 0x16, 0x0a, 0x12, 0x4c, 0x45, - 0x53, 0x53, 0x5f, 0x54, 0x48, 0x41, 0x4e, 0x5f, 0x4f, 0x52, 0x5f, 0x45, 0x51, 0x55, 0x41, 0x4c, - 0x10, 0x05, 0x2a, 0x5d, 0x0a, 0x11, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x44, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x45, 0x43, 0x4f, 0x4e, - 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x49, 0x4e, 0x55, 0x54, 0x45, 0x10, 0x01, 0x12, - 0x08, 0x0a, 0x04, 0x48, 0x4f, 0x55, 0x52, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x44, 0x41, 0x59, - 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x45, 0x45, 0x4b, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, - 0x4d, 0x4f, 0x4e, 0x54, 0x48, 0x10, 0x05, 0x12, 0x08, 0x0a, 0x04, 0x59, 0x45, 0x41, 0x52, 0x10, - 0x06, 0x32, 0xdc, 0x02, 0x0a, 0x0f, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x0b, 0x50, 0x75, 0x74, 0x57, 0x6f, 0x72, 0x6b, - 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x13, 0x2e, 0x50, 0x75, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x57, 0x6f, 0x72, 0x6b, - 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x10, 0x53, - 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, - 0x18, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x57, 0x6f, 0x72, 0x6b, - 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x44, 0x0a, 0x0f, 0x54, - 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x17, - 0x2e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, - 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x50, 0x0a, 0x13, 0x42, 0x75, 0x6c, 0x6b, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, - 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x1b, 0x2e, 0x42, 0x75, 0x6c, 0x6b, 0x54, - 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x42, 0x75, 0x6c, 0x6b, 0x54, 0x72, 0x69, 0x67, - 0x67, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x0c, 0x50, 0x75, 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, - 0x6d, 0x69, 0x74, 0x12, 0x14, 0x2e, 0x50, 0x75, 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, - 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x50, 0x75, 0x74, 0x52, - 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x42, 0x42, 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, - 0x61, 0x74, 0x63, 0x68, 0x65, 0x74, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x68, 0x61, 0x74, 0x63, 0x68, - 0x65, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x2f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x61, 0x63, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x12, 0x28, 0x0a, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x72, 0x75, 0x6e, + 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, + 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x73, 0x22, 0x41, 0x0a, 0x17, 0x54, 0x72, + 0x69, 0x67, 0x67, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, + 0x77, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, + 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x22, 0x6d, 0x0a, + 0x13, 0x50, 0x75, 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x2e, 0x0a, 0x08, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, + 0x2e, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x16, 0x0a, 0x14, + 0x50, 0x75, 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x24, 0x0a, 0x0e, 0x53, 0x74, 0x69, 0x63, 0x6b, 0x79, 0x53, 0x74, + 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x4f, 0x46, 0x54, 0x10, 0x00, + 0x12, 0x08, 0x0a, 0x04, 0x48, 0x41, 0x52, 0x44, 0x10, 0x01, 0x2a, 0x32, 0x0a, 0x0c, 0x57, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x55, + 0x4e, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x55, 0x52, 0x41, + 0x42, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x44, 0x41, 0x47, 0x10, 0x02, 0x2a, 0x7f, + 0x0a, 0x18, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x4c, 0x69, 0x6d, + 0x69, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x16, 0x0a, 0x12, 0x43, 0x41, + 0x4e, 0x43, 0x45, 0x4c, 0x5f, 0x49, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, + 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x44, 0x52, 0x4f, 0x50, 0x5f, 0x4e, 0x45, 0x57, 0x45, 0x53, + 0x54, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x51, 0x55, 0x45, 0x55, 0x45, 0x5f, 0x4e, 0x45, 0x57, + 0x45, 0x53, 0x54, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x52, + 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x52, 0x4f, 0x42, 0x49, 0x4e, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, + 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x5f, 0x4e, 0x45, 0x57, 0x45, 0x53, 0x54, 0x10, 0x04, 0x2a, + 0x85, 0x01, 0x0a, 0x15, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x43, + 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x51, 0x55, + 0x41, 0x4c, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x54, 0x5f, 0x45, 0x51, 0x55, 0x41, + 0x4c, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x47, 0x52, 0x45, 0x41, 0x54, 0x45, 0x52, 0x5f, 0x54, + 0x48, 0x41, 0x4e, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x47, 0x52, 0x45, 0x41, 0x54, 0x45, 0x52, + 0x5f, 0x54, 0x48, 0x41, 0x4e, 0x5f, 0x4f, 0x52, 0x5f, 0x45, 0x51, 0x55, 0x41, 0x4c, 0x10, 0x03, + 0x12, 0x0d, 0x0a, 0x09, 0x4c, 0x45, 0x53, 0x53, 0x5f, 0x54, 0x48, 0x41, 0x4e, 0x10, 0x04, 0x12, + 0x16, 0x0a, 0x12, 0x4c, 0x45, 0x53, 0x53, 0x5f, 0x54, 0x48, 0x41, 0x4e, 0x5f, 0x4f, 0x52, 0x5f, + 0x45, 0x51, 0x55, 0x41, 0x4c, 0x10, 0x05, 0x2a, 0x5d, 0x0a, 0x11, 0x52, 0x61, 0x74, 0x65, 0x4c, + 0x69, 0x6d, 0x69, 0x74, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, + 0x53, 0x45, 0x43, 0x4f, 0x4e, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x49, 0x4e, 0x55, + 0x54, 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x4f, 0x55, 0x52, 0x10, 0x02, 0x12, 0x07, + 0x0a, 0x03, 0x44, 0x41, 0x59, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x45, 0x45, 0x4b, 0x10, + 0x04, 0x12, 0x09, 0x0a, 0x05, 0x4d, 0x4f, 0x4e, 0x54, 0x48, 0x10, 0x05, 0x12, 0x08, 0x0a, 0x04, + 0x59, 0x45, 0x41, 0x52, 0x10, 0x06, 0x32, 0xdf, 0x02, 0x0a, 0x0f, 0x57, 0x6f, 0x72, 0x6b, 0x66, + 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x0b, 0x50, 0x75, + 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x13, 0x2e, 0x50, 0x75, 0x74, 0x57, + 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, + 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x3e, 0x0a, 0x10, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x57, 0x6f, 0x72, 0x6b, + 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x18, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x57, + 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, + 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x47, 0x0a, 0x0f, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, + 0x6c, 0x6f, 0x77, 0x12, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, + 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x18, 0x2e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, + 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x13, 0x42, 0x75, 0x6c, + 0x6b, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, + 0x12, 0x1b, 0x2e, 0x42, 0x75, 0x6c, 0x6b, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x57, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, + 0x42, 0x75, 0x6c, 0x6b, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, + 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x0c, 0x50, + 0x75, 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x14, 0x2e, 0x50, 0x75, + 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x15, 0x2e, 0x50, 0x75, 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x42, 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x74, 0x63, 0x68, 0x65, 0x74, 0x2d, 0x64, + 0x65, 0x76, 0x2f, 0x68, 0x61, 0x74, 0x63, 0x68, 0x65, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x61, 0x64, 0x6d, + 0x69, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( - file_workflows_proto_rawDescOnce sync.Once - file_workflows_proto_rawDescData = file_workflows_proto_rawDesc + file_workflows_workflows_proto_rawDescOnce sync.Once + file_workflows_workflows_proto_rawDescData = file_workflows_workflows_proto_rawDesc ) -func file_workflows_proto_rawDescGZIP() []byte { - file_workflows_proto_rawDescOnce.Do(func() { - file_workflows_proto_rawDescData = protoimpl.X.CompressGZIP(file_workflows_proto_rawDescData) +func file_workflows_workflows_proto_rawDescGZIP() []byte { + file_workflows_workflows_proto_rawDescOnce.Do(func() { + file_workflows_workflows_proto_rawDescData = protoimpl.X.CompressGZIP(file_workflows_workflows_proto_rawDescData) }) - return file_workflows_proto_rawDescData + return file_workflows_workflows_proto_rawDescData } -var file_workflows_proto_enumTypes = make([]protoimpl.EnumInfo, 5) -var file_workflows_proto_msgTypes = make([]protoimpl.MessageInfo, 20) -var file_workflows_proto_goTypes = []interface{}{ +var file_workflows_workflows_proto_enumTypes = make([]protoimpl.EnumInfo, 5) +var file_workflows_workflows_proto_msgTypes = make([]protoimpl.MessageInfo, 19) +var file_workflows_workflows_proto_goTypes = []interface{}{ (StickyStrategy)(0), // 0: StickyStrategy (WorkflowKind)(0), // 1: WorkflowKind (ConcurrencyLimitStrategy)(0), // 2: ConcurrencyLimitStrategy @@ -2091,16 +1939,16 @@ var file_workflows_proto_goTypes = []interface{}{ (*WorkflowTriggerCronRef)(nil), // 17: WorkflowTriggerCronRef (*BulkTriggerWorkflowRequest)(nil), // 18: BulkTriggerWorkflowRequest (*BulkTriggerWorkflowResponse)(nil), // 19: BulkTriggerWorkflowResponse - (*TriggerWorkflowRequest)(nil), // 20: TriggerWorkflowRequest - (*TriggerWorkflowResponse)(nil), // 21: TriggerWorkflowResponse - (*PutRateLimitRequest)(nil), // 22: PutRateLimitRequest - (*PutRateLimitResponse)(nil), // 23: PutRateLimitResponse - nil, // 24: CreateWorkflowStepOpts.WorkerLabelsEntry - (*timestamppb.Timestamp)(nil), // 25: google.protobuf.Timestamp + (*TriggerWorkflowResponse)(nil), // 20: TriggerWorkflowResponse + (*PutRateLimitRequest)(nil), // 21: PutRateLimitRequest + (*PutRateLimitResponse)(nil), // 22: PutRateLimitResponse + nil, // 23: CreateWorkflowStepOpts.WorkerLabelsEntry + (*timestamppb.Timestamp)(nil), // 24: google.protobuf.Timestamp + (*v1.TriggerWorkflowRequest)(nil), // 25: v1.TriggerWorkflowRequest } -var file_workflows_proto_depIdxs = []int32{ +var file_workflows_workflows_proto_depIdxs = []int32{ 6, // 0: PutWorkflowRequest.opts:type_name -> CreateWorkflowVersionOpts - 25, // 1: CreateWorkflowVersionOpts.scheduled_triggers:type_name -> google.protobuf.Timestamp + 24, // 1: CreateWorkflowVersionOpts.scheduled_triggers:type_name -> google.protobuf.Timestamp 8, // 2: CreateWorkflowVersionOpts.jobs:type_name -> CreateWorkflowJobOpts 7, // 3: CreateWorkflowVersionOpts.concurrency:type_name -> WorkflowConcurrencyOpts 8, // 4: CreateWorkflowVersionOpts.on_failure_job:type_name -> CreateWorkflowJobOpts @@ -2110,26 +1958,26 @@ var file_workflows_proto_depIdxs = []int32{ 10, // 8: CreateWorkflowJobOpts.steps:type_name -> CreateWorkflowStepOpts 3, // 9: DesiredWorkerLabels.comparator:type_name -> WorkerLabelComparator 11, // 10: CreateWorkflowStepOpts.rate_limits:type_name -> CreateStepRateLimit - 24, // 11: CreateWorkflowStepOpts.worker_labels:type_name -> CreateWorkflowStepOpts.WorkerLabelsEntry + 23, // 11: CreateWorkflowStepOpts.worker_labels:type_name -> CreateWorkflowStepOpts.WorkerLabelsEntry 4, // 12: CreateStepRateLimit.duration:type_name -> RateLimitDuration - 25, // 13: ScheduleWorkflowRequest.schedules:type_name -> google.protobuf.Timestamp - 25, // 14: ScheduledWorkflow.trigger_at:type_name -> google.protobuf.Timestamp - 25, // 15: WorkflowVersion.created_at:type_name -> google.protobuf.Timestamp - 25, // 16: WorkflowVersion.updated_at:type_name -> google.protobuf.Timestamp + 24, // 13: ScheduleWorkflowRequest.schedules:type_name -> google.protobuf.Timestamp + 24, // 14: ScheduledWorkflow.trigger_at:type_name -> google.protobuf.Timestamp + 24, // 15: WorkflowVersion.created_at:type_name -> google.protobuf.Timestamp + 24, // 16: WorkflowVersion.updated_at:type_name -> google.protobuf.Timestamp 14, // 17: WorkflowVersion.scheduled_workflows:type_name -> ScheduledWorkflow - 20, // 18: BulkTriggerWorkflowRequest.workflows:type_name -> TriggerWorkflowRequest + 25, // 18: BulkTriggerWorkflowRequest.workflows:type_name -> v1.TriggerWorkflowRequest 4, // 19: PutRateLimitRequest.duration:type_name -> RateLimitDuration 9, // 20: CreateWorkflowStepOpts.WorkerLabelsEntry.value:type_name -> DesiredWorkerLabels 5, // 21: WorkflowService.PutWorkflow:input_type -> PutWorkflowRequest 13, // 22: WorkflowService.ScheduleWorkflow:input_type -> ScheduleWorkflowRequest - 20, // 23: WorkflowService.TriggerWorkflow:input_type -> TriggerWorkflowRequest + 25, // 23: WorkflowService.TriggerWorkflow:input_type -> v1.TriggerWorkflowRequest 18, // 24: WorkflowService.BulkTriggerWorkflow:input_type -> BulkTriggerWorkflowRequest - 22, // 25: WorkflowService.PutRateLimit:input_type -> PutRateLimitRequest + 21, // 25: WorkflowService.PutRateLimit:input_type -> PutRateLimitRequest 15, // 26: WorkflowService.PutWorkflow:output_type -> WorkflowVersion 15, // 27: WorkflowService.ScheduleWorkflow:output_type -> WorkflowVersion - 21, // 28: WorkflowService.TriggerWorkflow:output_type -> TriggerWorkflowResponse + 20, // 28: WorkflowService.TriggerWorkflow:output_type -> TriggerWorkflowResponse 19, // 29: WorkflowService.BulkTriggerWorkflow:output_type -> BulkTriggerWorkflowResponse - 23, // 30: WorkflowService.PutRateLimit:output_type -> PutRateLimitResponse + 22, // 30: WorkflowService.PutRateLimit:output_type -> PutRateLimitResponse 26, // [26:31] is the sub-list for method output_type 21, // [21:26] is the sub-list for method input_type 21, // [21:21] is the sub-list for extension type_name @@ -2137,13 +1985,13 @@ var file_workflows_proto_depIdxs = []int32{ 0, // [0:21] is the sub-list for field type_name } -func init() { file_workflows_proto_init() } -func file_workflows_proto_init() { - if File_workflows_proto != nil { +func init() { file_workflows_workflows_proto_init() } +func file_workflows_workflows_proto_init() { + if File_workflows_workflows_proto != nil { return } if !protoimpl.UnsafeEnabled { - file_workflows_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_workflows_workflows_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PutWorkflowRequest); i { case 0: return &v.state @@ -2155,7 +2003,7 @@ func file_workflows_proto_init() { return nil } } - file_workflows_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + file_workflows_workflows_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CreateWorkflowVersionOpts); i { case 0: return &v.state @@ -2167,7 +2015,7 @@ func file_workflows_proto_init() { return nil } } - file_workflows_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + file_workflows_workflows_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WorkflowConcurrencyOpts); i { case 0: return &v.state @@ -2179,7 +2027,7 @@ func file_workflows_proto_init() { return nil } } - file_workflows_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + file_workflows_workflows_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CreateWorkflowJobOpts); i { case 0: return &v.state @@ -2191,7 +2039,7 @@ func file_workflows_proto_init() { return nil } } - file_workflows_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + file_workflows_workflows_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DesiredWorkerLabels); i { case 0: return &v.state @@ -2203,7 +2051,7 @@ func file_workflows_proto_init() { return nil } } - file_workflows_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + file_workflows_workflows_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CreateWorkflowStepOpts); i { case 0: return &v.state @@ -2215,7 +2063,7 @@ func file_workflows_proto_init() { return nil } } - file_workflows_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + file_workflows_workflows_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CreateStepRateLimit); i { case 0: return &v.state @@ -2227,7 +2075,7 @@ func file_workflows_proto_init() { return nil } } - file_workflows_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + file_workflows_workflows_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListWorkflowsRequest); i { case 0: return &v.state @@ -2239,7 +2087,7 @@ func file_workflows_proto_init() { return nil } } - file_workflows_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + file_workflows_workflows_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ScheduleWorkflowRequest); i { case 0: return &v.state @@ -2251,7 +2099,7 @@ func file_workflows_proto_init() { return nil } } - file_workflows_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + file_workflows_workflows_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ScheduledWorkflow); i { case 0: return &v.state @@ -2263,7 +2111,7 @@ func file_workflows_proto_init() { return nil } } - file_workflows_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + file_workflows_workflows_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WorkflowVersion); i { case 0: return &v.state @@ -2275,7 +2123,7 @@ func file_workflows_proto_init() { return nil } } - file_workflows_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + file_workflows_workflows_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WorkflowTriggerEventRef); i { case 0: return &v.state @@ -2287,7 +2135,7 @@ func file_workflows_proto_init() { return nil } } - file_workflows_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + file_workflows_workflows_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WorkflowTriggerCronRef); i { case 0: return &v.state @@ -2299,7 +2147,7 @@ func file_workflows_proto_init() { return nil } } - file_workflows_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + file_workflows_workflows_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*BulkTriggerWorkflowRequest); i { case 0: return &v.state @@ -2311,7 +2159,7 @@ func file_workflows_proto_init() { return nil } } - file_workflows_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + file_workflows_workflows_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*BulkTriggerWorkflowResponse); i { case 0: return &v.state @@ -2323,19 +2171,7 @@ func file_workflows_proto_init() { return nil } } - file_workflows_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TriggerWorkflowRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_workflows_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + file_workflows_workflows_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TriggerWorkflowResponse); i { case 0: return &v.state @@ -2347,7 +2183,7 @@ func file_workflows_proto_init() { return nil } } - file_workflows_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + file_workflows_workflows_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PutRateLimitRequest); i { case 0: return &v.state @@ -2359,7 +2195,7 @@ func file_workflows_proto_init() { return nil } } - file_workflows_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + file_workflows_workflows_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PutRateLimitResponse); i { case 0: return &v.state @@ -2372,30 +2208,29 @@ func file_workflows_proto_init() { } } } - file_workflows_proto_msgTypes[1].OneofWrappers = []interface{}{} - file_workflows_proto_msgTypes[2].OneofWrappers = []interface{}{} - file_workflows_proto_msgTypes[4].OneofWrappers = []interface{}{} - file_workflows_proto_msgTypes[5].OneofWrappers = []interface{}{} - file_workflows_proto_msgTypes[6].OneofWrappers = []interface{}{} - file_workflows_proto_msgTypes[8].OneofWrappers = []interface{}{} - file_workflows_proto_msgTypes[15].OneofWrappers = []interface{}{} + file_workflows_workflows_proto_msgTypes[1].OneofWrappers = []interface{}{} + file_workflows_workflows_proto_msgTypes[2].OneofWrappers = []interface{}{} + file_workflows_workflows_proto_msgTypes[4].OneofWrappers = []interface{}{} + file_workflows_workflows_proto_msgTypes[5].OneofWrappers = []interface{}{} + file_workflows_workflows_proto_msgTypes[6].OneofWrappers = []interface{}{} + file_workflows_workflows_proto_msgTypes[8].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_workflows_proto_rawDesc, + RawDescriptor: file_workflows_workflows_proto_rawDesc, NumEnums: 5, - NumMessages: 20, + NumMessages: 19, NumExtensions: 0, NumServices: 1, }, - GoTypes: file_workflows_proto_goTypes, - DependencyIndexes: file_workflows_proto_depIdxs, - EnumInfos: file_workflows_proto_enumTypes, - MessageInfos: file_workflows_proto_msgTypes, + GoTypes: file_workflows_workflows_proto_goTypes, + DependencyIndexes: file_workflows_workflows_proto_depIdxs, + EnumInfos: file_workflows_workflows_proto_enumTypes, + MessageInfos: file_workflows_workflows_proto_msgTypes, }.Build() - File_workflows_proto = out.File - file_workflows_proto_rawDesc = nil - file_workflows_proto_goTypes = nil - file_workflows_proto_depIdxs = nil + File_workflows_workflows_proto = out.File + file_workflows_workflows_proto_rawDesc = nil + file_workflows_workflows_proto_goTypes = nil + file_workflows_workflows_proto_depIdxs = nil } diff --git a/internal/services/admin/contracts/workflows_grpc.pb.go b/internal/services/admin/contracts/workflows/workflows_grpc.pb.go similarity index 94% rename from internal/services/admin/contracts/workflows_grpc.pb.go rename to internal/services/admin/contracts/workflows/workflows_grpc.pb.go index 932e7ec85..33dd1d223 100644 --- a/internal/services/admin/contracts/workflows_grpc.pb.go +++ b/internal/services/admin/contracts/workflows/workflows_grpc.pb.go @@ -2,12 +2,13 @@ // versions: // - protoc-gen-go-grpc v1.2.0 // - protoc v5.29.3 -// source: workflows.proto +// source: workflows/workflows.proto package contracts import ( context "context" + v1 "github.com/hatchet-dev/hatchet/internal/services/shared/proto/v1" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" @@ -24,7 +25,7 @@ const _ = grpc.SupportPackageIsVersion7 type WorkflowServiceClient interface { PutWorkflow(ctx context.Context, in *PutWorkflowRequest, opts ...grpc.CallOption) (*WorkflowVersion, error) ScheduleWorkflow(ctx context.Context, in *ScheduleWorkflowRequest, opts ...grpc.CallOption) (*WorkflowVersion, error) - TriggerWorkflow(ctx context.Context, in *TriggerWorkflowRequest, opts ...grpc.CallOption) (*TriggerWorkflowResponse, error) + TriggerWorkflow(ctx context.Context, in *v1.TriggerWorkflowRequest, opts ...grpc.CallOption) (*TriggerWorkflowResponse, error) BulkTriggerWorkflow(ctx context.Context, in *BulkTriggerWorkflowRequest, opts ...grpc.CallOption) (*BulkTriggerWorkflowResponse, error) PutRateLimit(ctx context.Context, in *PutRateLimitRequest, opts ...grpc.CallOption) (*PutRateLimitResponse, error) } @@ -55,7 +56,7 @@ func (c *workflowServiceClient) ScheduleWorkflow(ctx context.Context, in *Schedu return out, nil } -func (c *workflowServiceClient) TriggerWorkflow(ctx context.Context, in *TriggerWorkflowRequest, opts ...grpc.CallOption) (*TriggerWorkflowResponse, error) { +func (c *workflowServiceClient) TriggerWorkflow(ctx context.Context, in *v1.TriggerWorkflowRequest, opts ...grpc.CallOption) (*TriggerWorkflowResponse, error) { out := new(TriggerWorkflowResponse) err := c.cc.Invoke(ctx, "/WorkflowService/TriggerWorkflow", in, out, opts...) if err != nil { @@ -88,7 +89,7 @@ func (c *workflowServiceClient) PutRateLimit(ctx context.Context, in *PutRateLim type WorkflowServiceServer interface { PutWorkflow(context.Context, *PutWorkflowRequest) (*WorkflowVersion, error) ScheduleWorkflow(context.Context, *ScheduleWorkflowRequest) (*WorkflowVersion, error) - TriggerWorkflow(context.Context, *TriggerWorkflowRequest) (*TriggerWorkflowResponse, error) + TriggerWorkflow(context.Context, *v1.TriggerWorkflowRequest) (*TriggerWorkflowResponse, error) BulkTriggerWorkflow(context.Context, *BulkTriggerWorkflowRequest) (*BulkTriggerWorkflowResponse, error) PutRateLimit(context.Context, *PutRateLimitRequest) (*PutRateLimitResponse, error) mustEmbedUnimplementedWorkflowServiceServer() @@ -104,7 +105,7 @@ func (UnimplementedWorkflowServiceServer) PutWorkflow(context.Context, *PutWorkf func (UnimplementedWorkflowServiceServer) ScheduleWorkflow(context.Context, *ScheduleWorkflowRequest) (*WorkflowVersion, error) { return nil, status.Errorf(codes.Unimplemented, "method ScheduleWorkflow not implemented") } -func (UnimplementedWorkflowServiceServer) TriggerWorkflow(context.Context, *TriggerWorkflowRequest) (*TriggerWorkflowResponse, error) { +func (UnimplementedWorkflowServiceServer) TriggerWorkflow(context.Context, *v1.TriggerWorkflowRequest) (*TriggerWorkflowResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method TriggerWorkflow not implemented") } func (UnimplementedWorkflowServiceServer) BulkTriggerWorkflow(context.Context, *BulkTriggerWorkflowRequest) (*BulkTriggerWorkflowResponse, error) { @@ -163,7 +164,7 @@ func _WorkflowService_ScheduleWorkflow_Handler(srv interface{}, ctx context.Cont } func _WorkflowService_TriggerWorkflow_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(TriggerWorkflowRequest) + in := new(v1.TriggerWorkflowRequest) if err := dec(in); err != nil { return nil, err } @@ -175,7 +176,7 @@ func _WorkflowService_TriggerWorkflow_Handler(srv interface{}, ctx context.Conte FullMethod: "/WorkflowService/TriggerWorkflow", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(WorkflowServiceServer).TriggerWorkflow(ctx, req.(*TriggerWorkflowRequest)) + return srv.(WorkflowServiceServer).TriggerWorkflow(ctx, req.(*v1.TriggerWorkflowRequest)) } return interceptor(ctx, in, info, handler) } @@ -245,5 +246,5 @@ var WorkflowService_ServiceDesc = grpc.ServiceDesc{ }, }, Streams: []grpc.StreamDesc{}, - Metadata: "workflows.proto", + Metadata: "workflows/workflows.proto", } diff --git a/internal/services/admin/server.go b/internal/services/admin/server.go index 1209fccbd..2fb15ffae 100644 --- a/internal/services/admin/server.go +++ b/internal/services/admin/server.go @@ -11,16 +11,17 @@ import ( "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" + contracts "github.com/hatchet-dev/hatchet/internal/services/admin/contracts/workflows" + v1contracts "github.com/hatchet-dev/hatchet/internal/services/shared/proto/v1" tasktypes "github.com/hatchet-dev/hatchet/internal/services/shared/tasktypes/v1" "github.com/hatchet-dev/hatchet/internal/msgqueue" - "github.com/hatchet-dev/hatchet/internal/services/admin/contracts" "github.com/hatchet-dev/hatchet/pkg/client/types" v1 "github.com/hatchet-dev/hatchet/pkg/repository" "github.com/hatchet-dev/hatchet/pkg/repository/sqlcv1" ) -func (a *AdminServiceImpl) TriggerWorkflow(ctx context.Context, req *contracts.TriggerWorkflowRequest) (*contracts.TriggerWorkflowResponse, error) { +func (a *AdminServiceImpl) TriggerWorkflow(ctx context.Context, req *v1contracts.TriggerWorkflowRequest) (*contracts.TriggerWorkflowResponse, error) { return a.triggerWorkflowV1(ctx, req) } diff --git a/internal/services/admin/server_v1.go b/internal/services/admin/server_v1.go index e12fe8312..1eba12ced 100644 --- a/internal/services/admin/server_v1.go +++ b/internal/services/admin/server_v1.go @@ -5,15 +5,15 @@ import ( "errors" "fmt" - "go.opentelemetry.io/otel/attribute" "golang.org/x/sync/errgroup" "github.com/google/uuid" "github.com/hatchet-dev/hatchet/internal/datautils" "github.com/hatchet-dev/hatchet/internal/msgqueue" - "github.com/hatchet-dev/hatchet/internal/services/admin/contracts" + contracts "github.com/hatchet-dev/hatchet/internal/services/admin/contracts/workflows" "github.com/hatchet-dev/hatchet/internal/services/controllers/task/trigger" + v1contracts "github.com/hatchet-dev/hatchet/internal/services/shared/proto/v1" tasktypes "github.com/hatchet-dev/hatchet/internal/services/shared/tasktypes/v1" "github.com/hatchet-dev/hatchet/pkg/constants" grpcmiddleware "github.com/hatchet-dev/hatchet/pkg/grpc/middleware" @@ -27,7 +27,7 @@ import ( schedulingv1 "github.com/hatchet-dev/hatchet/pkg/scheduling/v1" ) -func (a *AdminServiceImpl) triggerWorkflowV1(ctx context.Context, req *contracts.TriggerWorkflowRequest) (*contracts.TriggerWorkflowResponse, error) { +func (a *AdminServiceImpl) triggerWorkflowV1(ctx context.Context, req *v1contracts.TriggerWorkflowRequest) (*contracts.TriggerWorkflowResponse, error) { tenant := ctx.Value("tenant").(*sqlcv1.Tenant) tenantId := tenant.ID @@ -53,8 +53,14 @@ func (a *AdminServiceImpl) triggerWorkflowV1(ctx context.Context, req *contracts opt, err := a.newTriggerOpt(ctx, tenantId, req) + re, isInvalidArgument := err.(*v1.TriggerOptInvalidArgumentError) + if err != nil { - return nil, fmt.Errorf("could not create trigger opt: %w", err) + if isInvalidArgument { + return nil, status.Errorf(codes.InvalidArgument, "Invalid request: %s", re.Err) + } else { + return nil, fmt.Errorf("could not create trigger opt: %w", err) + } } if err := v1.ValidateJSONB(opt.Data, "payload"); err != nil { @@ -111,8 +117,14 @@ func (a *AdminServiceImpl) bulkTriggerWorkflowV1(ctx context.Context, req *contr for i, workflow := range req.Workflows { opt, err := a.newTriggerOpt(ctx, tenantId, workflow) + re, isInvalidArgument := err.(*v1.TriggerOptInvalidArgumentError) + if err != nil { - return nil, fmt.Errorf("could not create trigger opt: %w", err) + if isInvalidArgument { + return nil, status.Errorf(codes.InvalidArgument, "Invalid request: %s", re.Err) + } else { + return nil, fmt.Errorf("could not create trigger opt: %w", err) + } } if err := v1.ValidateJSONB(opt.Data, "payload"); err != nil { @@ -170,46 +182,12 @@ func (a *AdminServiceImpl) bulkTriggerWorkflowV1(ctx context.Context, req *contr func (i *AdminServiceImpl) newTriggerOpt( ctx context.Context, tenantId uuid.UUID, - req *contracts.TriggerWorkflowRequest, + req *v1contracts.TriggerWorkflowRequest, ) (*v1.WorkflowNameTriggerOpts, error) { ctx, span := telemetry.NewSpan(ctx, "admin_service.new_trigger_opt") defer span.End() - span.SetAttributes( - attribute.String("admin_service.new_trigger_opt.workflow_name", req.Name), - attribute.Int("admin_service.new_trigger_opt.payload_size", len(req.Input)), - attribute.Bool("admin_service.new_trigger_opt.is_child_workflow", req.ParentTaskRunExternalId != nil), - ) - - additionalMeta := "" - - if req.AdditionalMetadata != nil { - additionalMeta = *req.AdditionalMetadata - } - - var desiredWorkerId *uuid.UUID - if req.DesiredWorkerId != nil { - workerId, err := uuid.Parse(*req.DesiredWorkerId) - if err != nil { - return nil, status.Errorf(codes.InvalidArgument, "desiredWorkerId must be a valid UUID: %s", err) - } - desiredWorkerId = &workerId - } - - t := &v1.TriggerTaskData{ - WorkflowName: req.Name, - Data: []byte(req.Input), - AdditionalMetadata: []byte(additionalMeta), - DesiredWorkerId: desiredWorkerId, - Priority: req.Priority, - } - - if req.Priority != nil { - if *req.Priority < 1 || *req.Priority > 3 { - return nil, status.Errorf(codes.InvalidArgument, "priority must be between 1 and 3, got %d", *req.Priority) - } - t.Priority = req.Priority - } + var parentTask *sqlcv1.FlattenExternalIdsRow if req.ParentTaskRunExternalId != nil { parentTaskExternalId, err := uuid.Parse(*req.ParentTaskRunExternalId) @@ -218,8 +196,7 @@ func (i *AdminServiceImpl) newTriggerOpt( return nil, status.Errorf(codes.InvalidArgument, "parentStepRunId must be a valid UUID: %s", err) } - // lookup the parent external id - parentTask, err := i.repov1.Tasks().GetTaskByExternalId( + maybeParentTask, err := i.repov1.Tasks().GetTaskByExternalId( ctx, tenantId, parentTaskExternalId, @@ -230,14 +207,19 @@ func (i *AdminServiceImpl) newTriggerOpt( return nil, fmt.Errorf("could not find parent task: %w", err) } - parentExternalId := parentTask.ExternalID - childIndex := int64(*req.ChildIndex) + parentTask = maybeParentTask + } - t.ParentExternalId = &parentExternalId - t.ParentTaskId = &parentTask.ID - t.ParentTaskInsertedAt = &parentTask.InsertedAt.Time - t.ChildIndex = &childIndex - t.ChildKey = req.ChildKey + t, err := i.repov1.Triggers().NewTriggerTaskData(ctx, tenantId, req, parentTask) + + if err != nil { + re, isInvalidArgument := err.(*v1.TriggerOptInvalidArgumentError) + + if isInvalidArgument { + return nil, re + } else { + return nil, fmt.Errorf("could not create trigger opt: %w", err) + } } return &v1.WorkflowNameTriggerOpts{ diff --git a/internal/services/admin/v1/server.go b/internal/services/admin/v1/server.go index ab190bd47..fbbe36d3e 100644 --- a/internal/services/admin/v1/server.go +++ b/internal/services/admin/v1/server.go @@ -397,8 +397,14 @@ func (a *AdminServiceImpl) TriggerWorkflowRun(ctx context.Context, req *contract opt, err := a.newTriggerOpt(ctx, tenantId, req) + re, isInvalidArgument := err.(*v1.TriggerOptInvalidArgumentError) + if err != nil { - return nil, fmt.Errorf("could not create trigger opt: %w", err) + if isInvalidArgument { + return nil, status.Errorf(codes.InvalidArgument, "Invalid request: %s", re.Err) + } else { + return nil, fmt.Errorf("could not create trigger opt: %w", err) + } } err = a.generateExternalIds(ctx, tenantId, []*v1.WorkflowNameTriggerOpts{opt}) diff --git a/internal/services/controllers/olap/signal/signal.go b/internal/services/controllers/olap/signal/signal.go index 71eb991eb..eee553517 100644 --- a/internal/services/controllers/olap/signal/signal.go +++ b/internal/services/controllers/olap/signal/signal.go @@ -34,6 +34,24 @@ func NewOLAPSignaler(mq msgqueue.MessageQueue, repo v1.Repository, l *zerolog.Lo } } +func (s *OLAPSignaler) SignalCreated(ctx context.Context, tenantId uuid.UUID, tasks []*v1.V1TaskWithPayload, dags []*v1.DAGWithData) error { + eg := &errgroup.Group{} + + if len(tasks) > 0 { + eg.Go(func() error { + return s.SignalTasksCreated(ctx, tenantId, tasks) + }) + } + + if len(dags) > 0 { + eg.Go(func() error { + return s.SignalDAGsCreated(ctx, tenantId, dags) + }) + } + + return eg.Wait() +} + func (s *OLAPSignaler) SignalDAGsCreated(ctx context.Context, tenantId uuid.UUID, dags []*v1.DAGWithData) error { // notify that tasks have been created // TODO: make this transactionally safe? diff --git a/internal/services/controllers/task/controller.go b/internal/services/controllers/task/controller.go index 0b76f7b96..1ab527f1a 100644 --- a/internal/services/controllers/task/controller.go +++ b/internal/services/controllers/task/controller.go @@ -1097,6 +1097,12 @@ func (tc *TasksControllerImpl) processUserEventMatches(ctx context.Context, tena } } + if len(matchResult.SatisfiedCallbacks) > 0 { + if err := tc.processSatisfiedCallbacks(ctx, tenantId, matchResult.SatisfiedCallbacks); err != nil { + tc.l.Error().Err(err).Msg("could not process satisfied callbacks") + } + } + return nil } @@ -1137,6 +1143,12 @@ func (tc *TasksControllerImpl) processInternalEvents(ctx context.Context, tenant } } + if len(matchResult.SatisfiedCallbacks) > 0 { + if err := tc.processSatisfiedCallbacks(ctx, tenantId, matchResult.SatisfiedCallbacks); err != nil { + tc.l.Error().Err(err).Msg("could not process satisfied callbacks") + } + } + return nil } diff --git a/internal/services/controllers/task/durable_callbacks.go b/internal/services/controllers/task/durable_callbacks.go new file mode 100644 index 000000000..f5872e28f --- /dev/null +++ b/internal/services/controllers/task/durable_callbacks.go @@ -0,0 +1,72 @@ +package task + +import ( + "context" + "fmt" + + "github.com/google/uuid" + + "github.com/hatchet-dev/hatchet/internal/msgqueue" + tasktypes "github.com/hatchet-dev/hatchet/internal/services/shared/tasktypes/v1" + v1 "github.com/hatchet-dev/hatchet/pkg/repository" +) + +func (tc *TasksControllerImpl) processSatisfiedCallbacks(ctx context.Context, tenantId uuid.UUID, callbacks []v1.SatisfiedCallback) error { + if len(callbacks) == 0 { + return nil + } + + idInsertedAtTuples := make([]v1.IdInsertedAt, 0) + + for _, cb := range callbacks { + idInsertedAtTuples = append(idInsertedAtTuples, v1.IdInsertedAt{ + ID: cb.DurableTaskId, + InsertedAt: cb.DurableTaskInsertedAt, + }) + } + + idInsertedAtToDispatcherId, err := tc.repov1.Workers().GetDurableDispatcherIdsForTasks(ctx, tenantId, idInsertedAtTuples) + + if err != nil { + return fmt.Errorf("could not list dispatcher ids for tasks: %w", err) + } + + dispatcherToMsgs := make(map[uuid.UUID][]*msgqueue.Message) + + for _, cb := range callbacks { + key := v1.IdInsertedAt{ + ID: cb.DurableTaskId, + InsertedAt: cb.DurableTaskInsertedAt, + } + + dispatcherId, ok := idInsertedAtToDispatcherId[key] + + if !ok { + tc.l.Warn().Msgf("could not find dispatcher id for task %d inserted at %s, skipping callback", cb.DurableTaskId, cb.DurableTaskInsertedAt) + continue + } + + msg, err := tasktypes.DurableCallbackCompletedMessage( + tenantId, + cb.DurableTaskExternalId, + cb.NodeId, + cb.Data, + ) + if err != nil { + tc.l.Error().Err(err).Msgf("failed to create callback completed message for task %s node %d", cb.DurableTaskExternalId, cb.NodeId) + continue + } + + dispatcherToMsgs[dispatcherId] = append(dispatcherToMsgs[dispatcherId], msg) + } + + for dispatcherId, msgs := range dispatcherToMsgs { + for _, m := range msgs { + if err := tc.mq.SendMessage(ctx, msgqueue.QueueTypeFromDispatcherID(dispatcherId), m); err != nil { + tc.l.Error().Err(err).Msgf("failed to send callback completed message to dispatcher %s", dispatcherId) + } + } + } + + return nil +} diff --git a/internal/services/controllers/task/process_sleeps.go b/internal/services/controllers/task/process_sleeps.go index 9c03d1650..d85766b44 100644 --- a/internal/services/controllers/task/process_sleeps.go +++ b/internal/services/controllers/task/process_sleeps.go @@ -33,5 +33,11 @@ func (tc *TasksControllerImpl) processSleeps(ctx context.Context, tenantId strin } } + if len(matchResult.SatisfiedCallbacks) > 0 { + if err := tc.processSatisfiedCallbacks(ctx, tenantIdUUID, matchResult.SatisfiedCallbacks); err != nil { + tc.l.Error().Err(err).Msg("could not process satisfied callbacks from sleep") + } + } + return shouldContinue, nil } diff --git a/internal/services/controllers/task/trigger/trigger.go b/internal/services/controllers/task/trigger/trigger.go index 527ccd835..b90a1b6f5 100644 --- a/internal/services/controllers/task/trigger/trigger.go +++ b/internal/services/controllers/task/trigger/trigger.go @@ -137,22 +137,20 @@ func (tw *TriggerWriter) TriggerFromWorkflowNames(ctx context.Context, tenantId return fmt.Errorf("could not trigger workflows from names: %w", err) } - eg := &errgroup.Group{} - - eg.Go(func() error { - return tw.signaler.SignalTasksCreated(ctx, tenantId, tasks) - }) - - eg.Go(func() error { - return tw.signaler.SignalDAGsCreated(ctx, tenantId, dags) - }) - // signaling errors do not result in a failure, since we have already written the tasks to the database, but // we log the error // FIXME: we need a mechanism to DLQ these failed signals - if err := eg.Wait(); err != nil { + if err := tw.signaler.SignalCreated(ctx, tenantId, tasks, dags); err != nil { tw.l.Error().Err(err).Msg("failed to signal created tasks and DAGs in TriggerFromWorkflowNames") } return nil } + +func (tw *TriggerWriter) SignalCreated(ctx context.Context, tenantId uuid.UUID, tasks []*v1.V1TaskWithPayload, dags []*v1.DAGWithData) error { + if err := tw.signaler.SignalCreated(ctx, tenantId, tasks, dags); err != nil { + tw.l.Error().Err(err).Msg("failed to signal created tasks and DAGs in SignalCreated") + } + + return nil +} diff --git a/internal/services/dispatcher/dispatcher.go b/internal/services/dispatcher/dispatcher.go index 977bab348..9facf9f71 100644 --- a/internal/services/dispatcher/dispatcher.go +++ b/internal/services/dispatcher/dispatcher.go @@ -14,6 +14,7 @@ import ( "github.com/hatchet-dev/hatchet/internal/msgqueue" "github.com/hatchet-dev/hatchet/internal/services/dispatcher/contracts" "github.com/hatchet-dev/hatchet/internal/services/shared/recoveryutils" + tasktypes "github.com/hatchet-dev/hatchet/internal/services/shared/tasktypes/v1" "github.com/hatchet-dev/hatchet/internal/syncx" "github.com/hatchet-dev/hatchet/pkg/logger" v1 "github.com/hatchet-dev/hatchet/pkg/repository" @@ -48,6 +49,8 @@ type DispatcherImpl struct { dispatcherId uuid.UUID workers *workers a *hatcheterrors.Wrapped + + durableCallbackFn func(taskExternalId uuid.UUID, nodeId int64, payload []byte) error } var ErrWorkerNotFound = fmt.Errorf("worker not found") @@ -372,6 +375,8 @@ func (d *DispatcherImpl) handleV1Task(ctx context.Context, task *msgqueue.Messag err = d.a.WrapErr(d.handleTaskBulkAssignedTask(ctx, task), map[string]interface{}{}) case "task-cancelled": err = d.a.WrapErr(d.handleTaskCancelled(ctx, task), map[string]interface{}{}) + case msgqueue.MsgIDDurableCallbackCompleted: + err = d.a.WrapErr(d.handleDurableCallbackCompleted(ctx, task), map[string]interface{}{}) default: err = fmt.Errorf("unknown task: %s", task.ID) } @@ -379,6 +384,36 @@ func (d *DispatcherImpl) handleV1Task(ctx context.Context, task *msgqueue.Messag return err } +func (d *DispatcherImpl) DispatcherId() uuid.UUID { + return d.dispatcherId +} + +func (d *DispatcherImpl) SetDurableCallbackHandler(fn func(uuid.UUID, int64, []byte) error) { + d.durableCallbackFn = fn +} + +func (d *DispatcherImpl) handleDurableCallbackCompleted(ctx context.Context, task *msgqueue.Message) error { + if d.durableCallbackFn == nil { + return nil + } + + payloads := msgqueue.JSONConvert[tasktypes.DurableCallbackCompletedPayload](task.Payloads) + + for _, payload := range payloads { + err := d.durableCallbackFn( + payload.TaskExternalId, + payload.NodeId, + payload.Payload, + ) + + if err != nil { + d.l.Error().Err(err).Msgf("failed to deliver callback completion for task %s", payload.TaskExternalId) + } + } + + return nil +} + func (d *DispatcherImpl) runUpdateHeartbeat(ctx context.Context) func() { return func() { d.l.Debug().Msgf("dispatcher: updating heartbeat") diff --git a/internal/services/dispatcher/v1/dispatcher.go b/internal/services/dispatcher/v1/dispatcher.go index 0aa23d83e..35a2a9bc7 100644 --- a/internal/services/dispatcher/v1/dispatcher.go +++ b/internal/services/dispatcher/v1/dispatcher.go @@ -3,10 +3,13 @@ package v1 import ( "fmt" + "github.com/google/uuid" "github.com/rs/zerolog" "github.com/hatchet-dev/hatchet/internal/msgqueue" + "github.com/hatchet-dev/hatchet/internal/services/controllers/task/trigger" contracts "github.com/hatchet-dev/hatchet/internal/services/shared/proto/v1" + "github.com/hatchet-dev/hatchet/internal/syncx" "github.com/hatchet-dev/hatchet/pkg/logger" v1 "github.com/hatchet-dev/hatchet/pkg/repository" "github.com/hatchet-dev/hatchet/pkg/validator" @@ -19,19 +22,25 @@ type DispatcherService interface { type DispatcherServiceImpl struct { contracts.UnimplementedV1DispatcherServer - repo v1.Repository - mq msgqueue.MessageQueue - v validator.Validator - l *zerolog.Logger + repo v1.Repository + mq msgqueue.MessageQueue + v validator.Validator + l *zerolog.Logger + triggerWriter *trigger.TriggerWriter + dispatcherId uuid.UUID + + durableInvocations syncx.Map[uuid.UUID, *durableTaskInvocation] + workerInvocations syncx.Map[uuid.UUID, *durableTaskInvocation] } type DispatcherServiceOpt func(*DispatcherServiceOpts) type DispatcherServiceOpts struct { - repo v1.Repository - mq msgqueue.MessageQueue - v validator.Validator - l *zerolog.Logger + repo v1.Repository + mq msgqueue.MessageQueue + v validator.Validator + l *zerolog.Logger + dispatcherId uuid.UUID } func defaultDispatcherServiceOpts() *DispatcherServiceOpts { @@ -68,7 +77,13 @@ func WithLogger(l *zerolog.Logger) DispatcherServiceOpt { } } -func NewDispatcherService(fs ...DispatcherServiceOpt) (DispatcherService, error) { +func WithDispatcherId(id uuid.UUID) DispatcherServiceOpt { + return func(opts *DispatcherServiceOpts) { + opts.dispatcherId = id + } +} + +func NewDispatcherService(fs ...DispatcherServiceOpt) (*DispatcherServiceImpl, error) { opts := defaultDispatcherServiceOpts() for _, f := range fs { @@ -83,10 +98,15 @@ func NewDispatcherService(fs ...DispatcherServiceOpt) (DispatcherService, error) return nil, fmt.Errorf("task queue is required. use WithMessageQueue") } + pubBuffer := msgqueue.NewMQPubBuffer(opts.mq) + tw := trigger.NewTriggerWriter(opts.mq, opts.repo, opts.l, pubBuffer, 0) + return &DispatcherServiceImpl{ - repo: opts.repo, - mq: opts.mq, - v: opts.v, - l: opts.l, + repo: opts.repo, + mq: opts.mq, + v: opts.v, + l: opts.l, + triggerWriter: tw, + dispatcherId: opts.dispatcherId, }, nil } diff --git a/internal/services/dispatcher/v1/server.go b/internal/services/dispatcher/v1/server.go index 83538b6b5..1c14c1906 100644 --- a/internal/services/dispatcher/v1/server.go +++ b/internal/services/dispatcher/v1/server.go @@ -318,3 +318,340 @@ func waitFor(wg *sync.WaitGroup, timeout time.Duration, l *zerolog.Logger) { l.Error().Msg("timed out waiting for wait group") } } + +type durableTaskInvocation struct { + server contracts.V1Dispatcher_DurableTaskServer + tenantId uuid.UUID + workerId uuid.UUID + l *zerolog.Logger + + sendMu sync.Mutex +} + +func (s *durableTaskInvocation) send(resp *contracts.DurableTaskResponse) error { + s.sendMu.Lock() + defer s.sendMu.Unlock() + return s.server.Send(resp) +} + +func (d *DispatcherServiceImpl) DurableTask(server contracts.V1Dispatcher_DurableTaskServer) error { + tenant := server.Context().Value("tenant").(*sqlcv1.Tenant) + tenantId := tenant.ID + + ctx, cancel := context.WithCancel(server.Context()) + defer cancel() + + invocation := &durableTaskInvocation{ + server: server, + tenantId: tenantId, + l: d.l, + } + + registeredTasks := make(map[uuid.UUID]struct{}) + defer func() { + for taskId := range registeredTasks { + d.durableInvocations.Delete(taskId) + } + d.workerInvocations.Delete(invocation.workerId) + }() + + for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + req, err := server.Recv() + if err != nil { + if errors.Is(err, io.EOF) || status.Code(err) == codes.Canceled { + return nil + } + d.l.Error().Err(err).Msg("error receiving durable task request") + return err + } + + if event := req.GetEvent(); event != nil { + taskExtId, err := uuid.Parse(event.DurableTaskExternalId) + + if err != nil { + return status.Errorf(codes.InvalidArgument, "invalid durable task external id: %v", err) + } + + if _, exists := registeredTasks[taskExtId]; !exists { + d.durableInvocations.Store(taskExtId, invocation) + registeredTasks[taskExtId] = struct{}{} + } + } + + if err := d.handleDurableTaskRequest(ctx, invocation, req); err != nil { + d.l.Error().Err(err).Msg("error handling durable task request") + // Continue processing other requests rather than closing the stream + } + } +} + +func (d *DispatcherServiceImpl) handleDurableTaskRequest( + ctx context.Context, + invocation *durableTaskInvocation, + req *contracts.DurableTaskRequest, +) error { + switch msg := req.GetMessage().(type) { + case *contracts.DurableTaskRequest_RegisterWorker: + return d.handleRegisterWorker(ctx, invocation, msg.RegisterWorker) + + case *contracts.DurableTaskRequest_Event: + return d.handleDurableTaskEvent(ctx, invocation, msg.Event) + + case *contracts.DurableTaskRequest_EvictInvocation: + return d.handleEvictInvocation(ctx, invocation, msg.EvictInvocation) + + case *contracts.DurableTaskRequest_WorkerStatus: + return d.handleWorkerStatus(ctx, invocation, msg.WorkerStatus) + + default: + return status.Errorf(codes.InvalidArgument, "unknown message type: %T", msg) + } +} + +func (d *DispatcherServiceImpl) handleRegisterWorker( + ctx context.Context, + invocation *durableTaskInvocation, + req *contracts.DurableTaskRequestRegisterWorker, +) error { + workerId, err := uuid.Parse(req.WorkerId) + + if err != nil { + return status.Errorf(codes.InvalidArgument, "invalid worker id: %v", err) + } + + invocation.workerId = workerId + d.workerInvocations.Store(workerId, invocation) + + err = d.repo.Workers().UpdateWorkerDurableTaskDispatcherId(ctx, invocation.tenantId, workerId, d.dispatcherId) + + if err != nil { + return status.Errorf(codes.Internal, "failed to update worker durable task dispatcher id: %v", err) + } + + return invocation.send(&contracts.DurableTaskResponse{ + Message: &contracts.DurableTaskResponse_RegisterWorker{ + RegisterWorker: &contracts.DurableTaskResponseRegisterWorker{ + WorkerId: req.WorkerId, + }, + }, + }) +} + +func getDurableTaskEventKind(eventKind contracts.DurableTaskEventKind) (sqlcv1.V1DurableEventLogKind, error) { + switch eventKind { + case contracts.DurableTaskEventKind_DURABLE_TASK_TRIGGER_KIND_WAIT_FOR: + return sqlcv1.V1DurableEventLogKindWAITFOR, nil + case contracts.DurableTaskEventKind_DURABLE_TASK_TRIGGER_KIND_RUN: + return sqlcv1.V1DurableEventLogKindRUN, nil + case contracts.DurableTaskEventKind_DURABLE_TASK_TRIGGER_KIND_MEMO: + return sqlcv1.V1DurableEventLogKindMEMO, nil + default: + return "", fmt.Errorf("unsupported event kind: %v", eventKind) + } +} + +func (d *DispatcherServiceImpl) handleDurableTaskEvent( + ctx context.Context, + invocation *durableTaskInvocation, + req *contracts.DurableTaskEventRequest, +) error { + taskExternalId, err := uuid.Parse(req.DurableTaskExternalId) + if err != nil { + return status.Errorf(codes.InvalidArgument, "invalid durable_task_external_id: %v", err) + } + + task, err := d.repo.Tasks().GetTaskByExternalId(ctx, invocation.tenantId, taskExternalId, false) + if err != nil { + return status.Errorf(codes.NotFound, "task not found: %v", err) + } + + kind, err := getDurableTaskEventKind(req.Kind) + if err != nil { + return status.Errorf(codes.InvalidArgument, "invalid event kind: %v", err) + } + + var triggerOpts *v1.WorkflowNameTriggerOpts + + if kind == sqlcv1.V1DurableEventLogKindRUN { + ttd, err := d.repo.Triggers().NewTriggerTaskData(ctx, invocation.tenantId, req.TriggerOpts, task) + + if err != nil { + return status.Errorf(codes.Internal, "failed to create trigger options: %v", err) + } + + optsSlice := []*v1.WorkflowNameTriggerOpts{{ + TriggerTaskData: ttd, + }} + + err = d.repo.Triggers().PopulateExternalIdsForWorkflow(ctx, invocation.tenantId, optsSlice) + + if err != nil { + return status.Errorf(codes.Internal, "failed to populate external ids for workflow: %v", err) + } + + triggerOpts = optsSlice[0] + } + + createConditionOpts := make([]v1.CreateExternalSignalConditionOpt, 0) + + if req.WaitForConditions != nil { + for _, condition := range req.WaitForConditions.SleepConditions { + orGroupId, err := uuid.Parse(condition.Base.OrGroupId) + if err != nil { + return status.Errorf(codes.InvalidArgument, "or group id is not a valid uuid: %v", err) + } + + createConditionOpts = append(createConditionOpts, v1.CreateExternalSignalConditionOpt{ + Kind: v1.CreateExternalSignalConditionKindSLEEP, + ReadableDataKey: condition.Base.ReadableDataKey, + OrGroupId: orGroupId, + SleepFor: &condition.SleepFor, + }) + } + + for _, condition := range req.WaitForConditions.UserEventConditions { + orGroupId, err := uuid.Parse(condition.Base.OrGroupId) + if err != nil { + return status.Errorf(codes.InvalidArgument, "or group id is not a valid uuid: %v", err) + } + + createConditionOpts = append(createConditionOpts, v1.CreateExternalSignalConditionOpt{ + Kind: v1.CreateExternalSignalConditionKindUSEREVENT, + ReadableDataKey: condition.Base.ReadableDataKey, + OrGroupId: orGroupId, + UserEventKey: &condition.UserEventKey, + Expression: condition.Base.Expression, + }) + } + } + + ingestionResult, err := d.repo.DurableEvents().IngestDurableTaskEvent(ctx, v1.IngestDurableTaskEventOpts{ + TenantId: invocation.tenantId, + Task: task, + Kind: kind, + Payload: req.Payload, + WaitForConditions: createConditionOpts, + InvocationCount: req.InvocationCount, + TriggerOpts: triggerOpts, + }) + + if err != nil { + return status.Errorf(codes.Internal, "failed to ingest durable task event: %v", err) + } + + if len(ingestionResult.CreatedTasks) > 0 || len(ingestionResult.CreatedDAGs) > 0 { + if sigErr := d.triggerWriter.SignalCreated(ctx, invocation.tenantId, ingestionResult.CreatedTasks, ingestionResult.CreatedDAGs); sigErr != nil { + d.l.Error().Err(sigErr).Msg("failed to signal created tasks/DAGs for durable run trigger") + } + } + + err = invocation.send(&contracts.DurableTaskResponse{ + Message: &contracts.DurableTaskResponse_TriggerAck{ + TriggerAck: &contracts.DurableTaskEventAckResponse{ + InvocationCount: req.InvocationCount, + DurableTaskExternalId: req.DurableTaskExternalId, + NodeId: ingestionResult.NodeId, + }, + }, + }) + + if err != nil { + return status.Errorf(codes.Internal, "failed to send trigger ack: %v", err) + } + + if ingestionResult.Callback.Callback.IsSatisfied { + err := d.DeliverCallbackCompletion( + taskExternalId, + ingestionResult.NodeId, + ingestionResult.Callback.Result, + ) + + if err != nil { + d.l.Error().Err(err).Msgf("failed to deliver callback completion for task %s node %d", taskExternalId, ingestionResult.NodeId) + return status.Errorf(codes.Internal, "failed to deliver callback completion: %v", err) + } + } + + return nil +} + +func (d *DispatcherServiceImpl) handleEvictInvocation( + ctx context.Context, + invocation *durableTaskInvocation, + req *contracts.DurableTaskEvictInvocationRequest, +) error { + // todo: implement eviction here + + return nil +} + +func (d *DispatcherServiceImpl) handleWorkerStatus( + ctx context.Context, + invocation *durableTaskInvocation, + req *contracts.DurableTaskWorkerStatusRequest, +) error { + if len(req.WaitingCallbacks) == 0 { + return nil + } + + waiting := make([]v1.TaskExternalIdNodeId, 0, len(req.WaitingCallbacks)) + for _, cb := range req.WaitingCallbacks { + taskExternalId, err := uuid.Parse(cb.DurableTaskExternalId) + if err != nil { + d.l.Warn().Err(err).Msgf("invalid durable_task_external_id in worker_status: %s", cb.DurableTaskExternalId) + continue + } + waiting = append(waiting, v1.TaskExternalIdNodeId{ + TaskExternalId: taskExternalId, + NodeId: cb.NodeId, + }) + } + + if len(waiting) == 0 { + return nil + } + + callbacks, err := d.repo.DurableEvents().GetSatisfiedCallbacks(ctx, invocation.tenantId, waiting) + if err != nil { + return fmt.Errorf("failed to get satisfied callbacks: %w", err) + } + + for _, cb := range callbacks { + if err := invocation.send(&contracts.DurableTaskResponse{ + Message: &contracts.DurableTaskResponse_CallbackCompleted{ + CallbackCompleted: &contracts.DurableTaskCallbackCompletedResponse{ + DurableTaskExternalId: cb.TaskExternalId.String(), + NodeId: cb.NodeID, + Payload: cb.Result, + }, + }, + }); err != nil { + d.l.Error().Err(err).Msgf("failed to send callback_completed for task %s node %d", cb.TaskExternalId, cb.NodeID) + } + } + + return nil +} + +func (d *DispatcherServiceImpl) DeliverCallbackCompletion(taskExternalId uuid.UUID, nodeId int64, payload []byte) error { + inv, ok := d.durableInvocations.Load(taskExternalId) + if !ok { + return fmt.Errorf("no active invocation found for task %s", taskExternalId) + } + + return inv.send(&contracts.DurableTaskResponse{ + Message: &contracts.DurableTaskResponse_CallbackCompleted{ + CallbackCompleted: &contracts.DurableTaskCallbackCompletedResponse{ + DurableTaskExternalId: taskExternalId.String(), + NodeId: nodeId, + Payload: payload, + }, + }, + }) +} diff --git a/internal/services/grpc/server.go b/internal/services/grpc/server.go index 569284423..ed4e54d55 100644 --- a/internal/services/grpc/server.go +++ b/internal/services/grpc/server.go @@ -24,7 +24,7 @@ import ( collectortracev1 "go.opentelemetry.io/proto/otlp/collector/trace/v1" "github.com/hatchet-dev/hatchet/internal/services/admin" - admincontracts "github.com/hatchet-dev/hatchet/internal/services/admin/contracts" + admincontracts "github.com/hatchet-dev/hatchet/internal/services/admin/contracts/workflows" adminv1 "github.com/hatchet-dev/hatchet/internal/services/admin/v1" "github.com/hatchet-dev/hatchet/internal/services/dispatcher" dispatchercontracts "github.com/hatchet-dev/hatchet/internal/services/dispatcher/contracts" diff --git a/internal/services/scheduler/v1/optimistic.go b/internal/services/scheduler/v1/optimistic.go index b62177190..65fccb837 100644 --- a/internal/services/scheduler/v1/optimistic.go +++ b/internal/services/scheduler/v1/optimistic.go @@ -22,20 +22,8 @@ func (s *Scheduler) RunOptimisticScheduling(ctx context.Context, tenantId uuid.U ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() - eg := &errgroup.Group{} - - eg.Go(func() error { - return s.signaler.SignalTasksCreated(ctx, tenantId, tasks) - }) - - eg.Go(func() error { - return s.signaler.SignalDAGsCreated(ctx, tenantId, dags) - }) - - innerErr := eg.Wait() - - if innerErr != nil { - s.l.Error().Err(innerErr).Msgf("failed to signal optimistic scheduling results for tenant %s", tenantId) + if err := s.signaler.SignalCreated(ctx, tenantId, tasks, dags); err != nil { + s.l.Error().Err(err).Msgf("failed to signal optimistic scheduling results for tenant %s", tenantId) } }() @@ -70,11 +58,7 @@ func (s *Scheduler) RunOptimisticSchedulingFromEvents(ctx context.Context, tenan }) eg.Go(func() error { - return s.signaler.SignalTasksCreated(ctx, tenantId, eventRes.Tasks) - }) - - eg.Go(func() error { - return s.signaler.SignalDAGsCreated(ctx, tenantId, eventRes.Dags) + return s.signaler.SignalCreated(ctx, tenantId, eventRes.Tasks, eventRes.Dags) }) innerErr := eg.Wait() diff --git a/internal/services/shared/proto/v1/dispatcher.pb.go b/internal/services/shared/proto/v1/dispatcher.pb.go index d8acd18a2..d1d9dcd30 100644 --- a/internal/services/shared/proto/v1/dispatcher.pb.go +++ b/internal/services/shared/proto/v1/dispatcher.pb.go @@ -20,6 +20,756 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type DurableTaskEventKind int32 + +const ( + DurableTaskEventKind_DURABLE_TASK_TRIGGER_KIND_UNSPECIFIED DurableTaskEventKind = 0 + DurableTaskEventKind_DURABLE_TASK_TRIGGER_KIND_RUN DurableTaskEventKind = 1 + DurableTaskEventKind_DURABLE_TASK_TRIGGER_KIND_WAIT_FOR DurableTaskEventKind = 2 + DurableTaskEventKind_DURABLE_TASK_TRIGGER_KIND_MEMO DurableTaskEventKind = 3 +) + +// Enum value maps for DurableTaskEventKind. +var ( + DurableTaskEventKind_name = map[int32]string{ + 0: "DURABLE_TASK_TRIGGER_KIND_UNSPECIFIED", + 1: "DURABLE_TASK_TRIGGER_KIND_RUN", + 2: "DURABLE_TASK_TRIGGER_KIND_WAIT_FOR", + 3: "DURABLE_TASK_TRIGGER_KIND_MEMO", + } + DurableTaskEventKind_value = map[string]int32{ + "DURABLE_TASK_TRIGGER_KIND_UNSPECIFIED": 0, + "DURABLE_TASK_TRIGGER_KIND_RUN": 1, + "DURABLE_TASK_TRIGGER_KIND_WAIT_FOR": 2, + "DURABLE_TASK_TRIGGER_KIND_MEMO": 3, + } +) + +func (x DurableTaskEventKind) Enum() *DurableTaskEventKind { + p := new(DurableTaskEventKind) + *p = x + return p +} + +func (x DurableTaskEventKind) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (DurableTaskEventKind) Descriptor() protoreflect.EnumDescriptor { + return file_v1_dispatcher_proto_enumTypes[0].Descriptor() +} + +func (DurableTaskEventKind) Type() protoreflect.EnumType { + return &file_v1_dispatcher_proto_enumTypes[0] +} + +func (x DurableTaskEventKind) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use DurableTaskEventKind.Descriptor instead. +func (DurableTaskEventKind) EnumDescriptor() ([]byte, []int) { + return file_v1_dispatcher_proto_rawDescGZIP(), []int{0} +} + +type DurableTaskRequestRegisterWorker struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + WorkerId string `protobuf:"bytes,1,opt,name=worker_id,json=workerId,proto3" json:"worker_id,omitempty"` +} + +func (x *DurableTaskRequestRegisterWorker) Reset() { + *x = DurableTaskRequestRegisterWorker{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_dispatcher_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DurableTaskRequestRegisterWorker) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DurableTaskRequestRegisterWorker) ProtoMessage() {} + +func (x *DurableTaskRequestRegisterWorker) ProtoReflect() protoreflect.Message { + mi := &file_v1_dispatcher_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DurableTaskRequestRegisterWorker.ProtoReflect.Descriptor instead. +func (*DurableTaskRequestRegisterWorker) Descriptor() ([]byte, []int) { + return file_v1_dispatcher_proto_rawDescGZIP(), []int{0} +} + +func (x *DurableTaskRequestRegisterWorker) GetWorkerId() string { + if x != nil { + return x.WorkerId + } + return "" +} + +type DurableTaskResponseRegisterWorker struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + WorkerId string `protobuf:"bytes,1,opt,name=worker_id,json=workerId,proto3" json:"worker_id,omitempty"` +} + +func (x *DurableTaskResponseRegisterWorker) Reset() { + *x = DurableTaskResponseRegisterWorker{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_dispatcher_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DurableTaskResponseRegisterWorker) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DurableTaskResponseRegisterWorker) ProtoMessage() {} + +func (x *DurableTaskResponseRegisterWorker) ProtoReflect() protoreflect.Message { + mi := &file_v1_dispatcher_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DurableTaskResponseRegisterWorker.ProtoReflect.Descriptor instead. +func (*DurableTaskResponseRegisterWorker) Descriptor() ([]byte, []int) { + return file_v1_dispatcher_proto_rawDescGZIP(), []int{1} +} + +func (x *DurableTaskResponseRegisterWorker) GetWorkerId() string { + if x != nil { + return x.WorkerId + } + return "" +} + +type DurableTaskEventRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The invocation_count is a monotonically increasing count that uniquely identifies an "attempt" + // at running a durable task. Each time the task is started, it gets a new invocation count (which has) + // incremented by one since the previous invocation. This allows the server (and the worker) to have a way of + // differentiating between different attempts of the same task running in different places, to prevent race conditions + // and other problems from duplication. It also allows for older invocations to be evicted cleanly + InvocationCount int64 `protobuf:"varint,1,opt,name=invocation_count,json=invocationCount,proto3" json:"invocation_count,omitempty"` + DurableTaskExternalId string `protobuf:"bytes,2,opt,name=durable_task_external_id,json=durableTaskExternalId,proto3" json:"durable_task_external_id,omitempty"` + Kind DurableTaskEventKind `protobuf:"varint,3,opt,name=kind,proto3,enum=v1.DurableTaskEventKind" json:"kind,omitempty"` + Payload []byte `protobuf:"bytes,4,opt,name=payload,proto3,oneof" json:"payload,omitempty"` + WaitForConditions *DurableEventListenerConditions `protobuf:"bytes,5,opt,name=wait_for_conditions,json=waitForConditions,proto3,oneof" json:"wait_for_conditions,omitempty"` + // Fields for DURABLE_TASK_TRIGGER_KIND_RUN (spawning child workflows) + TriggerOpts *TriggerWorkflowRequest `protobuf:"bytes,6,opt,name=trigger_opts,json=triggerOpts,proto3,oneof" json:"trigger_opts,omitempty"` +} + +func (x *DurableTaskEventRequest) Reset() { + *x = DurableTaskEventRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_dispatcher_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DurableTaskEventRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DurableTaskEventRequest) ProtoMessage() {} + +func (x *DurableTaskEventRequest) ProtoReflect() protoreflect.Message { + mi := &file_v1_dispatcher_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DurableTaskEventRequest.ProtoReflect.Descriptor instead. +func (*DurableTaskEventRequest) Descriptor() ([]byte, []int) { + return file_v1_dispatcher_proto_rawDescGZIP(), []int{2} +} + +func (x *DurableTaskEventRequest) GetInvocationCount() int64 { + if x != nil { + return x.InvocationCount + } + return 0 +} + +func (x *DurableTaskEventRequest) GetDurableTaskExternalId() string { + if x != nil { + return x.DurableTaskExternalId + } + return "" +} + +func (x *DurableTaskEventRequest) GetKind() DurableTaskEventKind { + if x != nil { + return x.Kind + } + return DurableTaskEventKind_DURABLE_TASK_TRIGGER_KIND_UNSPECIFIED +} + +func (x *DurableTaskEventRequest) GetPayload() []byte { + if x != nil { + return x.Payload + } + return nil +} + +func (x *DurableTaskEventRequest) GetWaitForConditions() *DurableEventListenerConditions { + if x != nil { + return x.WaitForConditions + } + return nil +} + +func (x *DurableTaskEventRequest) GetTriggerOpts() *TriggerWorkflowRequest { + if x != nil { + return x.TriggerOpts + } + return nil +} + +type DurableTaskEventAckResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + InvocationCount int64 `protobuf:"varint,1,opt,name=invocation_count,json=invocationCount,proto3" json:"invocation_count,omitempty"` + DurableTaskExternalId string `protobuf:"bytes,2,opt,name=durable_task_external_id,json=durableTaskExternalId,proto3" json:"durable_task_external_id,omitempty"` + NodeId int64 `protobuf:"varint,3,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` +} + +func (x *DurableTaskEventAckResponse) Reset() { + *x = DurableTaskEventAckResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_dispatcher_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DurableTaskEventAckResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DurableTaskEventAckResponse) ProtoMessage() {} + +func (x *DurableTaskEventAckResponse) ProtoReflect() protoreflect.Message { + mi := &file_v1_dispatcher_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DurableTaskEventAckResponse.ProtoReflect.Descriptor instead. +func (*DurableTaskEventAckResponse) Descriptor() ([]byte, []int) { + return file_v1_dispatcher_proto_rawDescGZIP(), []int{3} +} + +func (x *DurableTaskEventAckResponse) GetInvocationCount() int64 { + if x != nil { + return x.InvocationCount + } + return 0 +} + +func (x *DurableTaskEventAckResponse) GetDurableTaskExternalId() string { + if x != nil { + return x.DurableTaskExternalId + } + return "" +} + +func (x *DurableTaskEventAckResponse) GetNodeId() int64 { + if x != nil { + return x.NodeId + } + return 0 +} + +type DurableTaskCallbackCompletedResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DurableTaskExternalId string `protobuf:"bytes,1,opt,name=durable_task_external_id,json=durableTaskExternalId,proto3" json:"durable_task_external_id,omitempty"` + NodeId int64 `protobuf:"varint,2,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` + Payload []byte `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"` +} + +func (x *DurableTaskCallbackCompletedResponse) Reset() { + *x = DurableTaskCallbackCompletedResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_dispatcher_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DurableTaskCallbackCompletedResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DurableTaskCallbackCompletedResponse) ProtoMessage() {} + +func (x *DurableTaskCallbackCompletedResponse) ProtoReflect() protoreflect.Message { + mi := &file_v1_dispatcher_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DurableTaskCallbackCompletedResponse.ProtoReflect.Descriptor instead. +func (*DurableTaskCallbackCompletedResponse) Descriptor() ([]byte, []int) { + return file_v1_dispatcher_proto_rawDescGZIP(), []int{4} +} + +func (x *DurableTaskCallbackCompletedResponse) GetDurableTaskExternalId() string { + if x != nil { + return x.DurableTaskExternalId + } + return "" +} + +func (x *DurableTaskCallbackCompletedResponse) GetNodeId() int64 { + if x != nil { + return x.NodeId + } + return 0 +} + +func (x *DurableTaskCallbackCompletedResponse) GetPayload() []byte { + if x != nil { + return x.Payload + } + return nil +} + +type DurableTaskEvictInvocationRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + InvocationCount int64 `protobuf:"varint,1,opt,name=invocation_count,json=invocationCount,proto3" json:"invocation_count,omitempty"` + DurableTaskExternalId string `protobuf:"bytes,2,opt,name=durable_task_external_id,json=durableTaskExternalId,proto3" json:"durable_task_external_id,omitempty"` +} + +func (x *DurableTaskEvictInvocationRequest) Reset() { + *x = DurableTaskEvictInvocationRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_dispatcher_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DurableTaskEvictInvocationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DurableTaskEvictInvocationRequest) ProtoMessage() {} + +func (x *DurableTaskEvictInvocationRequest) ProtoReflect() protoreflect.Message { + mi := &file_v1_dispatcher_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DurableTaskEvictInvocationRequest.ProtoReflect.Descriptor instead. +func (*DurableTaskEvictInvocationRequest) Descriptor() ([]byte, []int) { + return file_v1_dispatcher_proto_rawDescGZIP(), []int{5} +} + +func (x *DurableTaskEvictInvocationRequest) GetInvocationCount() int64 { + if x != nil { + return x.InvocationCount + } + return 0 +} + +func (x *DurableTaskEvictInvocationRequest) GetDurableTaskExternalId() string { + if x != nil { + return x.DurableTaskExternalId + } + return "" +} + +type DurableTaskAwaitedCallback struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DurableTaskExternalId string `protobuf:"bytes,1,opt,name=durable_task_external_id,json=durableTaskExternalId,proto3" json:"durable_task_external_id,omitempty"` + NodeId int64 `protobuf:"varint,2,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` +} + +func (x *DurableTaskAwaitedCallback) Reset() { + *x = DurableTaskAwaitedCallback{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_dispatcher_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DurableTaskAwaitedCallback) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DurableTaskAwaitedCallback) ProtoMessage() {} + +func (x *DurableTaskAwaitedCallback) ProtoReflect() protoreflect.Message { + mi := &file_v1_dispatcher_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DurableTaskAwaitedCallback.ProtoReflect.Descriptor instead. +func (*DurableTaskAwaitedCallback) Descriptor() ([]byte, []int) { + return file_v1_dispatcher_proto_rawDescGZIP(), []int{6} +} + +func (x *DurableTaskAwaitedCallback) GetDurableTaskExternalId() string { + if x != nil { + return x.DurableTaskExternalId + } + return "" +} + +func (x *DurableTaskAwaitedCallback) GetNodeId() int64 { + if x != nil { + return x.NodeId + } + return 0 +} + +type DurableTaskWorkerStatusRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + WorkerId string `protobuf:"bytes,1,opt,name=worker_id,json=workerId,proto3" json:"worker_id,omitempty"` + NodeId int64 `protobuf:"varint,2,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` + BranchId int64 `protobuf:"varint,3,opt,name=branch_id,json=branchId,proto3" json:"branch_id,omitempty"` + WaitingCallbacks []*DurableTaskAwaitedCallback `protobuf:"bytes,4,rep,name=waiting_callbacks,json=waitingCallbacks,proto3" json:"waiting_callbacks,omitempty"` +} + +func (x *DurableTaskWorkerStatusRequest) Reset() { + *x = DurableTaskWorkerStatusRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_dispatcher_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DurableTaskWorkerStatusRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DurableTaskWorkerStatusRequest) ProtoMessage() {} + +func (x *DurableTaskWorkerStatusRequest) ProtoReflect() protoreflect.Message { + mi := &file_v1_dispatcher_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DurableTaskWorkerStatusRequest.ProtoReflect.Descriptor instead. +func (*DurableTaskWorkerStatusRequest) Descriptor() ([]byte, []int) { + return file_v1_dispatcher_proto_rawDescGZIP(), []int{7} +} + +func (x *DurableTaskWorkerStatusRequest) GetWorkerId() string { + if x != nil { + return x.WorkerId + } + return "" +} + +func (x *DurableTaskWorkerStatusRequest) GetNodeId() int64 { + if x != nil { + return x.NodeId + } + return 0 +} + +func (x *DurableTaskWorkerStatusRequest) GetBranchId() int64 { + if x != nil { + return x.BranchId + } + return 0 +} + +func (x *DurableTaskWorkerStatusRequest) GetWaitingCallbacks() []*DurableTaskAwaitedCallback { + if x != nil { + return x.WaitingCallbacks + } + return nil +} + +type DurableTaskRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Message: + // + // *DurableTaskRequest_RegisterWorker + // *DurableTaskRequest_Event + // *DurableTaskRequest_EvictInvocation + // *DurableTaskRequest_WorkerStatus + Message isDurableTaskRequest_Message `protobuf_oneof:"message"` +} + +func (x *DurableTaskRequest) Reset() { + *x = DurableTaskRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_dispatcher_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DurableTaskRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DurableTaskRequest) ProtoMessage() {} + +func (x *DurableTaskRequest) ProtoReflect() protoreflect.Message { + mi := &file_v1_dispatcher_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DurableTaskRequest.ProtoReflect.Descriptor instead. +func (*DurableTaskRequest) Descriptor() ([]byte, []int) { + return file_v1_dispatcher_proto_rawDescGZIP(), []int{8} +} + +func (m *DurableTaskRequest) GetMessage() isDurableTaskRequest_Message { + if m != nil { + return m.Message + } + return nil +} + +func (x *DurableTaskRequest) GetRegisterWorker() *DurableTaskRequestRegisterWorker { + if x, ok := x.GetMessage().(*DurableTaskRequest_RegisterWorker); ok { + return x.RegisterWorker + } + return nil +} + +func (x *DurableTaskRequest) GetEvent() *DurableTaskEventRequest { + if x, ok := x.GetMessage().(*DurableTaskRequest_Event); ok { + return x.Event + } + return nil +} + +func (x *DurableTaskRequest) GetEvictInvocation() *DurableTaskEvictInvocationRequest { + if x, ok := x.GetMessage().(*DurableTaskRequest_EvictInvocation); ok { + return x.EvictInvocation + } + return nil +} + +func (x *DurableTaskRequest) GetWorkerStatus() *DurableTaskWorkerStatusRequest { + if x, ok := x.GetMessage().(*DurableTaskRequest_WorkerStatus); ok { + return x.WorkerStatus + } + return nil +} + +type isDurableTaskRequest_Message interface { + isDurableTaskRequest_Message() +} + +type DurableTaskRequest_RegisterWorker struct { + RegisterWorker *DurableTaskRequestRegisterWorker `protobuf:"bytes,1,opt,name=register_worker,json=registerWorker,proto3,oneof"` +} + +type DurableTaskRequest_Event struct { + Event *DurableTaskEventRequest `protobuf:"bytes,2,opt,name=event,proto3,oneof"` +} + +type DurableTaskRequest_EvictInvocation struct { + EvictInvocation *DurableTaskEvictInvocationRequest `protobuf:"bytes,3,opt,name=evict_invocation,json=evictInvocation,proto3,oneof"` +} + +type DurableTaskRequest_WorkerStatus struct { + WorkerStatus *DurableTaskWorkerStatusRequest `protobuf:"bytes,4,opt,name=worker_status,json=workerStatus,proto3,oneof"` +} + +func (*DurableTaskRequest_RegisterWorker) isDurableTaskRequest_Message() {} + +func (*DurableTaskRequest_Event) isDurableTaskRequest_Message() {} + +func (*DurableTaskRequest_EvictInvocation) isDurableTaskRequest_Message() {} + +func (*DurableTaskRequest_WorkerStatus) isDurableTaskRequest_Message() {} + +type DurableTaskResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Message: + // + // *DurableTaskResponse_RegisterWorker + // *DurableTaskResponse_TriggerAck + // *DurableTaskResponse_CallbackCompleted + Message isDurableTaskResponse_Message `protobuf_oneof:"message"` +} + +func (x *DurableTaskResponse) Reset() { + *x = DurableTaskResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_dispatcher_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DurableTaskResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DurableTaskResponse) ProtoMessage() {} + +func (x *DurableTaskResponse) ProtoReflect() protoreflect.Message { + mi := &file_v1_dispatcher_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DurableTaskResponse.ProtoReflect.Descriptor instead. +func (*DurableTaskResponse) Descriptor() ([]byte, []int) { + return file_v1_dispatcher_proto_rawDescGZIP(), []int{9} +} + +func (m *DurableTaskResponse) GetMessage() isDurableTaskResponse_Message { + if m != nil { + return m.Message + } + return nil +} + +func (x *DurableTaskResponse) GetRegisterWorker() *DurableTaskResponseRegisterWorker { + if x, ok := x.GetMessage().(*DurableTaskResponse_RegisterWorker); ok { + return x.RegisterWorker + } + return nil +} + +func (x *DurableTaskResponse) GetTriggerAck() *DurableTaskEventAckResponse { + if x, ok := x.GetMessage().(*DurableTaskResponse_TriggerAck); ok { + return x.TriggerAck + } + return nil +} + +func (x *DurableTaskResponse) GetCallbackCompleted() *DurableTaskCallbackCompletedResponse { + if x, ok := x.GetMessage().(*DurableTaskResponse_CallbackCompleted); ok { + return x.CallbackCompleted + } + return nil +} + +type isDurableTaskResponse_Message interface { + isDurableTaskResponse_Message() +} + +type DurableTaskResponse_RegisterWorker struct { + RegisterWorker *DurableTaskResponseRegisterWorker `protobuf:"bytes,1,opt,name=register_worker,json=registerWorker,proto3,oneof"` +} + +type DurableTaskResponse_TriggerAck struct { + TriggerAck *DurableTaskEventAckResponse `protobuf:"bytes,2,opt,name=trigger_ack,json=triggerAck,proto3,oneof"` +} + +type DurableTaskResponse_CallbackCompleted struct { + CallbackCompleted *DurableTaskCallbackCompletedResponse `protobuf:"bytes,3,opt,name=callback_completed,json=callbackCompleted,proto3,oneof"` +} + +func (*DurableTaskResponse_RegisterWorker) isDurableTaskResponse_Message() {} + +func (*DurableTaskResponse_TriggerAck) isDurableTaskResponse_Message() {} + +func (*DurableTaskResponse_CallbackCompleted) isDurableTaskResponse_Message() {} + type RegisterDurableEventRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -33,7 +783,7 @@ type RegisterDurableEventRequest struct { func (x *RegisterDurableEventRequest) Reset() { *x = RegisterDurableEventRequest{} if protoimpl.UnsafeEnabled { - mi := &file_v1_dispatcher_proto_msgTypes[0] + mi := &file_v1_dispatcher_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -46,7 +796,7 @@ func (x *RegisterDurableEventRequest) String() string { func (*RegisterDurableEventRequest) ProtoMessage() {} func (x *RegisterDurableEventRequest) ProtoReflect() protoreflect.Message { - mi := &file_v1_dispatcher_proto_msgTypes[0] + mi := &file_v1_dispatcher_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -59,7 +809,7 @@ func (x *RegisterDurableEventRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RegisterDurableEventRequest.ProtoReflect.Descriptor instead. func (*RegisterDurableEventRequest) Descriptor() ([]byte, []int) { - return file_v1_dispatcher_proto_rawDescGZIP(), []int{0} + return file_v1_dispatcher_proto_rawDescGZIP(), []int{10} } func (x *RegisterDurableEventRequest) GetTaskId() string { @@ -92,7 +842,7 @@ type RegisterDurableEventResponse struct { func (x *RegisterDurableEventResponse) Reset() { *x = RegisterDurableEventResponse{} if protoimpl.UnsafeEnabled { - mi := &file_v1_dispatcher_proto_msgTypes[1] + mi := &file_v1_dispatcher_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -105,7 +855,7 @@ func (x *RegisterDurableEventResponse) String() string { func (*RegisterDurableEventResponse) ProtoMessage() {} func (x *RegisterDurableEventResponse) ProtoReflect() protoreflect.Message { - mi := &file_v1_dispatcher_proto_msgTypes[1] + mi := &file_v1_dispatcher_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -118,7 +868,7 @@ func (x *RegisterDurableEventResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RegisterDurableEventResponse.ProtoReflect.Descriptor instead. func (*RegisterDurableEventResponse) Descriptor() ([]byte, []int) { - return file_v1_dispatcher_proto_rawDescGZIP(), []int{1} + return file_v1_dispatcher_proto_rawDescGZIP(), []int{11} } type ListenForDurableEventRequest struct { @@ -133,7 +883,7 @@ type ListenForDurableEventRequest struct { func (x *ListenForDurableEventRequest) Reset() { *x = ListenForDurableEventRequest{} if protoimpl.UnsafeEnabled { - mi := &file_v1_dispatcher_proto_msgTypes[2] + mi := &file_v1_dispatcher_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -146,7 +896,7 @@ func (x *ListenForDurableEventRequest) String() string { func (*ListenForDurableEventRequest) ProtoMessage() {} func (x *ListenForDurableEventRequest) ProtoReflect() protoreflect.Message { - mi := &file_v1_dispatcher_proto_msgTypes[2] + mi := &file_v1_dispatcher_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -159,7 +909,7 @@ func (x *ListenForDurableEventRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListenForDurableEventRequest.ProtoReflect.Descriptor instead. func (*ListenForDurableEventRequest) Descriptor() ([]byte, []int) { - return file_v1_dispatcher_proto_rawDescGZIP(), []int{2} + return file_v1_dispatcher_proto_rawDescGZIP(), []int{12} } func (x *ListenForDurableEventRequest) GetTaskId() string { @@ -189,7 +939,7 @@ type DurableEvent struct { func (x *DurableEvent) Reset() { *x = DurableEvent{} if protoimpl.UnsafeEnabled { - mi := &file_v1_dispatcher_proto_msgTypes[3] + mi := &file_v1_dispatcher_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -202,7 +952,7 @@ func (x *DurableEvent) String() string { func (*DurableEvent) ProtoMessage() {} func (x *DurableEvent) ProtoReflect() protoreflect.Message { - mi := &file_v1_dispatcher_proto_msgTypes[3] + mi := &file_v1_dispatcher_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -215,7 +965,7 @@ func (x *DurableEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use DurableEvent.ProtoReflect.Descriptor instead. func (*DurableEvent) Descriptor() ([]byte, []int) { - return file_v1_dispatcher_proto_rawDescGZIP(), []int{3} + return file_v1_dispatcher_proto_rawDescGZIP(), []int{13} } func (x *DurableEvent) GetTaskId() string { @@ -245,47 +995,182 @@ var file_v1_dispatcher_proto_rawDesc = []byte{ 0x0a, 0x13, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x76, 0x31, 0x1a, 0x19, 0x76, 0x31, 0x2f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x99, 0x01, 0x0a, 0x1b, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x12, 0x1d, 0x0a, - 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x12, 0x42, 0x0a, 0x0a, - 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x22, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x22, 0x1e, 0x0a, 0x1c, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x44, 0x75, 0x72, 0x61, - 0x62, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x56, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x46, 0x6f, 0x72, 0x44, 0x75, 0x72, - 0x61, 0x62, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x17, 0x0a, 0x07, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x69, 0x67, - 0x6e, 0x61, 0x6c, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, - 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x22, 0x5a, 0x0a, 0x0c, 0x44, 0x75, 0x72, 0x61, - 0x62, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x61, 0x73, 0x6b, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x73, 0x6b, 0x49, - 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x6b, 0x65, 0x79, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, - 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, - 0x64, 0x61, 0x74, 0x61, 0x32, 0xbe, 0x01, 0x0a, 0x0c, 0x56, 0x31, 0x44, 0x69, 0x73, 0x70, 0x61, - 0x74, 0x63, 0x68, 0x65, 0x72, 0x12, 0x5b, 0x0a, 0x14, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1f, 0x2e, - 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x44, 0x75, 0x72, 0x61, 0x62, - 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, - 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x44, 0x75, 0x72, 0x61, - 0x62, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x51, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x46, 0x6f, 0x72, 0x44, - 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x20, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x46, 0x6f, 0x72, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, - 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, - 0x76, 0x31, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, - 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x42, 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x74, 0x63, 0x68, 0x65, 0x74, 0x2d, 0x64, 0x65, 0x76, 0x2f, - 0x68, 0x61, 0x74, 0x63, 0x68, 0x65, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x76, 0x31, 0x2f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2f, + 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x3f, 0x0a, + 0x20, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x65, + 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x22, 0x40, + 0x0a, 0x21, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x57, 0x6f, 0x72, + 0x6b, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, + 0x22, 0x9c, 0x03, 0x0a, 0x17, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x61, 0x73, 0x6b, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, + 0x69, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x69, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x37, 0x0a, 0x18, 0x64, 0x75, 0x72, 0x61, 0x62, + 0x6c, 0x65, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x64, 0x75, 0x72, 0x61, 0x62, + 0x6c, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, + 0x12, 0x2c, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, + 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x1d, + 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x48, + 0x00, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x88, 0x01, 0x01, 0x12, 0x57, 0x0a, + 0x13, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x76, 0x31, 0x2e, + 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, + 0x65, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x48, 0x01, + 0x52, 0x11, 0x77, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x88, 0x01, 0x01, 0x12, 0x42, 0x0a, 0x0c, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, + 0x72, 0x5f, 0x6f, 0x70, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x76, + 0x31, 0x2e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, + 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x02, 0x52, 0x0b, 0x74, 0x72, 0x69, 0x67, + 0x67, 0x65, 0x72, 0x4f, 0x70, 0x74, 0x73, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x70, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x77, 0x61, 0x69, 0x74, 0x5f, + 0x66, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x0f, + 0x0a, 0x0d, 0x5f, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x5f, 0x6f, 0x70, 0x74, 0x73, 0x22, + 0x9a, 0x01, 0x0a, 0x1b, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x41, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x69, 0x6e, 0x76, 0x6f, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x37, 0x0a, 0x18, 0x64, 0x75, + 0x72, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x64, 0x75, + 0x72, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x22, 0x92, 0x01, 0x0a, + 0x24, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x43, 0x61, 0x6c, 0x6c, + 0x62, 0x61, 0x63, 0x6b, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x18, 0x64, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, + 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x64, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, + 0x54, 0x61, 0x73, 0x6b, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x17, + 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x22, 0x87, 0x01, 0x0a, 0x21, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x61, 0x73, + 0x6b, 0x45, 0x76, 0x69, 0x63, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x76, 0x6f, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0f, 0x69, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x37, 0x0a, 0x18, 0x64, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x61, + 0x73, 0x6b, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x64, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x61, 0x73, + 0x6b, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x22, 0x6e, 0x0a, 0x1a, 0x44, + 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x41, 0x77, 0x61, 0x69, 0x74, 0x65, + 0x64, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x37, 0x0a, 0x18, 0x64, 0x75, 0x72, + 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x64, 0x75, 0x72, + 0x61, 0x62, 0x6c, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x22, 0xc0, 0x01, 0x0a, 0x1e, + 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x57, 0x6f, 0x72, 0x6b, 0x65, + 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, + 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x6e, + 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6e, 0x6f, + 0x64, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x69, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x49, + 0x64, 0x12, 0x4b, 0x0a, 0x11, 0x77, 0x61, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x61, 0x6c, + 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x76, + 0x31, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x41, 0x77, 0x61, + 0x69, 0x74, 0x65, 0x64, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x10, 0x77, 0x61, + 0x69, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x22, 0xc4, + 0x02, 0x0a, 0x12, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x0f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, + 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x57, 0x6f, + 0x72, 0x6b, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x62, + 0x6c, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x52, 0x0a, 0x10, 0x65, + 0x76, 0x69, 0x63, 0x74, 0x5f, 0x69, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x62, + 0x6c, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x45, 0x76, 0x69, 0x63, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0f, + 0x65, 0x76, 0x69, 0x63, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x49, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x75, 0x72, 0x61, + 0x62, 0x6c, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x77, 0x6f, + 0x72, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x09, 0x0a, 0x07, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x91, 0x02, 0x0a, 0x13, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, + 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, + 0x0f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x75, 0x72, 0x61, + 0x62, 0x6c, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x48, 0x00, 0x52, + 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x12, + 0x42, 0x0a, 0x0b, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x5f, 0x61, 0x63, 0x6b, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, + 0x65, 0x54, 0x61, 0x73, 0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x41, 0x63, 0x6b, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, + 0x41, 0x63, 0x6b, 0x12, 0x59, 0x0a, 0x12, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, + 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x28, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x61, 0x73, 0x6b, + 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, + 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x11, 0x63, 0x61, 0x6c, + 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x42, 0x09, + 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x99, 0x01, 0x0a, 0x1b, 0x52, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x61, 0x73, + 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x73, 0x6b, + 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x6b, 0x65, 0x79, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x4b, 0x65, + 0x79, 0x12, 0x42, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x62, + 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x43, + 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x1e, 0x0a, 0x1c, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x56, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x46, + 0x6f, 0x72, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x12, 0x1d, + 0x0a, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x22, 0x5a, 0x0a, + 0x0c, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x0a, + 0x07, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, + 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x2a, 0xb0, 0x01, 0x0a, 0x14, 0x44, 0x75, + 0x72, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4b, 0x69, + 0x6e, 0x64, 0x12, 0x29, 0x0a, 0x25, 0x44, 0x55, 0x52, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x54, 0x41, + 0x53, 0x4b, 0x5f, 0x54, 0x52, 0x49, 0x47, 0x47, 0x45, 0x52, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x21, 0x0a, + 0x1d, 0x44, 0x55, 0x52, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x54, 0x41, 0x53, 0x4b, 0x5f, 0x54, 0x52, + 0x49, 0x47, 0x47, 0x45, 0x52, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x52, 0x55, 0x4e, 0x10, 0x01, + 0x12, 0x26, 0x0a, 0x22, 0x44, 0x55, 0x52, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x54, 0x41, 0x53, 0x4b, + 0x5f, 0x54, 0x52, 0x49, 0x47, 0x47, 0x45, 0x52, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x57, 0x41, + 0x49, 0x54, 0x5f, 0x46, 0x4f, 0x52, 0x10, 0x02, 0x12, 0x22, 0x0a, 0x1e, 0x44, 0x55, 0x52, 0x41, + 0x42, 0x4c, 0x45, 0x5f, 0x54, 0x41, 0x53, 0x4b, 0x5f, 0x54, 0x52, 0x49, 0x47, 0x47, 0x45, 0x52, + 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4d, 0x45, 0x4d, 0x4f, 0x10, 0x03, 0x32, 0x84, 0x02, 0x0a, + 0x0c, 0x56, 0x31, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x12, 0x44, 0x0a, + 0x0b, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x16, 0x2e, 0x76, + 0x31, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, + 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, + 0x01, 0x30, 0x01, 0x12, 0x5b, 0x0a, 0x14, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x44, + 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1f, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, + 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x51, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x46, 0x6f, 0x72, 0x44, 0x75, 0x72, + 0x61, 0x62, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x65, 0x6e, 0x46, 0x6f, 0x72, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x76, 0x31, + 0x2e, 0x44, 0x75, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x00, 0x28, + 0x01, 0x30, 0x01, 0x42, 0x42, 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x68, 0x61, 0x74, 0x63, 0x68, 0x65, 0x74, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x68, 0x61, + 0x74, 0x63, 0x68, 0x65, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -300,25 +1185,51 @@ func file_v1_dispatcher_proto_rawDescGZIP() []byte { return file_v1_dispatcher_proto_rawDescData } -var file_v1_dispatcher_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_v1_dispatcher_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_v1_dispatcher_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_v1_dispatcher_proto_goTypes = []interface{}{ - (*RegisterDurableEventRequest)(nil), // 0: v1.RegisterDurableEventRequest - (*RegisterDurableEventResponse)(nil), // 1: v1.RegisterDurableEventResponse - (*ListenForDurableEventRequest)(nil), // 2: v1.ListenForDurableEventRequest - (*DurableEvent)(nil), // 3: v1.DurableEvent - (*DurableEventListenerConditions)(nil), // 4: v1.DurableEventListenerConditions + (DurableTaskEventKind)(0), // 0: v1.DurableTaskEventKind + (*DurableTaskRequestRegisterWorker)(nil), // 1: v1.DurableTaskRequestRegisterWorker + (*DurableTaskResponseRegisterWorker)(nil), // 2: v1.DurableTaskResponseRegisterWorker + (*DurableTaskEventRequest)(nil), // 3: v1.DurableTaskEventRequest + (*DurableTaskEventAckResponse)(nil), // 4: v1.DurableTaskEventAckResponse + (*DurableTaskCallbackCompletedResponse)(nil), // 5: v1.DurableTaskCallbackCompletedResponse + (*DurableTaskEvictInvocationRequest)(nil), // 6: v1.DurableTaskEvictInvocationRequest + (*DurableTaskAwaitedCallback)(nil), // 7: v1.DurableTaskAwaitedCallback + (*DurableTaskWorkerStatusRequest)(nil), // 8: v1.DurableTaskWorkerStatusRequest + (*DurableTaskRequest)(nil), // 9: v1.DurableTaskRequest + (*DurableTaskResponse)(nil), // 10: v1.DurableTaskResponse + (*RegisterDurableEventRequest)(nil), // 11: v1.RegisterDurableEventRequest + (*RegisterDurableEventResponse)(nil), // 12: v1.RegisterDurableEventResponse + (*ListenForDurableEventRequest)(nil), // 13: v1.ListenForDurableEventRequest + (*DurableEvent)(nil), // 14: v1.DurableEvent + (*DurableEventListenerConditions)(nil), // 15: v1.DurableEventListenerConditions + (*TriggerWorkflowRequest)(nil), // 16: v1.TriggerWorkflowRequest } var file_v1_dispatcher_proto_depIdxs = []int32{ - 4, // 0: v1.RegisterDurableEventRequest.conditions:type_name -> v1.DurableEventListenerConditions - 0, // 1: v1.V1Dispatcher.RegisterDurableEvent:input_type -> v1.RegisterDurableEventRequest - 2, // 2: v1.V1Dispatcher.ListenForDurableEvent:input_type -> v1.ListenForDurableEventRequest - 1, // 3: v1.V1Dispatcher.RegisterDurableEvent:output_type -> v1.RegisterDurableEventResponse - 3, // 4: v1.V1Dispatcher.ListenForDurableEvent:output_type -> v1.DurableEvent - 3, // [3:5] is the sub-list for method output_type - 1, // [1:3] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name + 0, // 0: v1.DurableTaskEventRequest.kind:type_name -> v1.DurableTaskEventKind + 15, // 1: v1.DurableTaskEventRequest.wait_for_conditions:type_name -> v1.DurableEventListenerConditions + 16, // 2: v1.DurableTaskEventRequest.trigger_opts:type_name -> v1.TriggerWorkflowRequest + 7, // 3: v1.DurableTaskWorkerStatusRequest.waiting_callbacks:type_name -> v1.DurableTaskAwaitedCallback + 1, // 4: v1.DurableTaskRequest.register_worker:type_name -> v1.DurableTaskRequestRegisterWorker + 3, // 5: v1.DurableTaskRequest.event:type_name -> v1.DurableTaskEventRequest + 6, // 6: v1.DurableTaskRequest.evict_invocation:type_name -> v1.DurableTaskEvictInvocationRequest + 8, // 7: v1.DurableTaskRequest.worker_status:type_name -> v1.DurableTaskWorkerStatusRequest + 2, // 8: v1.DurableTaskResponse.register_worker:type_name -> v1.DurableTaskResponseRegisterWorker + 4, // 9: v1.DurableTaskResponse.trigger_ack:type_name -> v1.DurableTaskEventAckResponse + 5, // 10: v1.DurableTaskResponse.callback_completed:type_name -> v1.DurableTaskCallbackCompletedResponse + 15, // 11: v1.RegisterDurableEventRequest.conditions:type_name -> v1.DurableEventListenerConditions + 9, // 12: v1.V1Dispatcher.DurableTask:input_type -> v1.DurableTaskRequest + 11, // 13: v1.V1Dispatcher.RegisterDurableEvent:input_type -> v1.RegisterDurableEventRequest + 13, // 14: v1.V1Dispatcher.ListenForDurableEvent:input_type -> v1.ListenForDurableEventRequest + 10, // 15: v1.V1Dispatcher.DurableTask:output_type -> v1.DurableTaskResponse + 12, // 16: v1.V1Dispatcher.RegisterDurableEvent:output_type -> v1.RegisterDurableEventResponse + 14, // 17: v1.V1Dispatcher.ListenForDurableEvent:output_type -> v1.DurableEvent + 15, // [15:18] is the sub-list for method output_type + 12, // [12:15] is the sub-list for method input_type + 12, // [12:12] is the sub-list for extension type_name + 12, // [12:12] is the sub-list for extension extendee + 0, // [0:12] is the sub-list for field type_name } func init() { file_v1_dispatcher_proto_init() } @@ -327,9 +1238,10 @@ func file_v1_dispatcher_proto_init() { return } file_v1_shared_condition_proto_init() + file_v1_shared_trigger_proto_init() if !protoimpl.UnsafeEnabled { file_v1_dispatcher_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RegisterDurableEventRequest); i { + switch v := v.(*DurableTaskRequestRegisterWorker); i { case 0: return &v.state case 1: @@ -341,7 +1253,7 @@ func file_v1_dispatcher_proto_init() { } } file_v1_dispatcher_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RegisterDurableEventResponse); i { + switch v := v.(*DurableTaskResponseRegisterWorker); i { case 0: return &v.state case 1: @@ -353,7 +1265,7 @@ func file_v1_dispatcher_proto_init() { } } file_v1_dispatcher_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListenForDurableEventRequest); i { + switch v := v.(*DurableTaskEventRequest); i { case 0: return &v.state case 1: @@ -365,6 +1277,126 @@ func file_v1_dispatcher_proto_init() { } } file_v1_dispatcher_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DurableTaskEventAckResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_v1_dispatcher_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DurableTaskCallbackCompletedResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_v1_dispatcher_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DurableTaskEvictInvocationRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_v1_dispatcher_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DurableTaskAwaitedCallback); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_v1_dispatcher_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DurableTaskWorkerStatusRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_v1_dispatcher_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DurableTaskRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_v1_dispatcher_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DurableTaskResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_v1_dispatcher_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterDurableEventRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_v1_dispatcher_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterDurableEventResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_v1_dispatcher_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListenForDurableEventRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_v1_dispatcher_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DurableEvent); i { case 0: return &v.state @@ -377,18 +1409,31 @@ func file_v1_dispatcher_proto_init() { } } } + file_v1_dispatcher_proto_msgTypes[2].OneofWrappers = []interface{}{} + file_v1_dispatcher_proto_msgTypes[8].OneofWrappers = []interface{}{ + (*DurableTaskRequest_RegisterWorker)(nil), + (*DurableTaskRequest_Event)(nil), + (*DurableTaskRequest_EvictInvocation)(nil), + (*DurableTaskRequest_WorkerStatus)(nil), + } + file_v1_dispatcher_proto_msgTypes[9].OneofWrappers = []interface{}{ + (*DurableTaskResponse_RegisterWorker)(nil), + (*DurableTaskResponse_TriggerAck)(nil), + (*DurableTaskResponse_CallbackCompleted)(nil), + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_v1_dispatcher_proto_rawDesc, - NumEnums: 0, - NumMessages: 4, + NumEnums: 1, + NumMessages: 14, NumExtensions: 0, NumServices: 1, }, GoTypes: file_v1_dispatcher_proto_goTypes, DependencyIndexes: file_v1_dispatcher_proto_depIdxs, + EnumInfos: file_v1_dispatcher_proto_enumTypes, MessageInfos: file_v1_dispatcher_proto_msgTypes, }.Build() File_v1_dispatcher_proto = out.File diff --git a/internal/services/shared/proto/v1/dispatcher_grpc.pb.go b/internal/services/shared/proto/v1/dispatcher_grpc.pb.go index 676e5ce92..953cce76b 100644 --- a/internal/services/shared/proto/v1/dispatcher_grpc.pb.go +++ b/internal/services/shared/proto/v1/dispatcher_grpc.pb.go @@ -22,6 +22,8 @@ const _ = grpc.SupportPackageIsVersion7 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type V1DispatcherClient interface { + DurableTask(ctx context.Context, opts ...grpc.CallOption) (V1Dispatcher_DurableTaskClient, error) + // NOTE: deprecated after DurableEventLog is implemented RegisterDurableEvent(ctx context.Context, in *RegisterDurableEventRequest, opts ...grpc.CallOption) (*RegisterDurableEventResponse, error) ListenForDurableEvent(ctx context.Context, opts ...grpc.CallOption) (V1Dispatcher_ListenForDurableEventClient, error) } @@ -34,6 +36,37 @@ func NewV1DispatcherClient(cc grpc.ClientConnInterface) V1DispatcherClient { return &v1DispatcherClient{cc} } +func (c *v1DispatcherClient) DurableTask(ctx context.Context, opts ...grpc.CallOption) (V1Dispatcher_DurableTaskClient, error) { + stream, err := c.cc.NewStream(ctx, &V1Dispatcher_ServiceDesc.Streams[0], "/v1.V1Dispatcher/DurableTask", opts...) + if err != nil { + return nil, err + } + x := &v1DispatcherDurableTaskClient{stream} + return x, nil +} + +type V1Dispatcher_DurableTaskClient interface { + Send(*DurableTaskRequest) error + Recv() (*DurableTaskResponse, error) + grpc.ClientStream +} + +type v1DispatcherDurableTaskClient struct { + grpc.ClientStream +} + +func (x *v1DispatcherDurableTaskClient) Send(m *DurableTaskRequest) error { + return x.ClientStream.SendMsg(m) +} + +func (x *v1DispatcherDurableTaskClient) Recv() (*DurableTaskResponse, error) { + m := new(DurableTaskResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + func (c *v1DispatcherClient) RegisterDurableEvent(ctx context.Context, in *RegisterDurableEventRequest, opts ...grpc.CallOption) (*RegisterDurableEventResponse, error) { out := new(RegisterDurableEventResponse) err := c.cc.Invoke(ctx, "/v1.V1Dispatcher/RegisterDurableEvent", in, out, opts...) @@ -44,7 +77,7 @@ func (c *v1DispatcherClient) RegisterDurableEvent(ctx context.Context, in *Regis } func (c *v1DispatcherClient) ListenForDurableEvent(ctx context.Context, opts ...grpc.CallOption) (V1Dispatcher_ListenForDurableEventClient, error) { - stream, err := c.cc.NewStream(ctx, &V1Dispatcher_ServiceDesc.Streams[0], "/v1.V1Dispatcher/ListenForDurableEvent", opts...) + stream, err := c.cc.NewStream(ctx, &V1Dispatcher_ServiceDesc.Streams[1], "/v1.V1Dispatcher/ListenForDurableEvent", opts...) if err != nil { return nil, err } @@ -78,6 +111,8 @@ func (x *v1DispatcherListenForDurableEventClient) Recv() (*DurableEvent, error) // All implementations must embed UnimplementedV1DispatcherServer // for forward compatibility type V1DispatcherServer interface { + DurableTask(V1Dispatcher_DurableTaskServer) error + // NOTE: deprecated after DurableEventLog is implemented RegisterDurableEvent(context.Context, *RegisterDurableEventRequest) (*RegisterDurableEventResponse, error) ListenForDurableEvent(V1Dispatcher_ListenForDurableEventServer) error mustEmbedUnimplementedV1DispatcherServer() @@ -87,6 +122,9 @@ type V1DispatcherServer interface { type UnimplementedV1DispatcherServer struct { } +func (UnimplementedV1DispatcherServer) DurableTask(V1Dispatcher_DurableTaskServer) error { + return status.Errorf(codes.Unimplemented, "method DurableTask not implemented") +} func (UnimplementedV1DispatcherServer) RegisterDurableEvent(context.Context, *RegisterDurableEventRequest) (*RegisterDurableEventResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method RegisterDurableEvent not implemented") } @@ -106,6 +144,32 @@ func RegisterV1DispatcherServer(s grpc.ServiceRegistrar, srv V1DispatcherServer) s.RegisterService(&V1Dispatcher_ServiceDesc, srv) } +func _V1Dispatcher_DurableTask_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(V1DispatcherServer).DurableTask(&v1DispatcherDurableTaskServer{stream}) +} + +type V1Dispatcher_DurableTaskServer interface { + Send(*DurableTaskResponse) error + Recv() (*DurableTaskRequest, error) + grpc.ServerStream +} + +type v1DispatcherDurableTaskServer struct { + grpc.ServerStream +} + +func (x *v1DispatcherDurableTaskServer) Send(m *DurableTaskResponse) error { + return x.ServerStream.SendMsg(m) +} + +func (x *v1DispatcherDurableTaskServer) Recv() (*DurableTaskRequest, error) { + m := new(DurableTaskRequest) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + func _V1Dispatcher_RegisterDurableEvent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(RegisterDurableEventRequest) if err := dec(in); err != nil { @@ -163,6 +227,12 @@ var V1Dispatcher_ServiceDesc = grpc.ServiceDesc{ }, }, Streams: []grpc.StreamDesc{ + { + StreamName: "DurableTask", + Handler: _V1Dispatcher_DurableTask_Handler, + ServerStreams: true, + ClientStreams: true, + }, { StreamName: "ListenForDurableEvent", Handler: _V1Dispatcher_ListenForDurableEvent_Handler, diff --git a/internal/services/shared/proto/v1/trigger.pb.go b/internal/services/shared/proto/v1/trigger.pb.go new file mode 100644 index 000000000..34da821c7 --- /dev/null +++ b/internal/services/shared/proto/v1/trigger.pb.go @@ -0,0 +1,253 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v5.29.3 +// source: v1/shared/trigger.proto + +package v1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type TriggerWorkflowRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // (optional) the input data for the workflow + Input string `protobuf:"bytes,2,opt,name=input,proto3" json:"input,omitempty"` + // (optional) the parent workflow run id + ParentId *string `protobuf:"bytes,3,opt,name=parent_id,json=parentId,proto3,oneof" json:"parent_id,omitempty"` + // (optional) the parent task external run id + ParentTaskRunExternalId *string `protobuf:"bytes,4,opt,name=parent_task_run_external_id,json=parentTaskRunExternalId,proto3,oneof" json:"parent_task_run_external_id,omitempty"` + // (optional) the index of the child workflow. if this is set, matches on the index or the + // child key will return an existing workflow run if the parent id, parent task run id, and + // child index/key match an existing workflow run. + ChildIndex *int32 `protobuf:"varint,5,opt,name=child_index,json=childIndex,proto3,oneof" json:"child_index,omitempty"` + // (optional) the key for the child. if this is set, matches on the index or the + // child key will return an existing workflow run if the parent id, parent task run id, and + // child index/key match an existing workflow run. + ChildKey *string `protobuf:"bytes,6,opt,name=child_key,json=childKey,proto3,oneof" json:"child_key,omitempty"` + // (optional) additional metadata for the workflow + AdditionalMetadata *string `protobuf:"bytes,7,opt,name=additional_metadata,json=additionalMetadata,proto3,oneof" json:"additional_metadata,omitempty"` + // (optional) desired worker id for the workflow run, + // requires the workflow definition to have a sticky strategy + DesiredWorkerId *string `protobuf:"bytes,8,opt,name=desired_worker_id,json=desiredWorkerId,proto3,oneof" json:"desired_worker_id,omitempty"` + // (optional) override for the priority of the workflow tasks, will set all tasks to this priority + Priority *int32 `protobuf:"varint,9,opt,name=priority,proto3,oneof" json:"priority,omitempty"` +} + +func (x *TriggerWorkflowRequest) Reset() { + *x = TriggerWorkflowRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_shared_trigger_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TriggerWorkflowRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TriggerWorkflowRequest) ProtoMessage() {} + +func (x *TriggerWorkflowRequest) ProtoReflect() protoreflect.Message { + mi := &file_v1_shared_trigger_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TriggerWorkflowRequest.ProtoReflect.Descriptor instead. +func (*TriggerWorkflowRequest) Descriptor() ([]byte, []int) { + return file_v1_shared_trigger_proto_rawDescGZIP(), []int{0} +} + +func (x *TriggerWorkflowRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *TriggerWorkflowRequest) GetInput() string { + if x != nil { + return x.Input + } + return "" +} + +func (x *TriggerWorkflowRequest) GetParentId() string { + if x != nil && x.ParentId != nil { + return *x.ParentId + } + return "" +} + +func (x *TriggerWorkflowRequest) GetParentTaskRunExternalId() string { + if x != nil && x.ParentTaskRunExternalId != nil { + return *x.ParentTaskRunExternalId + } + return "" +} + +func (x *TriggerWorkflowRequest) GetChildIndex() int32 { + if x != nil && x.ChildIndex != nil { + return *x.ChildIndex + } + return 0 +} + +func (x *TriggerWorkflowRequest) GetChildKey() string { + if x != nil && x.ChildKey != nil { + return *x.ChildKey + } + return "" +} + +func (x *TriggerWorkflowRequest) GetAdditionalMetadata() string { + if x != nil && x.AdditionalMetadata != nil { + return *x.AdditionalMetadata + } + return "" +} + +func (x *TriggerWorkflowRequest) GetDesiredWorkerId() string { + if x != nil && x.DesiredWorkerId != nil { + return *x.DesiredWorkerId + } + return "" +} + +func (x *TriggerWorkflowRequest) GetPriority() int32 { + if x != nil && x.Priority != nil { + return *x.Priority + } + return 0 +} + +var File_v1_shared_trigger_proto protoreflect.FileDescriptor + +var file_v1_shared_trigger_proto_rawDesc = []byte{ + 0x0a, 0x17, 0x76, 0x31, 0x2f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2f, 0x74, 0x72, 0x69, 0x67, + 0x67, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x76, 0x31, 0x22, 0xfe, 0x03, + 0x0a, 0x16, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, + 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6e, 0x70, + 0x75, 0x74, 0x12, 0x20, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, + 0x64, 0x88, 0x01, 0x01, 0x12, 0x41, 0x0a, 0x1b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x74, + 0x61, 0x73, 0x6b, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x17, 0x70, 0x61, 0x72, + 0x65, 0x6e, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x75, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x0b, 0x63, 0x68, 0x69, 0x6c, 0x64, + 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x48, 0x02, 0x52, 0x0a, + 0x63, 0x68, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, + 0x09, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x03, 0x52, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x4b, 0x65, 0x79, 0x88, 0x01, 0x01, 0x12, + 0x34, 0x0a, 0x13, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x6d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x48, 0x04, 0x52, 0x12, + 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a, 0x11, 0x64, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, + 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x05, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x65, + 0x72, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, + 0x74, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x48, 0x06, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x70, 0x61, 0x72, 0x65, + 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x42, 0x1e, 0x0a, 0x1c, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, + 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, + 0x6b, 0x65, 0x79, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x61, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x14, 0x0a, 0x12, 0x5f, + 0x64, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, + 0x64, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x42, 0x42, + 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x74, + 0x63, 0x68, 0x65, 0x74, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x68, 0x61, 0x74, 0x63, 0x68, 0x65, 0x74, + 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x2f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_v1_shared_trigger_proto_rawDescOnce sync.Once + file_v1_shared_trigger_proto_rawDescData = file_v1_shared_trigger_proto_rawDesc +) + +func file_v1_shared_trigger_proto_rawDescGZIP() []byte { + file_v1_shared_trigger_proto_rawDescOnce.Do(func() { + file_v1_shared_trigger_proto_rawDescData = protoimpl.X.CompressGZIP(file_v1_shared_trigger_proto_rawDescData) + }) + return file_v1_shared_trigger_proto_rawDescData +} + +var file_v1_shared_trigger_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_v1_shared_trigger_proto_goTypes = []interface{}{ + (*TriggerWorkflowRequest)(nil), // 0: v1.TriggerWorkflowRequest +} +var file_v1_shared_trigger_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_v1_shared_trigger_proto_init() } +func file_v1_shared_trigger_proto_init() { + if File_v1_shared_trigger_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_v1_shared_trigger_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TriggerWorkflowRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_v1_shared_trigger_proto_msgTypes[0].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_v1_shared_trigger_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_v1_shared_trigger_proto_goTypes, + DependencyIndexes: file_v1_shared_trigger_proto_depIdxs, + MessageInfos: file_v1_shared_trigger_proto_msgTypes, + }.Build() + File_v1_shared_trigger_proto = out.File + file_v1_shared_trigger_proto_rawDesc = nil + file_v1_shared_trigger_proto_goTypes = nil + file_v1_shared_trigger_proto_depIdxs = nil +} diff --git a/internal/services/shared/tasktypes/v1/task.go b/internal/services/shared/tasktypes/v1/task.go index c1743fd33..094ebaa5f 100644 --- a/internal/services/shared/tasktypes/v1/task.go +++ b/internal/services/shared/tasktypes/v1/task.go @@ -214,3 +214,26 @@ type CandidateFinalizedPayload struct { // (required) the workflow run id (can either be a workflow run id or single task) WorkflowRunId uuid.UUID `validate:"required"` } + +type DurableCallbackCompletedPayload struct { + TaskExternalId uuid.UUID + NodeId int64 + InvocationCount int64 + Payload []byte +} + +func DurableCallbackCompletedMessage( + tenantId, taskExternalId uuid.UUID, nodeId int64, payload []byte, +) (*msgqueue.Message, error) { + return msgqueue.NewTenantMessage( + tenantId, + msgqueue.MsgIDDurableCallbackCompleted, + false, + true, + DurableCallbackCompletedPayload{ + TaskExternalId: taskExternalId, + NodeId: nodeId, + Payload: payload, + }, + ) +} diff --git a/pkg/client/admin.go b/pkg/client/admin.go index 810a4a0ea..f980edb30 100644 --- a/pkg/client/admin.go +++ b/pkg/client/admin.go @@ -16,12 +16,11 @@ import ( "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" - admincontracts "github.com/hatchet-dev/hatchet/internal/services/admin/contracts" + admincontracts "github.com/hatchet-dev/hatchet/internal/services/admin/contracts/workflows" + v1contracts "github.com/hatchet-dev/hatchet/internal/services/shared/proto/v1" "github.com/hatchet-dev/hatchet/pkg/client/types" "github.com/hatchet-dev/hatchet/pkg/config/client" "github.com/hatchet-dev/hatchet/pkg/validator" - - v1contracts "github.com/hatchet-dev/hatchet/internal/services/shared/proto/v1" ) type ChildWorkflowOpts struct { @@ -216,10 +215,10 @@ func (a *adminClientImpl) ScheduleWorkflow(workflowName string, fs ...ScheduleOp return nil } -type RunOptFunc func(*admincontracts.TriggerWorkflowRequest) error +type RunOptFunc func(*v1contracts.TriggerWorkflowRequest) error func WithRunMetadata(metadata interface{}) RunOptFunc { - return func(r *admincontracts.TriggerWorkflowRequest) error { + return func(r *v1contracts.TriggerWorkflowRequest) error { metadataBytes, err := json.Marshal(metadata) if err != nil { return err @@ -234,7 +233,7 @@ func WithRunMetadata(metadata interface{}) RunOptFunc { } func WithPriority(priority int32) RunOptFunc { - return func(r *admincontracts.TriggerWorkflowRequest) error { + return func(r *v1contracts.TriggerWorkflowRequest) error { r.Priority = &priority return nil @@ -258,7 +257,7 @@ func (a *adminClientImpl) RunWorkflow(workflowName string, input interface{}, op workflowName = client.ApplyNamespace(workflowName, &a.namespace) - request := &admincontracts.TriggerWorkflowRequest{ + request := &v1contracts.TriggerWorkflowRequest{ Name: workflowName, Input: string(inputBytes), } @@ -296,7 +295,7 @@ func (a *adminClientImpl) RunWorkflow(workflowName string, input interface{}, op func (a *adminClientImpl) BulkRunWorkflow(workflows []*WorkflowRun) ([]string, error) { - triggerWorkflowRequests := make([]*admincontracts.TriggerWorkflowRequest, len(workflows)) + triggerWorkflowRequests := make([]*v1contracts.TriggerWorkflowRequest, len(workflows)) for i, workflow := range workflows { inputBytes, err := json.Marshal(workflow.Input) @@ -305,7 +304,7 @@ func (a *adminClientImpl) BulkRunWorkflow(workflows []*WorkflowRun) ([]string, e } workflowName := client.ApplyNamespace(workflow.Name, &a.namespace) - triggerWorkflowRequests[i] = &admincontracts.TriggerWorkflowRequest{ + triggerWorkflowRequests[i] = &v1contracts.TriggerWorkflowRequest{ Name: workflowName, Input: string(inputBytes), } @@ -351,7 +350,7 @@ func (a *adminClientImpl) RunChildWorkflow(workflowName string, input interface{ metadata := string(metadataBytes) - res, err := a.client.TriggerWorkflow(a.ctx.newContext(context.Background()), &admincontracts.TriggerWorkflowRequest{ + res, err := a.client.TriggerWorkflow(a.ctx.newContext(context.Background()), &v1contracts.TriggerWorkflowRequest{ Name: workflowName, Input: string(inputBytes), ParentId: &opts.ParentId, @@ -385,7 +384,7 @@ type RunChildWorkflowsOpts struct { func (a *adminClientImpl) RunChildWorkflows(workflows []*RunChildWorkflowsOpts) ([]string, error) { - triggerWorkflowRequests := make([]*admincontracts.TriggerWorkflowRequest, len(workflows)) + triggerWorkflowRequests := make([]*v1contracts.TriggerWorkflowRequest, len(workflows)) for i, workflow := range workflows { if workflow.Opts == nil { @@ -413,7 +412,7 @@ func (a *adminClientImpl) RunChildWorkflows(workflows []*RunChildWorkflowsOpts) metadata := string(metadataBytes) - triggerWorkflowRequests[i] = &admincontracts.TriggerWorkflowRequest{ + triggerWorkflowRequests[i] = &v1contracts.TriggerWorkflowRequest{ Name: workflowName, Input: string(inputBytes), ParentId: &workflow.Opts.ParentId, diff --git a/pkg/repository/durable_events.go b/pkg/repository/durable_events.go index 22000bd94..d33e50cf9 100644 --- a/pkg/repository/durable_events.go +++ b/pkg/repository/durable_events.go @@ -2,59 +2,66 @@ package repository import ( "context" - "crypto/sha256" + "errors" + "fmt" + "time" "github.com/google/uuid" + "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgtype" "github.com/hatchet-dev/hatchet/pkg/repository/sqlchelpers" "github.com/hatchet-dev/hatchet/pkg/repository/sqlcv1" ) -type CreateEventLogFileOpts struct { - DurableTaskId int64 - DurableTaskInsertedAt pgtype.Timestamptz - LatestInsertedAt pgtype.Timestamptz - LatestNodeId int64 - LatestBranchId int64 - LatestBranchFirstParentNodeId int64 +type EventLogCallbackWithPayload struct { + Callback *sqlcv1.V1DurableEventLogCallback + Result []byte + AlreadyExisted bool } -type CreateEventLogEntryOpts struct { - TenantId uuid.UUID - ExternalId uuid.UUID - DurableTaskId int64 - DurableTaskInsertedAt pgtype.Timestamptz - InsertedAt pgtype.Timestamptz - Kind string - NodeId int64 - ParentNodeId int64 - BranchId int64 - Data []byte +type EventLogEntryWithPayload struct { + Entry *sqlcv1.V1DurableEventLogEntry + Payload []byte + AlreadyExisted bool } -type CreateEventLogCallbackOpts struct { - DurableTaskId int64 - DurableTaskInsertedAt pgtype.Timestamptz - InsertedAt pgtype.Timestamptz - Kind string - Key string - NodeId int64 - IsSatisfied bool +type TaskExternalIdNodeId struct { + TaskExternalId uuid.UUID `validate:"required"` + NodeId int64 `validate:"required"` +} + +type SatisfiedCallbackWithPayload struct { + TaskExternalId uuid.UUID + NodeID int64 + Result []byte +} + +type IngestDurableTaskEventOpts struct { + TenantId uuid.UUID `validate:"required"` + Task *sqlcv1.FlattenExternalIdsRow `validate:"required"` + Kind sqlcv1.V1DurableEventLogKind `validate:"required,oneof=RUN WAIT_FOR MEMO"` + Payload []byte + WaitForConditions []CreateExternalSignalConditionOpt + InvocationCount int64 + TriggerOpts *WorkflowNameTriggerOpts +} + +type IngestDurableTaskEventResult struct { + NodeId int64 + Callback *EventLogCallbackWithPayload + EventLogEntry *EventLogEntryWithPayload + EventLogFile *sqlcv1.V1DurableEventLogFile + + // Populated for RUNTRIGGERED: the tasks/DAGs created by the child spawn. + CreatedTasks []*V1TaskWithPayload + CreatedDAGs []*DAGWithData } type DurableEventsRepository interface { - CreateEventLogFiles(ctx context.Context, opts []CreateEventLogFileOpts) ([]*sqlcv1.V1DurableEventLogFile, error) - GetEventLogFileForTask(ctx context.Context, durableTaskId int64, durableTaskInsertedAt pgtype.Timestamptz) (*sqlcv1.V1DurableEventLogFile, error) + IngestDurableTaskEvent(ctx context.Context, opts IngestDurableTaskEventOpts) (*IngestDurableTaskEventResult, error) - CreateEventLogEntries(ctx context.Context, opts []CreateEventLogEntryOpts) ([]*sqlcv1.V1DurableEventLogEntry, error) - GetEventLogEntry(ctx context.Context, durableTaskId int64, durableTaskInsertedAt pgtype.Timestamptz, nodeId int64) (*sqlcv1.V1DurableEventLogEntry, error) - ListEventLogEntries(ctx context.Context, durableTaskId int64, durableTaskInsertedAt pgtype.Timestamptz) ([]*sqlcv1.V1DurableEventLogEntry, error) - - CreateEventLogCallbacks(ctx context.Context, opts []CreateEventLogCallbackOpts) ([]*sqlcv1.V1DurableEventLogCallback, error) - GetEventLogCallback(ctx context.Context, durableTaskId int64, durableTaskInsertedAt pgtype.Timestamptz, key string) (*sqlcv1.V1DurableEventLogCallback, error) - ListEventLogCallbacks(ctx context.Context, durableTaskId int64, durableTaskInsertedAt pgtype.Timestamptz) ([]*sqlcv1.V1DurableEventLogCallback, error) - UpdateEventLogCallbackSatisfied(ctx context.Context, durableTaskId int64, durableTaskInsertedAt pgtype.Timestamptz, key string, isSatisfied bool) (*sqlcv1.V1DurableEventLogCallback, error) + GetSatisfiedCallbacks(ctx context.Context, tenantId uuid.UUID, callbacks []TaskExternalIdNodeId) ([]*SatisfiedCallbackWithPayload, error) } type durableEventsRepository struct { @@ -67,231 +74,449 @@ func newDurableEventsRepository(shared *sharedRepository) DurableEventsRepositor } } -func (r *durableEventsRepository) CreateEventLogFiles(ctx context.Context, opts []CreateEventLogFileOpts) ([]*sqlcv1.V1DurableEventLogFile, error) { - // note: might need to pass a tx in here instead - tx, commit, rollback, err := sqlchelpers.PrepareTx(ctx, r.pool, r.l) - if err != nil { - return nil, err - } - defer rollback() - - durableTaskIds := make([]int64, len(opts)) - durableTaskInsertedAts := make([]pgtype.Timestamptz, len(opts)) - latestInsertedAts := make([]pgtype.Timestamptz, len(opts)) - latestNodeIds := make([]int64, len(opts)) - latestBranchIds := make([]int64, len(opts)) - latestBranchFirstParentNodeIds := make([]int64, len(opts)) - - for i, opt := range opts { - durableTaskIds[i] = opt.DurableTaskId - durableTaskInsertedAts[i] = opt.DurableTaskInsertedAt - latestInsertedAts[i] = opt.LatestInsertedAt - latestNodeIds[i] = opt.LatestNodeId - latestBranchIds[i] = opt.LatestBranchId - latestBranchFirstParentNodeIds[i] = opt.LatestBranchFirstParentNodeId - } - - files, err := r.queries.CreateDurableEventLogFile(ctx, tx, sqlcv1.CreateDurableEventLogFileParams{ - Durabletaskids: durableTaskIds, - Durabletaskinsertedats: durableTaskInsertedAts, - Latestinsertedats: latestInsertedAts, - Latestnodeids: latestNodeIds, - Latestbranchids: latestBranchIds, - Latestbranchfirstparentnodeids: latestBranchFirstParentNodeIds, +func (r *durableEventsRepository) getOrCreateEventLogEntry( + ctx context.Context, + tx sqlcv1.DBTX, + tenantId uuid.UUID, + params sqlcv1.CreateDurableEventLogEntryParams, + payload []byte, +) (*EventLogEntryWithPayload, error) { + alreadyExisted := true + entry, err := r.queries.GetDurableEventLogEntry(ctx, tx, sqlcv1.GetDurableEventLogEntryParams{ + Durabletaskid: params.Durabletaskid, + Durabletaskinsertedat: params.Durabletaskinsertedat, + Nodeid: params.Nodeid, }) - if err != nil { + + if err != nil && !errors.Is(err, pgx.ErrNoRows) { return nil, err - } + } else if errors.Is(err, pgx.ErrNoRows) { + alreadyExisted = false + entry, err := r.queries.CreateDurableEventLogEntry(ctx, tx, sqlcv1.CreateDurableEventLogEntryParams{ + Tenantid: params.Tenantid, + Externalid: params.Externalid, + Durabletaskid: params.Durabletaskid, + Durabletaskinsertedat: params.Durabletaskinsertedat, + Kind: params.Kind, + Nodeid: params.Nodeid, + ParentNodeId: params.ParentNodeId, + Branchid: params.Branchid, + Datahash: params.Datahash, + Datahashalg: params.Datahashalg, + }) - if err := commit(ctx); err != nil { - return nil, err - } - - return files, nil -} - -func (r *durableEventsRepository) GetEventLogFileForTask(ctx context.Context, durableTaskId int64, durableTaskInsertedAt pgtype.Timestamptz) (*sqlcv1.V1DurableEventLogFile, error) { - return r.queries.GetDurableEventLogFileForTask(ctx, r.pool, sqlcv1.GetDurableEventLogFileForTaskParams{ - Durabletaskid: durableTaskId, - Durabletaskinsertedat: durableTaskInsertedAt, - }) -} - -func (r *durableEventsRepository) CreateEventLogEntries(ctx context.Context, opts []CreateEventLogEntryOpts) ([]*sqlcv1.V1DurableEventLogEntry, error) { - // note: might need to pass a tx in here instead - tx, commit, rollback, err := sqlchelpers.PrepareTx(ctx, r.pool, r.l) - if err != nil { - return nil, err - } - defer rollback() - - externalIds := make([]uuid.UUID, len(opts)) - durableTaskIds := make([]int64, len(opts)) - durableTaskInsertedAts := make([]pgtype.Timestamptz, len(opts)) - insertedAts := make([]pgtype.Timestamptz, len(opts)) - kinds := make([]string, len(opts)) - nodeIds := make([]int64, len(opts)) - parentNodeIds := make([]int64, len(opts)) - branchIds := make([]int64, len(opts)) - dataHashes := make([][]byte, len(opts)) - dataHashAlgs := make([]string, len(opts)) - - payloadOpts := make([]StorePayloadOpts, 0, len(opts)) - - for i, opt := range opts { - externalIds[i] = opt.ExternalId - durableTaskIds[i] = opt.DurableTaskId - durableTaskInsertedAts[i] = opt.DurableTaskInsertedAt - insertedAts[i] = opt.InsertedAt - kinds[i] = opt.Kind - nodeIds[i] = opt.NodeId - parentNodeIds[i] = opt.ParentNodeId - branchIds[i] = opt.BranchId - - if len(opt.Data) > 0 { - hash := sha256.Sum256(opt.Data) - dataHashes[i] = hash[:] - dataHashAlgs[i] = "sha256" - - payloadOpts = append(payloadOpts, StorePayloadOpts{ - // todo: confirm node id + inserted at uniquely identifies an entry - Id: opt.NodeId, - InsertedAt: opt.InsertedAt, - ExternalId: opt.ExternalId, - Type: sqlcv1.V1PayloadTypeDURABLEEVENTLOGENTRYDATA, - Payload: opt.Data, - TenantId: opt.TenantId, - }) - } - } - - entries, err := r.queries.CreateDurableEventLogEntries(ctx, tx, sqlcv1.CreateDurableEventLogEntriesParams{ - Externalids: externalIds, - Durabletaskids: durableTaskIds, - Durabletaskinsertedats: durableTaskInsertedAts, - Insertedats: insertedAts, - Kinds: kinds, - Nodeids: nodeIds, - Parentnodeids: parentNodeIds, - Branchids: branchIds, - Datahashes: dataHashes, - Datahashalgs: dataHashAlgs, - }) - if err != nil { - return nil, err - } - - if len(payloadOpts) > 0 { - err = r.payloadStore.Store(ctx, tx, payloadOpts...) if err != nil { return nil, err } + + if len(payload) > 0 { + err = r.payloadStore.Store(ctx, tx, StorePayloadOpts{ + Id: entry.ID, + InsertedAt: entry.InsertedAt, + ExternalId: entry.ExternalID, + Type: sqlcv1.V1PayloadTypeDURABLEEVENTLOGENTRYDATA, + Payload: payload, + TenantId: tenantId, + }) + if err != nil { + return nil, err + } + } } - if err := commit(ctx); err != nil { - return nil, err - } - - return entries, nil + return &EventLogEntryWithPayload{Entry: entry, Payload: payload, AlreadyExisted: alreadyExisted}, nil } -func (r *durableEventsRepository) GetEventLogEntry(ctx context.Context, durableTaskId int64, durableTaskInsertedAt pgtype.Timestamptz, nodeId int64) (*sqlcv1.V1DurableEventLogEntry, error) { - return r.queries.GetDurableEventLogEntry(ctx, r.pool, sqlcv1.GetDurableEventLogEntryParams{ - Durabletaskid: durableTaskId, - Durabletaskinsertedat: durableTaskInsertedAt, +func (r *durableEventsRepository) getOrCreateEventLogCallback( + ctx context.Context, + tx sqlcv1.DBTX, + tenantId uuid.UUID, + params sqlcv1.CreateDurableEventLogCallbackParams, + payload []byte, +) (*EventLogCallbackWithPayload, error) { + alreadyExists := true + callback, err := r.queries.GetDurableEventLogCallback(ctx, tx, sqlcv1.GetDurableEventLogCallbackParams{ + Durabletaskid: params.Durabletaskid, + Durabletaskinsertedat: params.Durabletaskinsertedat, + Nodeid: params.Nodeid, + }) + + if err != nil && !errors.Is(err, pgx.ErrNoRows) { + return nil, err + } else if errors.Is(err, pgx.ErrNoRows) { + alreadyExists = false + callback, err := r.queries.CreateDurableEventLogCallback(ctx, tx, sqlcv1.CreateDurableEventLogCallbackParams{ + Tenantid: params.Tenantid, + Durabletaskid: params.Durabletaskid, + Durabletaskinsertedat: params.Durabletaskinsertedat, + Insertedat: params.Insertedat, + Externalid: params.Externalid, + Kind: params.Kind, + Nodeid: params.Nodeid, + Issatisfied: params.Issatisfied, + }) + + if err != nil { + return nil, err + } + + if len(payload) > 0 { + err = r.payloadStore.Store(ctx, tx, StorePayloadOpts{ + Id: callback.ID, + InsertedAt: callback.InsertedAt, + ExternalId: callback.ExternalID, + Type: sqlcv1.V1PayloadTypeDURABLEEVENTLOGCALLBACKRESULTDATA, + Payload: payload, + TenantId: tenantId, + }) + + if err != nil { + return nil, err + } + } + } + + var result []byte + if alreadyExists { + result, err = r.payloadStore.RetrieveSingle(ctx, tx, RetrievePayloadOpts{ + Id: callback.ID, + InsertedAt: callback.InsertedAt, + Type: sqlcv1.V1PayloadTypeDURABLEEVENTLOGCALLBACKRESULTDATA, + TenantId: tenantId, + }) + + if err != nil { + result = nil + } + } + + return &EventLogCallbackWithPayload{Callback: callback, Result: result, AlreadyExisted: alreadyExists}, nil +} + +func (r *durableEventsRepository) GetSatisfiedCallbacks(ctx context.Context, tenantId uuid.UUID, callbacks []TaskExternalIdNodeId) ([]*SatisfiedCallbackWithPayload, error) { + if len(callbacks) == 0 { + return nil, nil + } + + taskExternalIds := make([]uuid.UUID, len(callbacks)) + nodeIds := make([]int64, len(callbacks)) + isSatisfieds := make([]bool, len(callbacks)) + + for i, cb := range callbacks { + if err := r.v.Validate(cb); err != nil { + return nil, fmt.Errorf("invalid callback at index %d: %w", i, err) + } + + taskExternalIds[i] = cb.TaskExternalId + nodeIds[i] = cb.NodeId + isSatisfieds[i] = true + } + + rows, err := r.queries.ListSatisfiedCallbacks(ctx, r.pool, sqlcv1.ListSatisfiedCallbacksParams{ + Taskexternalids: taskExternalIds, + Nodeids: nodeIds, + }) + + if err != nil { + return nil, fmt.Errorf("failed to list satisfied callbacks: %w", err) + } + + result := make([]*SatisfiedCallbackWithPayload, 0, len(rows)) + + for _, row := range rows { + payload, err := r.payloadStore.RetrieveSingle(ctx, r.pool, RetrievePayloadOpts{ + Id: row.ID, + InsertedAt: row.InsertedAt, + Type: sqlcv1.V1PayloadTypeDURABLEEVENTLOGCALLBACKRESULTDATA, + TenantId: tenantId, + }) + if err != nil { + r.l.Warn().Err(err).Msgf("failed to retrieve payload for callback %d", row.NodeID) + payload = nil + } + + result = append(result, &SatisfiedCallbackWithPayload{ + TaskExternalId: row.TaskExternalID, + NodeID: row.NodeID, + Result: payload, + }) + } + + return result, nil +} + +func getDurableTaskSignalKey(taskExternalId uuid.UUID, nodeId int64) string { + return fmt.Sprintf("durable:%s:%d", taskExternalId.String(), nodeId) +} + +func (r *durableEventsRepository) IngestDurableTaskEvent(ctx context.Context, opts IngestDurableTaskEventOpts) (*IngestDurableTaskEventResult, error) { + if err := r.v.Validate(opts); err != nil { + return nil, fmt.Errorf("invalid opts: %w", err) + } + + task := opts.Task + + optTx, err := r.PrepareOptimisticTx(ctx) + if err != nil { + return nil, fmt.Errorf("failed to prepare tx: %w", err) + } + defer optTx.Rollback() + + tx := optTx.tx + + // take a lock of the log file so nothing else can concurrently write to it and e.g. increment the node id or branch + // id while this tx is running + logFile, err := r.queries.GetAndLockLogFile(ctx, tx, sqlcv1.GetAndLockLogFileParams{ + Durabletaskid: task.ID, + Durabletaskinsertedat: task.InsertedAt, + }) + + if err != nil && !errors.Is(err, pgx.ErrNoRows) { + return nil, fmt.Errorf("failed to lock log file: %w", err) + } + + if errors.Is(err, pgx.ErrNoRows) { + logFile, err = r.queries.CreateEventLogFile(ctx, tx, sqlcv1.CreateEventLogFileParams{ + Tenantid: opts.TenantId, + Durabletaskid: task.ID, + Durabletaskinsertedat: task.InsertedAt, + }) + + if err != nil { + return nil, fmt.Errorf("failed to get or create event log file: %w", err) + } + } + + isNewInvocation := false + if logFile.LatestInvocationCount < opts.InvocationCount { + isNewInvocation = true + } + + var nodeId int64 + if isNewInvocation { + newNode, err := r.queries.UpdateLogFileNodeIdInvocationCount(ctx, tx, sqlcv1.UpdateLogFileNodeIdInvocationCountParams{ + NodeId: sqlchelpers.ToBigInt(1), + InvocationCount: sqlchelpers.ToBigInt(opts.InvocationCount), + Durabletaskid: task.ID, + Durabletaskinsertedat: task.InsertedAt, + }) + + if err != nil { + return nil, fmt.Errorf("failed to reset latest node id for new invocation: %w", err) + } + + nodeId = newNode.LatestNodeID + } else { + // if it's not a new invocation, we need to increment the latest node id (of the current invocation) + nodeId = logFile.LatestNodeID + 1 + } + + now := sqlchelpers.TimestamptzFromTime(time.Now().UTC()) + + // todo: real logic here for figuring out the parent + parentNodeId := pgtype.Int8{ + Int64: 0, + Valid: false, + } + + // todo: real branching logic here + branchId := logFile.LatestBranchID + + logEntry, err := r.getOrCreateEventLogEntry(ctx, tx, opts.TenantId, sqlcv1.CreateDurableEventLogEntryParams{ + Tenantid: opts.TenantId, + Externalid: uuid.New(), + Durabletaskid: task.ID, + Durabletaskinsertedat: task.InsertedAt, + Kind: opts.Kind, Nodeid: nodeId, - }) -} + ParentNodeId: parentNodeId, + Branchid: branchId, + Datahash: nil, // todo: implement this for nondeterminism check + Datahashalg: "", + }, opts.Payload) -func (r *durableEventsRepository) ListEventLogEntries(ctx context.Context, durableTaskId int64, durableTaskInsertedAt pgtype.Timestamptz) ([]*sqlcv1.V1DurableEventLogEntry, error) { - return r.queries.ListDurableEventLogEntries(ctx, r.pool, sqlcv1.ListDurableEventLogEntriesParams{ - Durabletaskid: durableTaskId, - Durabletaskinsertedat: durableTaskInsertedAt, - }) -} - -func (r *durableEventsRepository) CreateEventLogCallbacks(ctx context.Context, opts []CreateEventLogCallbackOpts) ([]*sqlcv1.V1DurableEventLogCallback, error) { - // note: might need to pass a tx in here instead - tx, commit, rollback, err := sqlchelpers.PrepareTx(ctx, r.pool, r.l) if err != nil { - return nil, err - } - defer rollback() - - durableTaskIds := make([]int64, len(opts)) - durableTaskInsertedAts := make([]pgtype.Timestamptz, len(opts)) - insertedAts := make([]pgtype.Timestamptz, len(opts)) - kinds := make([]string, len(opts)) - keys := make([]string, len(opts)) - nodeIds := make([]int64, len(opts)) - isSatisfieds := make([]bool, len(opts)) - - for i, opt := range opts { - durableTaskIds[i] = opt.DurableTaskId - durableTaskInsertedAts[i] = opt.DurableTaskInsertedAt - insertedAts[i] = opt.InsertedAt - kinds[i] = opt.Kind - keys[i] = opt.Key - nodeIds[i] = opt.NodeId - isSatisfieds[i] = opt.IsSatisfied + return nil, fmt.Errorf("failed to get or create event log entry: %w", err) } - callbacks, err := r.queries.CreateDurableEventLogCallbacks(ctx, tx, sqlcv1.CreateDurableEventLogCallbacksParams{ - Durabletaskids: durableTaskIds, - Durabletaskinsertedats: durableTaskInsertedAts, - Insertedats: insertedAts, - Kinds: kinds, - Keys: keys, - Nodeids: nodeIds, - Issatisfieds: isSatisfieds, - }) + var callbackPayload []byte + isSatisfied := false + + switch opts.Kind { + case sqlcv1.V1DurableEventLogKindWAITFOR: + case sqlcv1.V1DurableEventLogKindRUN: + // do nothing + case sqlcv1.V1DurableEventLogKindMEMO: + // for memoization, we don't need to wait for anything before marking the callback as satisfied since it's just a cache entry + isSatisfied = true + callbackPayload = opts.Payload + default: + return nil, fmt.Errorf("unsupported durable event log entry kind: %s", opts.Kind) + } + + callbackResult, err := r.getOrCreateEventLogCallback( + ctx, + tx, + opts.TenantId, + sqlcv1.CreateDurableEventLogCallbackParams{ + Tenantid: opts.TenantId, + Durabletaskid: task.ID, + Durabletaskinsertedat: task.InsertedAt, + Insertedat: now, + Kind: opts.Kind, + Nodeid: nodeId, + Issatisfied: isSatisfied, + Externalid: uuid.New(), + }, + callbackPayload, + ) + if err != nil { - return nil, err + return nil, fmt.Errorf("failed to get or create callback entry: %w", err) } - if err := commit(ctx); err != nil { - return nil, err + var spawnedTasks []*V1TaskWithPayload + var spawnedDAGs []*DAGWithData + + if !logEntry.AlreadyExisted { + switch opts.Kind { + case sqlcv1.V1DurableEventLogKindWAITFOR: + err := r.handleWaitFor(ctx, tx, nodeId, opts, task) + + if err != nil { + return nil, fmt.Errorf("failed to handle wait for conditions: %w", err) + } + case sqlcv1.V1DurableEventLogKindRUN: + spawnedDAGs, spawnedTasks, err = r.handleTriggerRuns(ctx, optTx, nodeId, opts, task) + + if err != nil { + return nil, fmt.Errorf("failed to handle trigger runs: %w", err) + } + case sqlcv1.V1DurableEventLogKindMEMO: + // todo: memo here + default: + return nil, fmt.Errorf("unsupported durable event log entry kind: %s", opts.Kind) + } } - return callbacks, nil -} - -func (r *durableEventsRepository) GetEventLogCallback(ctx context.Context, durableTaskId int64, durableTaskInsertedAt pgtype.Timestamptz, key string) (*sqlcv1.V1DurableEventLogCallback, error) { - return r.queries.GetDurableEventLogCallback(ctx, r.pool, sqlcv1.GetDurableEventLogCallbackParams{ - Durabletaskid: durableTaskId, - Durabletaskinsertedat: durableTaskInsertedAt, - Key: key, + logFile, err = r.queries.UpdateLogFileNodeIdInvocationCount(ctx, tx, sqlcv1.UpdateLogFileNodeIdInvocationCountParams{ + NodeId: sqlchelpers.ToBigInt(nodeId), + Durabletaskid: task.ID, + Durabletaskinsertedat: task.InsertedAt, }) -} -func (r *durableEventsRepository) ListEventLogCallbacks(ctx context.Context, durableTaskId int64, durableTaskInsertedAt pgtype.Timestamptz) ([]*sqlcv1.V1DurableEventLogCallback, error) { - return r.queries.ListDurableEventLogCallbacks(ctx, r.pool, sqlcv1.ListDurableEventLogCallbacksParams{ - Durabletaskid: durableTaskId, - Durabletaskinsertedat: durableTaskInsertedAt, - }) -} - -func (r *durableEventsRepository) UpdateEventLogCallbackSatisfied(ctx context.Context, durableTaskId int64, durableTaskInsertedAt pgtype.Timestamptz, key string, isSatisfied bool) (*sqlcv1.V1DurableEventLogCallback, error) { - // note: might need to pass a tx in here instead - tx, commit, rollback, err := sqlchelpers.PrepareTx(ctx, r.pool, r.l) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to update latest node id: %w", err) } - defer rollback() - callback, err := r.queries.UpdateDurableEventLogCallbackSatisfied(ctx, tx, sqlcv1.UpdateDurableEventLogCallbackSatisfiedParams{ - Durabletaskid: durableTaskId, - Durabletaskinsertedat: durableTaskInsertedAt, - Key: key, - Issatisfied: isSatisfied, - }) - if err != nil { + if err := optTx.Commit(ctx); err != nil { return nil, err } - if err := commit(ctx); err != nil { - return nil, err - } - - return callback, nil + return &IngestDurableTaskEventResult{ + NodeId: nodeId, + Callback: callbackResult, + EventLogFile: logFile, + EventLogEntry: logEntry, + CreatedTasks: spawnedTasks, + CreatedDAGs: spawnedDAGs, + }, nil +} + +func (r *durableEventsRepository) handleWaitFor(ctx context.Context, tx sqlcv1.DBTX, nodeId int64, opts IngestDurableTaskEventOpts, task *sqlcv1.FlattenExternalIdsRow) error { + if opts.WaitForConditions == nil { + return nil + } + + if len(opts.WaitForConditions) == 0 { + return nil + } + + taskExternalId := opts.Task.ExternalID + signalKey := getDurableTaskSignalKey(taskExternalId, nodeId) + + createMatchOpts := []ExternalCreateSignalMatchOpts{{ + Conditions: opts.WaitForConditions, + SignalTaskId: task.ID, + SignalTaskInsertedAt: task.InsertedAt, + SignalExternalId: task.ExternalID, + SignalKey: signalKey, + DurableCallbackTaskId: &task.ID, + DurableCallbackTaskInsertedAt: task.InsertedAt, + DurableCallbackNodeId: &nodeId, + DurableCallbackTaskExternalId: &taskExternalId, + }} + + return r.registerSignalMatchConditions(ctx, tx, opts.TenantId, createMatchOpts) +} + +func (r *durableEventsRepository) handleTriggerRuns(ctx context.Context, tx *OptimisticTx, nodeId int64, opts IngestDurableTaskEventOpts, task *sqlcv1.FlattenExternalIdsRow) ([]*DAGWithData, []*V1TaskWithPayload, error) { + createdTasks, createdDAGs, err := r.triggerFromWorkflowNames(ctx, tx, opts.TenantId, []*WorkflowNameTriggerOpts{opts.TriggerOpts}) + + if err != nil { + return nil, nil, fmt.Errorf("failed to trigger workflows: %w", err) + } + + taskId := task.ID + taskExternalId := task.ExternalID + + var conditions []GroupMatchCondition + + for _, childTask := range createdTasks { + childHint := childTask.ExternalID.String() + orGroupId := uuid.New() + + conditions = append(conditions, + GroupMatchCondition{ + GroupId: orGroupId, + EventType: sqlcv1.V1EventTypeINTERNAL, + EventKey: string(sqlcv1.V1TaskEventTypeCOMPLETED), + ReadableDataKey: "output", + EventResourceHint: &childHint, + Expression: "true", + Action: sqlcv1.V1MatchConditionActionCREATE, + }, + GroupMatchCondition{ + GroupId: orGroupId, + EventType: sqlcv1.V1EventTypeINTERNAL, + EventKey: string(sqlcv1.V1TaskEventTypeFAILED), + ReadableDataKey: "output", + EventResourceHint: &childHint, + Expression: "true", + Action: sqlcv1.V1MatchConditionActionCREATE, + }, + GroupMatchCondition{ + GroupId: orGroupId, + EventType: sqlcv1.V1EventTypeINTERNAL, + EventKey: string(sqlcv1.V1TaskEventTypeCANCELLED), + ReadableDataKey: "output", + EventResourceHint: &childHint, + Expression: "true", + Action: sqlcv1.V1MatchConditionActionCREATE, + }, + ) + } + + if len(conditions) > 0 { + runCallbackSignalKey := fmt.Sprintf("durable_run:%s:%d", task.ExternalID.String(), nodeId) + + err = r.createEventMatches(ctx, tx.tx, opts.TenantId, []CreateMatchOpts{{ + Kind: sqlcv1.V1MatchKindSIGNAL, + Conditions: conditions, + SignalTaskId: &taskId, + SignalTaskInsertedAt: task.InsertedAt, + SignalExternalId: &taskExternalId, + SignalKey: &runCallbackSignalKey, + DurableCallbackTaskId: &taskId, + DurableCallbackTaskInsertedAt: task.InsertedAt, + DurableCallbackNodeId: &nodeId, + DurableCallbackTaskExternalId: &taskExternalId, + }}) + + if err != nil { + return nil, nil, fmt.Errorf("failed to register run completion match: %w", err) + } + } + + return createdDAGs, createdTasks, nil } diff --git a/pkg/repository/match.go b/pkg/repository/match.go index 134ba88bd..384dee40c 100644 --- a/pkg/repository/match.go +++ b/pkg/repository/match.go @@ -44,6 +44,12 @@ type ExternalCreateSignalMatchOpts struct { SignalExternalId uuid.UUID `validate:"required"` SignalKey string `validate:"required"` + + // Optional callback fields for durable WAIT_FOR + DurableCallbackTaskId *int64 + DurableCallbackTaskInsertedAt pgtype.Timestamptz + DurableCallbackNodeId *int64 + DurableCallbackTaskExternalId *uuid.UUID } type CreateExternalSignalConditionKind string @@ -109,6 +115,12 @@ type CreateMatchOpts struct { SignalExternalId *uuid.UUID SignalKey *string + + // Optional callback fields for durable WAIT_FOR + DurableCallbackTaskId *int64 + DurableCallbackTaskInsertedAt pgtype.Timestamptz + DurableCallbackNodeId *int64 + DurableCallbackTaskExternalId *uuid.UUID } type EventMatchResults struct { @@ -117,6 +129,9 @@ type EventMatchResults struct { // The list of tasks which were replayed from the matches ReplayedTasks []*V1TaskWithPayload + + // The list of satisfied durable callbacks from matches + SatisfiedCallbacks []SatisfiedCallback } type GroupMatchCondition struct { @@ -140,6 +155,14 @@ type GroupMatchCondition struct { Data []byte } +type SatisfiedCallback struct { + DurableTaskExternalId uuid.UUID + DurableTaskId int64 + DurableTaskInsertedAt pgtype.Timestamptz + NodeId int64 + Data []byte +} + type MatchRepository interface { RegisterSignalMatchConditions(ctx context.Context, tenantId uuid.UUID, eventMatches []ExternalCreateSignalMatchOpts) error @@ -157,20 +180,7 @@ func newMatchRepository(s *sharedRepository) MatchRepository { } } -func (m *MatchRepositoryImpl) RegisterSignalMatchConditions(ctx context.Context, tenantId uuid.UUID, signalMatches []ExternalCreateSignalMatchOpts) error { - // TODO: ADD BACK VALIDATION - // if err := m.v.Validate(signalMatches); err != nil { - // return err - // } - - tx, commit, rollback, err := sqlchelpers.PrepareTx(ctx, m.pool, m.l) - - if err != nil { - return err - } - - defer rollback() - +func (r *sharedRepository) registerSignalMatchConditions(ctx context.Context, tx sqlcv1.DBTX, tenantId uuid.UUID, signalMatches []ExternalCreateSignalMatchOpts) error { eventMatches := make([]CreateMatchOpts, 0, len(signalMatches)) for _, signalMatch := range signalMatches { @@ -183,7 +193,7 @@ func (m *MatchRepositoryImpl) RegisterSignalMatchConditions(ctx context.Context, return fmt.Errorf("sleep condition requires a duration") } - c, err := m.durableSleepCondition( + c, err := r.durableSleepCondition( ctx, tx, tenantId, @@ -203,7 +213,7 @@ func (m *MatchRepositoryImpl) RegisterSignalMatchConditions(ctx context.Context, return fmt.Errorf("user event condition requires a user event key") } - conditions = append(conditions, m.userEventCondition( + conditions = append(conditions, r.userEventCondition( condition.OrGroupId, condition.ReadableDataKey, *condition.UserEventKey, @@ -218,21 +228,38 @@ func (m *MatchRepositoryImpl) RegisterSignalMatchConditions(ctx context.Context, signalKey := signalMatch.SignalKey eventMatches = append(eventMatches, CreateMatchOpts{ - Kind: sqlcv1.V1MatchKindSIGNAL, - Conditions: conditions, - SignalTaskId: &taskId, - SignalTaskInsertedAt: signalMatch.SignalTaskInsertedAt, - SignalExternalId: &externalId, - SignalKey: &signalKey, + Kind: sqlcv1.V1MatchKindSIGNAL, + Conditions: conditions, + SignalTaskId: &taskId, + SignalTaskInsertedAt: signalMatch.SignalTaskInsertedAt, + SignalExternalId: &externalId, + SignalKey: &signalKey, + DurableCallbackTaskId: signalMatch.DurableCallbackTaskId, + DurableCallbackTaskInsertedAt: signalMatch.DurableCallbackTaskInsertedAt, + DurableCallbackNodeId: signalMatch.DurableCallbackNodeId, + DurableCallbackTaskExternalId: signalMatch.DurableCallbackTaskExternalId, }) } - err = m.createEventMatches(ctx, tx, tenantId, eventMatches) + return r.createEventMatches(ctx, tx, tenantId, eventMatches) +} + +func (m *MatchRepositoryImpl) RegisterSignalMatchConditions(ctx context.Context, tenantId uuid.UUID, signalMatches []ExternalCreateSignalMatchOpts) error { + // TODO: ADD BACK VALIDATION + // if err := m.v.Validate(signalMatches); err != nil { + // return err + // } + + tx, commit, rollback, err := sqlchelpers.PrepareTx(ctx, m.pool, m.l) if err != nil { return err } + defer rollback() + + err = m.registerSignalMatchConditions(ctx, tx, tenantId, signalMatches) + if err := commit(ctx); err != nil { return err } @@ -327,6 +354,12 @@ func (m *MatchRepositoryImpl) ProcessUserEventMatches(ctx context.Context, tenan return res, nil } +type DurableTaskNodeIdKey struct { + DurableTaskId int64 + DurableTaskInsertedAt time.Time + NodeId int64 +} + func (m *sharedRepository) processEventMatches(ctx context.Context, tx sqlcv1.DBTX, tenantId uuid.UUID, events []CandidateEventMatch, eventType sqlcv1.V1EventType) (*EventMatchResults, error) { start := time.Now() @@ -653,6 +686,95 @@ func (m *sharedRepository) processEventMatches(ctx context.Context, tx sqlcv1.DB res.CreatedTasks = tasks + durableTaskIds := make([]int64, 0) + durableTaskInsertedAts := make([]pgtype.Timestamptz, 0) + durableTaskNodeIds := make([]int64, 0) + payloadsToStore := make([]StorePayloadOpts, 0) + idInsertedAtNodeIdToSatisfiedCallback := make(map[DurableTaskNodeIdKey]SatisfiedCallback) + + for _, match := range satisfiedMatches { + if match.DurableEventLogCallbackNodeID.Valid && match.DurableEventLogCallbackDurableTaskID.Valid && match.DurableEventLogCallbackDurableTaskExternalID != nil { + durableTaskId := match.DurableEventLogCallbackDurableTaskID.Int64 + key := DurableTaskNodeIdKey{ + DurableTaskId: durableTaskId, + DurableTaskInsertedAt: match.DurableEventLogCallbackDurableTaskInsertedAt.Time, + NodeId: match.DurableEventLogCallbackNodeID.Int64, + } + + cb := SatisfiedCallback{ + DurableTaskExternalId: *match.DurableEventLogCallbackDurableTaskExternalID, + DurableTaskId: durableTaskId, + DurableTaskInsertedAt: match.DurableEventLogCallbackDurableTaskInsertedAt, + NodeId: match.DurableEventLogCallbackNodeID.Int64, + Data: match.McAggregatedData, + } + + idInsertedAtNodeIdToSatisfiedCallback[key] = cb + + durableTaskIds = append(durableTaskIds, match.DurableEventLogCallbackDurableTaskID.Int64) + durableTaskInsertedAts = append(durableTaskInsertedAts, match.DurableEventLogCallbackDurableTaskInsertedAt) + durableTaskNodeIds = append(durableTaskNodeIds, match.DurableEventLogCallbackNodeID.Int64) + } + } + + callbacks, err := m.queries.UpdateDurableEventLogCallbacksSatisfied(ctx, tx, sqlcv1.UpdateDurableEventLogCallbacksSatisfiedParams{ + Nodeids: durableTaskNodeIds, + Durabletaskids: durableTaskIds, + Durabletaskinsertedats: durableTaskInsertedAts, + }) + + if err != nil { + return nil, fmt.Errorf("failed to list satisfied callbacks: %w", err) + } + + satisfiedCallbacks := make([]SatisfiedCallback, 0) + + for _, cb := range callbacks { + key := DurableTaskNodeIdKey{ + DurableTaskId: cb.DurableTaskID, + DurableTaskInsertedAt: cb.DurableTaskInsertedAt.Time, + NodeId: cb.NodeID, + } + + predeterminedCallback, ok := idInsertedAtNodeIdToSatisfiedCallback[key] + + if !ok { + m.l.Error().Msgf("no predetermined callback found for satisfied callback with node id %d, durable task id %d and durable task inserted at %s", cb.NodeID, cb.DurableTaskID, cb.DurableTaskInsertedAt.Time) + continue + } + + if cb.Kind == sqlcv1.V1DurableEventLogKindRUN { + if extracted, extractErr := ExtractOutputFromMatchData(predeterminedCallback.Data); extractErr != nil { + m.l.Error().Err(extractErr).Msgf("failed to extract output from RUN_COMPLETED match data for callback %d", cb.NodeID) + } else { + predeterminedCallback.Data = extracted + } + } + + if len(predeterminedCallback.Data) > 0 { + payloadsToStore = append(payloadsToStore, StorePayloadOpts{ + Id: cb.ID, + InsertedAt: cb.InsertedAt, + Type: sqlcv1.V1PayloadTypeDURABLEEVENTLOGCALLBACKRESULTDATA, + Payload: predeterminedCallback.Data, + ExternalId: cb.ExternalID, + TenantId: tenantId, + }) + } + + satisfiedCallbacks = append(satisfiedCallbacks, predeterminedCallback) + } + + if len(payloadsToStore) > 0 { + err = m.payloadStore.Store(ctx, tx, payloadsToStore...) + + if err != nil { + return nil, fmt.Errorf("failed to store callback result payloads for satisfied callbacks: %w", err) + } + } + + res.SatisfiedCallbacks = satisfiedCallbacks + if len(signalIds) > 0 { // create a SIGNAL_COMPLETED event for any signal taskIds := make([]TaskIdInsertedAtRetryCount, 0, len(satisfiedMatches)) @@ -977,6 +1099,10 @@ func (m *sharedRepository) createEventMatches(ctx context.Context, tx sqlcv1.DBT signalTaskIds := make([]int64, len(signalMatches)) signalTaskInsertedAts := make([]pgtype.Timestamptz, len(signalMatches)) signalKeys := make([]string, len(signalMatches)) + callbackTaskIds := make([]*int64, len(signalMatches)) + callbackTaskInsertedAts := make([]pgtype.Timestamptz, len(signalMatches)) + callbackNodeIds := make([]*int64, len(signalMatches)) + callbackDurableTaskExternalIds := make([]*uuid.UUID, len(signalMatches)) for i, match := range signalMatches { signalTenantIds[i] = tenantId @@ -984,6 +1110,11 @@ func (m *sharedRepository) createEventMatches(ctx context.Context, tx sqlcv1.DBT signalTaskIds[i] = *match.SignalTaskId signalTaskInsertedAts[i] = match.SignalTaskInsertedAt signalKeys[i] = *match.SignalKey + + callbackTaskIds[i] = match.DurableCallbackTaskId + callbackTaskInsertedAts[i] = match.DurableCallbackTaskInsertedAt + callbackNodeIds[i] = match.DurableCallbackNodeId + callbackDurableTaskExternalIds[i] = match.DurableCallbackTaskExternalId } // Create matches in the database @@ -991,11 +1122,15 @@ func (m *sharedRepository) createEventMatches(ctx context.Context, tx sqlcv1.DBT ctx, tx, sqlcv1.CreateMatchesForSignalTriggersParams{ - Tenantids: signalTenantIds, - Kinds: signalKinds, - Signaltaskids: signalTaskIds, - Signaltaskinsertedats: signalTaskInsertedAts, - Signalkeys: signalKeys, + Tenantids: signalTenantIds, + Kinds: signalKinds, + Signaltaskids: signalTaskIds, + Signaltaskinsertedats: signalTaskInsertedAts, + Signalkeys: signalKeys, + Callbackdurabletaskids: callbackTaskIds, + Callbackdurabletaskinsertedats: callbackTaskInsertedAts, + Callbacknodeids: callbackNodeIds, + Callbackdurabletaskexternalids: callbackDurableTaskExternalIds, }, ) diff --git a/pkg/repository/output.go b/pkg/repository/output.go index 22eb4e14b..47533fc6c 100644 --- a/pkg/repository/output.go +++ b/pkg/repository/output.go @@ -2,6 +2,7 @@ package repository import ( "encoding/json" + "fmt" "github.com/google/uuid" "github.com/hatchet-dev/hatchet/pkg/repository/sqlcv1" @@ -137,3 +138,27 @@ func newTaskEventFromBytes(b []byte) (*TaskOutputEvent, error) { return &e, err } + +func ExtractOutputFromMatchData(data []byte) ([]byte, error) { + var outer map[string]map[string][]json.RawMessage + if err := json.Unmarshal(data, &outer); err != nil { + return nil, fmt.Errorf("failed to unmarshal match data: %w", err) + } + + for _, keyMap := range outer { + for _, entries := range keyMap { + if len(entries) == 0 { + continue + } + + var event TaskOutputEvent + if err := json.Unmarshal(entries[0], &event); err != nil { + return nil, fmt.Errorf("failed to unmarshal task output event from match data: %w", err) + } + + return event.Output, nil + } + } + + return nil, fmt.Errorf("no entries found in match data") +} diff --git a/pkg/repository/payloadstore.go b/pkg/repository/payloadstore.go index 88928ed61..7b602b9f2 100644 --- a/pkg/repository/payloadstore.go +++ b/pkg/repository/payloadstore.go @@ -62,6 +62,7 @@ type ExternalStore interface { type PayloadStoreRepository interface { Store(ctx context.Context, tx sqlcv1.DBTX, payloads ...StorePayloadOpts) error Retrieve(ctx context.Context, tx sqlcv1.DBTX, opts ...RetrievePayloadOpts) (map[RetrievePayloadOpts][]byte, error) + RetrieveSingle(ctx context.Context, tx sqlcv1.DBTX, opt RetrievePayloadOpts) ([]byte, error) RetrieveFromExternal(ctx context.Context, keys ...ExternalPayloadLocationKey) (map[ExternalPayloadLocationKey][]byte, error) OverwriteExternalStore(store ExternalStore) DualWritesEnabled() bool @@ -282,6 +283,24 @@ func (p *payloadStoreRepositoryImpl) Retrieve(ctx context.Context, tx sqlcv1.DBT return p.retrieve(ctx, tx, opts...) } +func (p *payloadStoreRepositoryImpl) RetrieveSingle(ctx context.Context, tx sqlcv1.DBTX, opt RetrievePayloadOpts) ([]byte, error) { + if tx == nil { + tx = p.pool + } + + optsToPayload, err := p.retrieve(ctx, tx, opt) + + if err != nil { + return nil, err + } + + if len(optsToPayload) == 0 { + return nil, pgx.ErrNoRows + } + + return optsToPayload[opt], nil +} + func (p *payloadStoreRepositoryImpl) RetrieveFromExternal(ctx context.Context, keys ...ExternalPayloadLocationKey) (map[ExternalPayloadLocationKey][]byte, error) { if !p.externalStoreEnabled { return nil, fmt.Errorf("external store not enabled") diff --git a/pkg/repository/sqlchelpers/int.go b/pkg/repository/sqlchelpers/int.go index 50cd8e3fb..e66e8e16d 100644 --- a/pkg/repository/sqlchelpers/int.go +++ b/pkg/repository/sqlchelpers/int.go @@ -8,3 +8,10 @@ func ToInt(i int32) pgtype.Int4 { Int32: i, } } + +func ToBigInt(i int64) pgtype.Int8 { + return pgtype.Int8{ + Valid: true, + Int64: i, + } +} diff --git a/pkg/repository/sqlcv1/durable_event_log.sql b/pkg/repository/sqlcv1/durable_event_log.sql index b292655c5..4e977ee57 100644 --- a/pkg/repository/sqlcv1/durable_event_log.sql +++ b/pkg/repository/sqlcv1/durable_event_log.sql @@ -1,58 +1,57 @@ --- name: CreateDurableEventLogFile :many -WITH inputs AS ( - SELECT - UNNEST(@durableTaskIds::BIGINT[]) AS durable_task_id, - UNNEST(@durableTaskInsertedAts::TIMESTAMPTZ[]) AS durable_task_inserted_at, - UNNEST(@latestInsertedAts::TIMESTAMPTZ[]) AS latest_inserted_at, - UNNEST(@latestNodeIds::BIGINT[]) AS latest_node_id, - UNNEST(@latestBranchIds::BIGINT[]) AS latest_branch_id, - UNNEST(@latestBranchFirstParentNodeIds::BIGINT[]) AS latest_branch_first_parent_node_id -) +-- name: GetAndLockLogFile :one +SELECT * +FROM v1_durable_event_log_file +WHERE durable_task_id = @durableTaskId::BIGINT + AND durable_task_inserted_at = @durableTaskInsertedAt::TIMESTAMPTZ +FOR UPDATE; + +-- name: UpdateLogFileNodeIdInvocationCount :one +UPDATE v1_durable_event_log_file +SET + latest_node_id = COALESCE(sqlc.narg('nodeId')::BIGINT, v1_durable_event_log_file.latest_node_id), + latest_invocation_count = COALESCE(sqlc.narg('invocationCount')::BIGINT, v1_durable_event_log_file.latest_invocation_count) +WHERE durable_task_id = @durableTaskId::BIGINT + AND durable_task_inserted_at = @durableTaskInsertedAt::TIMESTAMPTZ +RETURNING *; + +-- name: CreateEventLogFile :one INSERT INTO v1_durable_event_log_file ( + tenant_id, durable_task_id, durable_task_inserted_at, + latest_invocation_count, latest_inserted_at, latest_node_id, latest_branch_id, latest_branch_first_parent_node_id +) VALUES ( + @tenantId::UUID, + @durableTaskId::BIGINT, + @durableTaskInsertedAt::TIMESTAMPTZ, + 0, + NOW(), + 1, + 1, + 0 ) -SELECT - i.durable_task_id, - i.durable_task_inserted_at, - i.latest_inserted_at, - i.latest_node_id, - i.latest_branch_id, - i.latest_branch_first_parent_node_id -FROM - inputs i +ON CONFLICT (durable_task_id, durable_task_inserted_at) +DO UPDATE SET + latest_node_id = GREATEST(v1_durable_event_log_file.latest_node_id, EXCLUDED.latest_node_id), + latest_inserted_at = NOW(), + latest_invocation_count = GREATEST(v1_durable_event_log_file.latest_invocation_count, EXCLUDED.latest_invocation_count) RETURNING * ; --- name: GetDurableEventLogFileForTask :one +-- name: GetDurableEventLogEntry :one SELECT * -FROM v1_durable_event_log_file -WHERE durable_task_id = @durableTaskId - AND durable_task_inserted_at = @durableTaskInsertedAt -; - --- todo: implement UpdateLatestNodeId - --- name: CreateDurableEventLogEntries :many -WITH inputs AS ( - SELECT - UNNEST(@externalIds::UUID[]) AS external_id, - UNNEST(@durableTaskIds::BIGINT[]) AS durable_task_id, - UNNEST(@durableTaskInsertedAts::TIMESTAMPTZ[]) AS durable_task_inserted_at, - UNNEST(@insertedAts::TIMESTAMPTZ[]) AS inserted_at, - UNNEST(CAST(@kinds::TEXT[] AS v1_durable_event_log_entry_kind[])) AS kind, - UNNEST(@nodeIds::BIGINT[]) AS node_id, - UNNEST(@parentNodeIds::BIGINT[]) AS parent_node_id, - UNNEST(@branchIds::BIGINT[]) AS branch_id, - UNNEST(@dataHashes::BYTEA[]) AS data_hash, - UNNEST(@dataHashAlgs::TEXT[]) AS data_hash_alg -) +FROM v1_durable_event_log_entry +WHERE durable_task_id = @durableTaskId::BIGINT + AND durable_task_inserted_at = @durableTaskInsertedAt::TIMESTAMPTZ + AND node_id = @nodeId::BIGINT; +-- name: CreateDurableEventLogEntry :one INSERT INTO v1_durable_event_log_entry ( + tenant_id, external_id, durable_task_id, durable_task_inserted_at, @@ -64,98 +63,85 @@ INSERT INTO v1_durable_event_log_entry ( data_hash, data_hash_alg ) -SELECT - i.external_id, - i.durable_task_id, - i.durable_task_inserted_at, - i.inserted_at, - i.kind, - i.node_id, - -- todo: check on if 0 is a safe sentinel value here or if we're zero-indexing the node id - NULLIF(i.parent_node_id, 0), - i.branch_id, - i.data_hash, - i.data_hash_alg -FROM - inputs i -ORDER BY - i.durable_task_id, - i.durable_task_inserted_at, - i.node_id --- todo: conflict resolution here -RETURNING * -; - --- name: ListDurableEventLogEntries :many -SELECT * -FROM v1_durable_event_log_entry -WHERE durable_task_id = @durableTaskId - AND durable_task_inserted_at = @durableTaskInsertedAt -ORDER BY node_id ASC -; - --- name: GetDurableEventLogEntry :one -SELECT * -FROM v1_durable_event_log_entry -WHERE durable_task_id = @durableTaskId - AND durable_task_inserted_at = @durableTaskInsertedAt - AND node_id = @nodeId -; - --- name: CreateDurableEventLogCallbacks :many -WITH inputs AS ( - SELECT - UNNEST(@durableTaskIds::BIGINT[]) AS durable_task_id, - UNNEST(@durableTaskInsertedAts::TIMESTAMPTZ[]) AS durable_task_inserted_at, - UNNEST(@insertedAts::TIMESTAMPTZ[]) AS inserted_at, - UNNEST(CAST(@kinds::TEXT[] AS v1_durable_event_log_callback_kind[])) AS kind, - UNNEST(@keys::TEXT[]) AS key, - UNNEST(@nodeIds::BIGINT[]) AS node_id, - UNNEST(@isSatisfieds::BOOLEAN[]) AS is_satisfied +VALUES ( + @tenantId::UUID, + @externalId::UUID, + @durableTaskId::BIGINT, + @durableTaskInsertedAt::TIMESTAMPTZ, + NOW(), + @kind::v1_durable_event_log_kind, + @nodeId::BIGINT, + sqlc.narg('parentNodeId')::BIGINT, + @branchId::BIGINT, + @dataHash::BYTEA, + @dataHashAlg::TEXT ) -INSERT INTO v1_durable_event_log_callback ( - durable_task_id, - durable_task_inserted_at, - inserted_at, - kind, - key, - node_id, - is_satisfied -) -SELECT - i.durable_task_id, - i.durable_task_inserted_at, - i.inserted_at, - i.kind, - i.key, - i.node_id, - i.is_satisfied -FROM - inputs i +ON CONFLICT (durable_task_id, durable_task_inserted_at, node_id) DO NOTHING RETURNING * ; -- name: GetDurableEventLogCallback :one SELECT * FROM v1_durable_event_log_callback -WHERE durable_task_id = @durableTaskId - AND durable_task_inserted_at = @durableTaskInsertedAt - AND key = @key +WHERE durable_task_id = @durableTaskId::BIGINT + AND durable_task_inserted_at = @durableTaskInsertedAt::TIMESTAMPTZ + AND node_id = @nodeId::BIGINT ; --- name: ListDurableEventLogCallbacks :many -SELECT * -FROM v1_durable_event_log_callback -WHERE durable_task_id = @durableTaskId - AND durable_task_inserted_at = @durableTaskInsertedAt -ORDER BY inserted_at ASC -; - --- name: UpdateDurableEventLogCallbackSatisfied :one -UPDATE v1_durable_event_log_callback -SET is_satisfied = @isSatisfied -WHERE durable_task_id = @durableTaskId - AND durable_task_inserted_at = @durableTaskInsertedAt - AND key = @key +-- name: CreateDurableEventLogCallback :one +INSERT INTO v1_durable_event_log_callback ( + tenant_id, + durable_task_id, + durable_task_inserted_at, + inserted_at, + kind, + node_id, + is_satisfied, + external_id +) +VALUES ( + @tenantId::UUID, + @durableTaskId::BIGINT, + @durableTaskInsertedAt::TIMESTAMPTZ, + @insertedAt::TIMESTAMPTZ, + @kind::v1_durable_event_log_kind, + @nodeId::BIGINT, + @isSatisfied::BOOLEAN, + @externalId::UUID +) +ON CONFLICT (durable_task_id, durable_task_inserted_at, node_id) DO NOTHING RETURNING * ; + +-- name: UpdateDurableEventLogCallbacksSatisfied :many +WITH inputs AS ( + SELECT + UNNEST(@durableTaskIds::BIGINT[]) AS durable_task_id, + UNNEST(@durableTaskInsertedAts::TIMESTAMPTZ[]) AS durable_task_inserted_at, + UNNEST(@nodeIds::BIGINT[]) AS node_id +) + +UPDATE v1_durable_event_log_callback +SET is_satisfied = true +FROM inputs +WHERE v1_durable_event_log_callback.durable_task_id = inputs.durable_task_id + AND v1_durable_event_log_callback.durable_task_inserted_at = inputs.durable_task_inserted_at + AND v1_durable_event_log_callback.node_id = inputs.node_id +RETURNING v1_durable_event_log_callback.* +; + +-- name: ListSatisfiedCallbacks :many +WITH tasks AS ( + SELECT t.* + FROM v1_lookup_table lt + JOIN v1_task t ON (t.id, t.inserted_at) = (lt.task_id, lt.inserted_at) + WHERE lt.external_id = ANY(@taskExternalIds::UUID[]) +) + +SELECT cb.*, t.external_id AS task_external_id +FROM v1_durable_event_log_callback cb +JOIN tasks t ON (t.id, t.inserted_at) = (cb.durable_task_id, cb.durable_task_inserted_at) +WHERE + cb.node_id = ANY(@nodeIds::BIGINT[]) + AND cb.is_satisfied +; diff --git a/pkg/repository/sqlcv1/durable_event_log.sql.go b/pkg/repository/sqlcv1/durable_event_log.sql.go index d8d03b64a..97805907a 100644 --- a/pkg/repository/sqlcv1/durable_event_log.sql.go +++ b/pkg/repository/sqlcv1/durable_event_log.sql.go @@ -12,104 +12,71 @@ import ( "github.com/jackc/pgx/v5/pgtype" ) -const createDurableEventLogCallbacks = `-- name: CreateDurableEventLogCallbacks :many -WITH inputs AS ( - SELECT - UNNEST($1::BIGINT[]) AS durable_task_id, - UNNEST($2::TIMESTAMPTZ[]) AS durable_task_inserted_at, - UNNEST($3::TIMESTAMPTZ[]) AS inserted_at, - UNNEST(CAST($4::TEXT[] AS v1_durable_event_log_callback_kind[])) AS kind, - UNNEST($5::TEXT[]) AS key, - UNNEST($6::BIGINT[]) AS node_id, - UNNEST($7::BOOLEAN[]) AS is_satisfied -) +const createDurableEventLogCallback = `-- name: CreateDurableEventLogCallback :one INSERT INTO v1_durable_event_log_callback ( + tenant_id, durable_task_id, durable_task_inserted_at, inserted_at, kind, - key, node_id, - is_satisfied + is_satisfied, + external_id ) -SELECT - i.durable_task_id, - i.durable_task_inserted_at, - i.inserted_at, - i.kind, - i.key, - i.node_id, - i.is_satisfied -FROM - inputs i -RETURNING external_id, inserted_at, id, durable_task_id, durable_task_inserted_at, kind, key, node_id, is_satisfied +VALUES ( + $1::UUID, + $2::BIGINT, + $3::TIMESTAMPTZ, + $4::TIMESTAMPTZ, + $5::v1_durable_event_log_kind, + $6::BIGINT, + $7::BOOLEAN, + $8::UUID +) +ON CONFLICT (durable_task_id, durable_task_inserted_at, node_id) DO NOTHING +RETURNING tenant_id, external_id, inserted_at, id, durable_task_id, durable_task_inserted_at, kind, node_id, is_satisfied ` -type CreateDurableEventLogCallbacksParams struct { - Durabletaskids []int64 `json:"durabletaskids"` - Durabletaskinsertedats []pgtype.Timestamptz `json:"durabletaskinsertedats"` - Insertedats []pgtype.Timestamptz `json:"insertedats"` - Kinds []string `json:"kinds"` - Keys []string `json:"keys"` - Nodeids []int64 `json:"nodeids"` - Issatisfieds []bool `json:"issatisfieds"` +type CreateDurableEventLogCallbackParams struct { + Tenantid uuid.UUID `json:"tenantid"` + Durabletaskid int64 `json:"durabletaskid"` + Durabletaskinsertedat pgtype.Timestamptz `json:"durabletaskinsertedat"` + Insertedat pgtype.Timestamptz `json:"insertedat"` + Kind V1DurableEventLogKind `json:"kind"` + Nodeid int64 `json:"nodeid"` + Issatisfied bool `json:"issatisfied"` + Externalid uuid.UUID `json:"externalid"` } -func (q *Queries) CreateDurableEventLogCallbacks(ctx context.Context, db DBTX, arg CreateDurableEventLogCallbacksParams) ([]*V1DurableEventLogCallback, error) { - rows, err := db.Query(ctx, createDurableEventLogCallbacks, - arg.Durabletaskids, - arg.Durabletaskinsertedats, - arg.Insertedats, - arg.Kinds, - arg.Keys, - arg.Nodeids, - arg.Issatisfieds, +func (q *Queries) CreateDurableEventLogCallback(ctx context.Context, db DBTX, arg CreateDurableEventLogCallbackParams) (*V1DurableEventLogCallback, error) { + row := db.QueryRow(ctx, createDurableEventLogCallback, + arg.Tenantid, + arg.Durabletaskid, + arg.Durabletaskinsertedat, + arg.Insertedat, + arg.Kind, + arg.Nodeid, + arg.Issatisfied, + arg.Externalid, ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []*V1DurableEventLogCallback - for rows.Next() { - var i V1DurableEventLogCallback - if err := rows.Scan( - &i.ExternalID, - &i.InsertedAt, - &i.ID, - &i.DurableTaskID, - &i.DurableTaskInsertedAt, - &i.Kind, - &i.Key, - &i.NodeID, - &i.IsSatisfied, - ); err != nil { - return nil, err - } - items = append(items, &i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil + var i V1DurableEventLogCallback + err := row.Scan( + &i.TenantID, + &i.ExternalID, + &i.InsertedAt, + &i.ID, + &i.DurableTaskID, + &i.DurableTaskInsertedAt, + &i.Kind, + &i.NodeID, + &i.IsSatisfied, + ) + return &i, err } -const createDurableEventLogEntries = `-- name: CreateDurableEventLogEntries :many - -WITH inputs AS ( - SELECT - UNNEST($1::UUID[]) AS external_id, - UNNEST($2::BIGINT[]) AS durable_task_id, - UNNEST($3::TIMESTAMPTZ[]) AS durable_task_inserted_at, - UNNEST($4::TIMESTAMPTZ[]) AS inserted_at, - UNNEST(CAST($5::TEXT[] AS v1_durable_event_log_entry_kind[])) AS kind, - UNNEST($6::BIGINT[]) AS node_id, - UNNEST($7::BIGINT[]) AS parent_node_id, - UNNEST($8::BIGINT[]) AS branch_id, - UNNEST($9::BYTEA[]) AS data_hash, - UNNEST($10::TEXT[]) AS data_hash_alg -) - +const createDurableEventLogEntry = `-- name: CreateDurableEventLogEntry :one INSERT INTO v1_durable_event_log_entry ( + tenant_id, external_id, durable_task_id, durable_task_inserted_at, @@ -121,207 +88,52 @@ INSERT INTO v1_durable_event_log_entry ( data_hash, data_hash_alg ) -SELECT - i.external_id, - i.durable_task_id, - i.durable_task_inserted_at, - i.inserted_at, - i.kind, - i.node_id, - -- todo: check on if 0 is a safe sentinel value here or if we're zero-indexing the node id - NULLIF(i.parent_node_id, 0), - i.branch_id, - i.data_hash, - i.data_hash_alg -FROM - inputs i -ORDER BY - i.durable_task_id, - i.durable_task_inserted_at, - i.node_id -RETURNING external_id, inserted_at, id, durable_task_id, durable_task_inserted_at, kind, node_id, parent_node_id, branch_id, data_hash, data_hash_alg -` - -type CreateDurableEventLogEntriesParams struct { - Externalids []uuid.UUID `json:"externalids"` - Durabletaskids []int64 `json:"durabletaskids"` - Durabletaskinsertedats []pgtype.Timestamptz `json:"durabletaskinsertedats"` - Insertedats []pgtype.Timestamptz `json:"insertedats"` - Kinds []string `json:"kinds"` - Nodeids []int64 `json:"nodeids"` - Parentnodeids []int64 `json:"parentnodeids"` - Branchids []int64 `json:"branchids"` - Datahashes [][]byte `json:"datahashes"` - Datahashalgs []string `json:"datahashalgs"` -} - -// todo: implement UpdateLatestNodeId -// todo: conflict resolution here -func (q *Queries) CreateDurableEventLogEntries(ctx context.Context, db DBTX, arg CreateDurableEventLogEntriesParams) ([]*V1DurableEventLogEntry, error) { - rows, err := db.Query(ctx, createDurableEventLogEntries, - arg.Externalids, - arg.Durabletaskids, - arg.Durabletaskinsertedats, - arg.Insertedats, - arg.Kinds, - arg.Nodeids, - arg.Parentnodeids, - arg.Branchids, - arg.Datahashes, - arg.Datahashalgs, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []*V1DurableEventLogEntry - for rows.Next() { - var i V1DurableEventLogEntry - if err := rows.Scan( - &i.ExternalID, - &i.InsertedAt, - &i.ID, - &i.DurableTaskID, - &i.DurableTaskInsertedAt, - &i.Kind, - &i.NodeID, - &i.ParentNodeID, - &i.BranchID, - &i.DataHash, - &i.DataHashAlg, - ); err != nil { - return nil, err - } - items = append(items, &i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const createDurableEventLogFile = `-- name: CreateDurableEventLogFile :many -WITH inputs AS ( - SELECT - UNNEST($1::BIGINT[]) AS durable_task_id, - UNNEST($2::TIMESTAMPTZ[]) AS durable_task_inserted_at, - UNNEST($3::TIMESTAMPTZ[]) AS latest_inserted_at, - UNNEST($4::BIGINT[]) AS latest_node_id, - UNNEST($5::BIGINT[]) AS latest_branch_id, - UNNEST($6::BIGINT[]) AS latest_branch_first_parent_node_id +VALUES ( + $1::UUID, + $2::UUID, + $3::BIGINT, + $4::TIMESTAMPTZ, + NOW(), + $5::v1_durable_event_log_kind, + $6::BIGINT, + $7::BIGINT, + $8::BIGINT, + $9::BYTEA, + $10::TEXT ) -INSERT INTO v1_durable_event_log_file ( - durable_task_id, - durable_task_inserted_at, - latest_inserted_at, - latest_node_id, - latest_branch_id, - latest_branch_first_parent_node_id -) -SELECT - i.durable_task_id, - i.durable_task_inserted_at, - i.latest_inserted_at, - i.latest_node_id, - i.latest_branch_id, - i.latest_branch_first_parent_node_id -FROM - inputs i -RETURNING durable_task_id, durable_task_inserted_at, latest_inserted_at, latest_node_id, latest_branch_id, latest_branch_first_parent_node_id +ON CONFLICT (durable_task_id, durable_task_inserted_at, node_id) DO NOTHING +RETURNING tenant_id, external_id, inserted_at, id, durable_task_id, durable_task_inserted_at, kind, node_id, parent_node_id, branch_id, data_hash, data_hash_alg ` -type CreateDurableEventLogFileParams struct { - Durabletaskids []int64 `json:"durabletaskids"` - Durabletaskinsertedats []pgtype.Timestamptz `json:"durabletaskinsertedats"` - Latestinsertedats []pgtype.Timestamptz `json:"latestinsertedats"` - Latestnodeids []int64 `json:"latestnodeids"` - Latestbranchids []int64 `json:"latestbranchids"` - Latestbranchfirstparentnodeids []int64 `json:"latestbranchfirstparentnodeids"` +type CreateDurableEventLogEntryParams struct { + Tenantid uuid.UUID `json:"tenantid"` + Externalid uuid.UUID `json:"externalid"` + Durabletaskid int64 `json:"durabletaskid"` + Durabletaskinsertedat pgtype.Timestamptz `json:"durabletaskinsertedat"` + Kind V1DurableEventLogKind `json:"kind"` + Nodeid int64 `json:"nodeid"` + ParentNodeId pgtype.Int8 `json:"parentNodeId"` + Branchid int64 `json:"branchid"` + Datahash []byte `json:"datahash"` + Datahashalg string `json:"datahashalg"` } -func (q *Queries) CreateDurableEventLogFile(ctx context.Context, db DBTX, arg CreateDurableEventLogFileParams) ([]*V1DurableEventLogFile, error) { - rows, err := db.Query(ctx, createDurableEventLogFile, - arg.Durabletaskids, - arg.Durabletaskinsertedats, - arg.Latestinsertedats, - arg.Latestnodeids, - arg.Latestbranchids, - arg.Latestbranchfirstparentnodeids, +func (q *Queries) CreateDurableEventLogEntry(ctx context.Context, db DBTX, arg CreateDurableEventLogEntryParams) (*V1DurableEventLogEntry, error) { + row := db.QueryRow(ctx, createDurableEventLogEntry, + arg.Tenantid, + arg.Externalid, + arg.Durabletaskid, + arg.Durabletaskinsertedat, + arg.Kind, + arg.Nodeid, + arg.ParentNodeId, + arg.Branchid, + arg.Datahash, + arg.Datahashalg, ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []*V1DurableEventLogFile - for rows.Next() { - var i V1DurableEventLogFile - if err := rows.Scan( - &i.DurableTaskID, - &i.DurableTaskInsertedAt, - &i.LatestInsertedAt, - &i.LatestNodeID, - &i.LatestBranchID, - &i.LatestBranchFirstParentNodeID, - ); err != nil { - return nil, err - } - items = append(items, &i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const getDurableEventLogCallback = `-- name: GetDurableEventLogCallback :one -SELECT external_id, inserted_at, id, durable_task_id, durable_task_inserted_at, kind, key, node_id, is_satisfied -FROM v1_durable_event_log_callback -WHERE durable_task_id = $1 - AND durable_task_inserted_at = $2 - AND key = $3 -` - -type GetDurableEventLogCallbackParams struct { - Durabletaskid int64 `json:"durabletaskid"` - Durabletaskinsertedat pgtype.Timestamptz `json:"durabletaskinsertedat"` - Key string `json:"key"` -} - -func (q *Queries) GetDurableEventLogCallback(ctx context.Context, db DBTX, arg GetDurableEventLogCallbackParams) (*V1DurableEventLogCallback, error) { - row := db.QueryRow(ctx, getDurableEventLogCallback, arg.Durabletaskid, arg.Durabletaskinsertedat, arg.Key) - var i V1DurableEventLogCallback - err := row.Scan( - &i.ExternalID, - &i.InsertedAt, - &i.ID, - &i.DurableTaskID, - &i.DurableTaskInsertedAt, - &i.Kind, - &i.Key, - &i.NodeID, - &i.IsSatisfied, - ) - return &i, err -} - -const getDurableEventLogEntry = `-- name: GetDurableEventLogEntry :one -SELECT external_id, inserted_at, id, durable_task_id, durable_task_inserted_at, kind, node_id, parent_node_id, branch_id, data_hash, data_hash_alg -FROM v1_durable_event_log_entry -WHERE durable_task_id = $1 - AND durable_task_inserted_at = $2 - AND node_id = $3 -` - -type GetDurableEventLogEntryParams struct { - Durabletaskid int64 `json:"durabletaskid"` - Durabletaskinsertedat pgtype.Timestamptz `json:"durabletaskinsertedat"` - Nodeid int64 `json:"nodeid"` -} - -func (q *Queries) GetDurableEventLogEntry(ctx context.Context, db DBTX, arg GetDurableEventLogEntryParams) (*V1DurableEventLogEntry, error) { - row := db.QueryRow(ctx, getDurableEventLogEntry, arg.Durabletaskid, arg.Durabletaskinsertedat, arg.Nodeid) var i V1DurableEventLogEntry err := row.Scan( + &i.TenantID, &i.ExternalID, &i.InsertedAt, &i.ID, @@ -337,24 +149,48 @@ func (q *Queries) GetDurableEventLogEntry(ctx context.Context, db DBTX, arg GetD return &i, err } -const getDurableEventLogFileForTask = `-- name: GetDurableEventLogFileForTask :one -SELECT durable_task_id, durable_task_inserted_at, latest_inserted_at, latest_node_id, latest_branch_id, latest_branch_first_parent_node_id -FROM v1_durable_event_log_file -WHERE durable_task_id = $1 - AND durable_task_inserted_at = $2 +const createEventLogFile = `-- name: CreateEventLogFile :one +INSERT INTO v1_durable_event_log_file ( + tenant_id, + durable_task_id, + durable_task_inserted_at, + latest_invocation_count, + latest_inserted_at, + latest_node_id, + latest_branch_id, + latest_branch_first_parent_node_id +) VALUES ( + $1::UUID, + $2::BIGINT, + $3::TIMESTAMPTZ, + 0, + NOW(), + 1, + 1, + 0 +) +ON CONFLICT (durable_task_id, durable_task_inserted_at) +DO UPDATE SET + latest_node_id = GREATEST(v1_durable_event_log_file.latest_node_id, EXCLUDED.latest_node_id), + latest_inserted_at = NOW(), + latest_invocation_count = GREATEST(v1_durable_event_log_file.latest_invocation_count, EXCLUDED.latest_invocation_count) +RETURNING tenant_id, durable_task_id, durable_task_inserted_at, latest_invocation_count, latest_inserted_at, latest_node_id, latest_branch_id, latest_branch_first_parent_node_id ` -type GetDurableEventLogFileForTaskParams struct { +type CreateEventLogFileParams struct { + Tenantid uuid.UUID `json:"tenantid"` Durabletaskid int64 `json:"durabletaskid"` Durabletaskinsertedat pgtype.Timestamptz `json:"durabletaskinsertedat"` } -func (q *Queries) GetDurableEventLogFileForTask(ctx context.Context, db DBTX, arg GetDurableEventLogFileForTaskParams) (*V1DurableEventLogFile, error) { - row := db.QueryRow(ctx, getDurableEventLogFileForTask, arg.Durabletaskid, arg.Durabletaskinsertedat) +func (q *Queries) CreateEventLogFile(ctx context.Context, db DBTX, arg CreateEventLogFileParams) (*V1DurableEventLogFile, error) { + row := db.QueryRow(ctx, createEventLogFile, arg.Tenantid, arg.Durabletaskid, arg.Durabletaskinsertedat) var i V1DurableEventLogFile err := row.Scan( + &i.TenantID, &i.DurableTaskID, &i.DurableTaskInsertedAt, + &i.LatestInvocationCount, &i.LatestInsertedAt, &i.LatestNodeID, &i.LatestBranchID, @@ -363,21 +199,190 @@ func (q *Queries) GetDurableEventLogFileForTask(ctx context.Context, db DBTX, ar return &i, err } -const listDurableEventLogCallbacks = `-- name: ListDurableEventLogCallbacks :many -SELECT external_id, inserted_at, id, durable_task_id, durable_task_inserted_at, kind, key, node_id, is_satisfied -FROM v1_durable_event_log_callback -WHERE durable_task_id = $1 - AND durable_task_inserted_at = $2 -ORDER BY inserted_at ASC +const getAndLockLogFile = `-- name: GetAndLockLogFile :one +SELECT tenant_id, durable_task_id, durable_task_inserted_at, latest_invocation_count, latest_inserted_at, latest_node_id, latest_branch_id, latest_branch_first_parent_node_id +FROM v1_durable_event_log_file +WHERE durable_task_id = $1::BIGINT + AND durable_task_inserted_at = $2::TIMESTAMPTZ +FOR UPDATE ` -type ListDurableEventLogCallbacksParams struct { +type GetAndLockLogFileParams struct { Durabletaskid int64 `json:"durabletaskid"` Durabletaskinsertedat pgtype.Timestamptz `json:"durabletaskinsertedat"` } -func (q *Queries) ListDurableEventLogCallbacks(ctx context.Context, db DBTX, arg ListDurableEventLogCallbacksParams) ([]*V1DurableEventLogCallback, error) { - rows, err := db.Query(ctx, listDurableEventLogCallbacks, arg.Durabletaskid, arg.Durabletaskinsertedat) +func (q *Queries) GetAndLockLogFile(ctx context.Context, db DBTX, arg GetAndLockLogFileParams) (*V1DurableEventLogFile, error) { + row := db.QueryRow(ctx, getAndLockLogFile, arg.Durabletaskid, arg.Durabletaskinsertedat) + var i V1DurableEventLogFile + err := row.Scan( + &i.TenantID, + &i.DurableTaskID, + &i.DurableTaskInsertedAt, + &i.LatestInvocationCount, + &i.LatestInsertedAt, + &i.LatestNodeID, + &i.LatestBranchID, + &i.LatestBranchFirstParentNodeID, + ) + return &i, err +} + +const getDurableEventLogCallback = `-- name: GetDurableEventLogCallback :one +SELECT tenant_id, external_id, inserted_at, id, durable_task_id, durable_task_inserted_at, kind, node_id, is_satisfied +FROM v1_durable_event_log_callback +WHERE durable_task_id = $1::BIGINT + AND durable_task_inserted_at = $2::TIMESTAMPTZ + AND node_id = $3::BIGINT +` + +type GetDurableEventLogCallbackParams struct { + Durabletaskid int64 `json:"durabletaskid"` + Durabletaskinsertedat pgtype.Timestamptz `json:"durabletaskinsertedat"` + Nodeid int64 `json:"nodeid"` +} + +func (q *Queries) GetDurableEventLogCallback(ctx context.Context, db DBTX, arg GetDurableEventLogCallbackParams) (*V1DurableEventLogCallback, error) { + row := db.QueryRow(ctx, getDurableEventLogCallback, arg.Durabletaskid, arg.Durabletaskinsertedat, arg.Nodeid) + var i V1DurableEventLogCallback + err := row.Scan( + &i.TenantID, + &i.ExternalID, + &i.InsertedAt, + &i.ID, + &i.DurableTaskID, + &i.DurableTaskInsertedAt, + &i.Kind, + &i.NodeID, + &i.IsSatisfied, + ) + return &i, err +} + +const getDurableEventLogEntry = `-- name: GetDurableEventLogEntry :one +SELECT tenant_id, external_id, inserted_at, id, durable_task_id, durable_task_inserted_at, kind, node_id, parent_node_id, branch_id, data_hash, data_hash_alg +FROM v1_durable_event_log_entry +WHERE durable_task_id = $1::BIGINT + AND durable_task_inserted_at = $2::TIMESTAMPTZ + AND node_id = $3::BIGINT +` + +type GetDurableEventLogEntryParams struct { + Durabletaskid int64 `json:"durabletaskid"` + Durabletaskinsertedat pgtype.Timestamptz `json:"durabletaskinsertedat"` + Nodeid int64 `json:"nodeid"` +} + +func (q *Queries) GetDurableEventLogEntry(ctx context.Context, db DBTX, arg GetDurableEventLogEntryParams) (*V1DurableEventLogEntry, error) { + row := db.QueryRow(ctx, getDurableEventLogEntry, arg.Durabletaskid, arg.Durabletaskinsertedat, arg.Nodeid) + var i V1DurableEventLogEntry + err := row.Scan( + &i.TenantID, + &i.ExternalID, + &i.InsertedAt, + &i.ID, + &i.DurableTaskID, + &i.DurableTaskInsertedAt, + &i.Kind, + &i.NodeID, + &i.ParentNodeID, + &i.BranchID, + &i.DataHash, + &i.DataHashAlg, + ) + return &i, err +} + +const listSatisfiedCallbacks = `-- name: ListSatisfiedCallbacks :many +WITH tasks AS ( + SELECT t.id, t.inserted_at, t.tenant_id, t.queue, t.action_id, t.step_id, t.step_readable_id, t.workflow_id, t.workflow_version_id, t.workflow_run_id, t.schedule_timeout, t.step_timeout, t.priority, t.sticky, t.desired_worker_id, t.external_id, t.display_name, t.input, t.retry_count, t.internal_retry_count, t.app_retry_count, t.step_index, t.additional_metadata, t.dag_id, t.dag_inserted_at, t.parent_task_external_id, t.parent_task_id, t.parent_task_inserted_at, t.child_index, t.child_key, t.initial_state, t.initial_state_reason, t.concurrency_parent_strategy_ids, t.concurrency_strategy_ids, t.concurrency_keys, t.retry_backoff_factor, t.retry_max_backoff + FROM v1_lookup_table lt + JOIN v1_task t ON (t.id, t.inserted_at) = (lt.task_id, lt.inserted_at) + WHERE lt.external_id = ANY($2::UUID[]) +) + +SELECT cb.tenant_id, cb.external_id, cb.inserted_at, cb.id, cb.durable_task_id, cb.durable_task_inserted_at, cb.kind, cb.node_id, cb.is_satisfied, t.external_id AS task_external_id +FROM v1_durable_event_log_callback cb +JOIN tasks t ON (t.id, t.inserted_at) = (cb.durable_task_id, cb.durable_task_inserted_at) +WHERE + cb.node_id = ANY($1::BIGINT[]) + AND cb.is_satisfied +` + +type ListSatisfiedCallbacksParams struct { + Nodeids []int64 `json:"nodeids"` + Taskexternalids []uuid.UUID `json:"taskexternalids"` +} + +type ListSatisfiedCallbacksRow struct { + TenantID uuid.UUID `json:"tenant_id"` + ExternalID uuid.UUID `json:"external_id"` + InsertedAt pgtype.Timestamptz `json:"inserted_at"` + ID int64 `json:"id"` + DurableTaskID int64 `json:"durable_task_id"` + DurableTaskInsertedAt pgtype.Timestamptz `json:"durable_task_inserted_at"` + Kind V1DurableEventLogKind `json:"kind"` + NodeID int64 `json:"node_id"` + IsSatisfied bool `json:"is_satisfied"` + TaskExternalID uuid.UUID `json:"task_external_id"` +} + +func (q *Queries) ListSatisfiedCallbacks(ctx context.Context, db DBTX, arg ListSatisfiedCallbacksParams) ([]*ListSatisfiedCallbacksRow, error) { + rows, err := db.Query(ctx, listSatisfiedCallbacks, arg.Nodeids, arg.Taskexternalids) + if err != nil { + return nil, err + } + defer rows.Close() + var items []*ListSatisfiedCallbacksRow + for rows.Next() { + var i ListSatisfiedCallbacksRow + if err := rows.Scan( + &i.TenantID, + &i.ExternalID, + &i.InsertedAt, + &i.ID, + &i.DurableTaskID, + &i.DurableTaskInsertedAt, + &i.Kind, + &i.NodeID, + &i.IsSatisfied, + &i.TaskExternalID, + ); err != nil { + return nil, err + } + items = append(items, &i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const updateDurableEventLogCallbacksSatisfied = `-- name: UpdateDurableEventLogCallbacksSatisfied :many +WITH inputs AS ( + SELECT + UNNEST($1::BIGINT[]) AS durable_task_id, + UNNEST($2::TIMESTAMPTZ[]) AS durable_task_inserted_at, + UNNEST($3::BIGINT[]) AS node_id +) + +UPDATE v1_durable_event_log_callback +SET is_satisfied = true +FROM inputs +WHERE v1_durable_event_log_callback.durable_task_id = inputs.durable_task_id + AND v1_durable_event_log_callback.durable_task_inserted_at = inputs.durable_task_inserted_at + AND v1_durable_event_log_callback.node_id = inputs.node_id +RETURNING v1_durable_event_log_callback.tenant_id, v1_durable_event_log_callback.external_id, v1_durable_event_log_callback.inserted_at, v1_durable_event_log_callback.id, v1_durable_event_log_callback.durable_task_id, v1_durable_event_log_callback.durable_task_inserted_at, v1_durable_event_log_callback.kind, v1_durable_event_log_callback.node_id, v1_durable_event_log_callback.is_satisfied +` + +type UpdateDurableEventLogCallbacksSatisfiedParams struct { + Durabletaskids []int64 `json:"durabletaskids"` + Durabletaskinsertedats []pgtype.Timestamptz `json:"durabletaskinsertedats"` + Nodeids []int64 `json:"nodeids"` +} + +func (q *Queries) UpdateDurableEventLogCallbacksSatisfied(ctx context.Context, db DBTX, arg UpdateDurableEventLogCallbacksSatisfiedParams) ([]*V1DurableEventLogCallback, error) { + rows, err := db.Query(ctx, updateDurableEventLogCallbacksSatisfied, arg.Durabletaskids, arg.Durabletaskinsertedats, arg.Nodeids) if err != nil { return nil, err } @@ -386,13 +391,13 @@ func (q *Queries) ListDurableEventLogCallbacks(ctx context.Context, db DBTX, arg for rows.Next() { var i V1DurableEventLogCallback if err := rows.Scan( + &i.TenantID, &i.ExternalID, &i.InsertedAt, &i.ID, &i.DurableTaskID, &i.DurableTaskInsertedAt, &i.Kind, - &i.Key, &i.NodeID, &i.IsSatisfied, ); err != nil { @@ -406,85 +411,40 @@ func (q *Queries) ListDurableEventLogCallbacks(ctx context.Context, db DBTX, arg return items, nil } -const listDurableEventLogEntries = `-- name: ListDurableEventLogEntries :many -SELECT external_id, inserted_at, id, durable_task_id, durable_task_inserted_at, kind, node_id, parent_node_id, branch_id, data_hash, data_hash_alg -FROM v1_durable_event_log_entry -WHERE durable_task_id = $1 - AND durable_task_inserted_at = $2 -ORDER BY node_id ASC +const updateLogFileNodeIdInvocationCount = `-- name: UpdateLogFileNodeIdInvocationCount :one +UPDATE v1_durable_event_log_file +SET + latest_node_id = COALESCE($1::BIGINT, v1_durable_event_log_file.latest_node_id), + latest_invocation_count = COALESCE($2::BIGINT, v1_durable_event_log_file.latest_invocation_count) +WHERE durable_task_id = $3::BIGINT + AND durable_task_inserted_at = $4::TIMESTAMPTZ +RETURNING tenant_id, durable_task_id, durable_task_inserted_at, latest_invocation_count, latest_inserted_at, latest_node_id, latest_branch_id, latest_branch_first_parent_node_id ` -type ListDurableEventLogEntriesParams struct { +type UpdateLogFileNodeIdInvocationCountParams struct { + NodeId pgtype.Int8 `json:"nodeId"` + InvocationCount pgtype.Int8 `json:"invocationCount"` Durabletaskid int64 `json:"durabletaskid"` Durabletaskinsertedat pgtype.Timestamptz `json:"durabletaskinsertedat"` } -func (q *Queries) ListDurableEventLogEntries(ctx context.Context, db DBTX, arg ListDurableEventLogEntriesParams) ([]*V1DurableEventLogEntry, error) { - rows, err := db.Query(ctx, listDurableEventLogEntries, arg.Durabletaskid, arg.Durabletaskinsertedat) - if err != nil { - return nil, err - } - defer rows.Close() - var items []*V1DurableEventLogEntry - for rows.Next() { - var i V1DurableEventLogEntry - if err := rows.Scan( - &i.ExternalID, - &i.InsertedAt, - &i.ID, - &i.DurableTaskID, - &i.DurableTaskInsertedAt, - &i.Kind, - &i.NodeID, - &i.ParentNodeID, - &i.BranchID, - &i.DataHash, - &i.DataHashAlg, - ); err != nil { - return nil, err - } - items = append(items, &i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const updateDurableEventLogCallbackSatisfied = `-- name: UpdateDurableEventLogCallbackSatisfied :one -UPDATE v1_durable_event_log_callback -SET is_satisfied = $1 -WHERE durable_task_id = $2 - AND durable_task_inserted_at = $3 - AND key = $4 -RETURNING external_id, inserted_at, id, durable_task_id, durable_task_inserted_at, kind, key, node_id, is_satisfied -` - -type UpdateDurableEventLogCallbackSatisfiedParams struct { - Issatisfied bool `json:"issatisfied"` - Durabletaskid int64 `json:"durabletaskid"` - Durabletaskinsertedat pgtype.Timestamptz `json:"durabletaskinsertedat"` - Key string `json:"key"` -} - -func (q *Queries) UpdateDurableEventLogCallbackSatisfied(ctx context.Context, db DBTX, arg UpdateDurableEventLogCallbackSatisfiedParams) (*V1DurableEventLogCallback, error) { - row := db.QueryRow(ctx, updateDurableEventLogCallbackSatisfied, - arg.Issatisfied, +func (q *Queries) UpdateLogFileNodeIdInvocationCount(ctx context.Context, db DBTX, arg UpdateLogFileNodeIdInvocationCountParams) (*V1DurableEventLogFile, error) { + row := db.QueryRow(ctx, updateLogFileNodeIdInvocationCount, + arg.NodeId, + arg.InvocationCount, arg.Durabletaskid, arg.Durabletaskinsertedat, - arg.Key, ) - var i V1DurableEventLogCallback + var i V1DurableEventLogFile err := row.Scan( - &i.ExternalID, - &i.InsertedAt, - &i.ID, + &i.TenantID, &i.DurableTaskID, &i.DurableTaskInsertedAt, - &i.Kind, - &i.Key, - &i.NodeID, - &i.IsSatisfied, + &i.LatestInvocationCount, + &i.LatestInsertedAt, + &i.LatestNodeID, + &i.LatestBranchID, + &i.LatestBranchFirstParentNodeID, ) return &i, err } diff --git a/pkg/repository/sqlcv1/matches-overwrite.sql.go b/pkg/repository/sqlcv1/matches-overwrite.sql.go index 3ba9d6537..07f8a2032 100644 --- a/pkg/repository/sqlcv1/matches-overwrite.sql.go +++ b/pkg/repository/sqlcv1/matches-overwrite.sql.go @@ -307,3 +307,123 @@ func (q *Queries) CreateMatchesForDAGTriggers(ctx context.Context, db DBTX, arg } return items, nil } + +const createMatchesForSignalTriggers = `-- name: CreateMatchesForSignalTriggers :many +WITH input AS ( + SELECT + tenant_id, kind, signal_task_id, signal_task_inserted_at, signal_external_id, signal_key, callback_durable_task_id, callback_durable_task_inserted_at, callback_node_id, callback_durable_task_external_id + FROM + ( + SELECT + unnest($1::uuid[]) AS tenant_id, + unnest(cast($2::text[] as v1_match_kind[])) AS kind, + unnest($3::bigint[]) AS signal_task_id, + unnest($4::timestamptz[]) AS signal_task_inserted_at, + unnest($5::uuid[]) AS signal_external_id, + unnest($6::text[]) AS signal_key, + unnest($7::bigint[]) AS callback_durable_task_id, + unnest($8::timestamptz[]) AS callback_durable_task_inserted_at, + unnest($9::bigint[]) AS callback_node_id, + unnest($10::uuid[]) AS callback_durable_task_external_id + ) AS subquery +) +INSERT INTO v1_match ( + tenant_id, + kind, + signal_task_id, + signal_task_inserted_at, + signal_external_id, + signal_key, + durable_event_log_callback_durable_task_id, + durable_event_log_callback_durable_task_inserted_at, + durable_event_log_callback_node_id, + durable_event_log_callback_durable_task_external_id +) +SELECT + i.tenant_id, + i.kind, + i.signal_task_id, + i.signal_task_inserted_at, + i.signal_external_id, + i.signal_key, + i.callback_durable_task_id, + i.callback_durable_task_inserted_at, + i.callback_node_id, + i.callback_durable_task_external_id +FROM + input i +RETURNING + id, tenant_id, kind, is_satisfied, existing_data, signal_task_id, signal_task_inserted_at, signal_external_id, signal_key, trigger_dag_id, trigger_dag_inserted_at, trigger_step_id, trigger_step_index, trigger_external_id, trigger_workflow_run_id, trigger_parent_task_external_id, trigger_parent_task_id, trigger_parent_task_inserted_at, trigger_child_index, trigger_child_key, trigger_existing_task_id, trigger_existing_task_inserted_at, trigger_priority, durable_event_log_callback_durable_task_external_id, durable_event_log_callback_durable_task_id, durable_event_log_callback_durable_task_inserted_at, durable_event_log_callback_node_id +` + +type CreateMatchesForSignalTriggersParams struct { + Tenantids []uuid.UUID `json:"tenantids"` + Kinds []string `json:"kinds"` + Signaltaskids []int64 `json:"signaltaskids"` + Signaltaskinsertedats []pgtype.Timestamptz `json:"signaltaskinsertedats"` + Signalexternalids []uuid.UUID `json:"signalexternalids"` + Signalkeys []string `json:"signalkeys"` + Callbackdurabletaskids []*int64 `json:"callbackdurabletaskids"` + Callbackdurabletaskinsertedats []pgtype.Timestamptz `json:"callbackdurabletaskinsertedats"` + Callbacknodeids []*int64 `json:"callbacknodeids"` + Callbackdurabletaskexternalids []*uuid.UUID `json:"callbackdurabletaskexternalids"` +} + +func (q *Queries) CreateMatchesForSignalTriggers(ctx context.Context, db DBTX, arg CreateMatchesForSignalTriggersParams) ([]*V1Match, error) { + rows, err := db.Query(ctx, createMatchesForSignalTriggers, + arg.Tenantids, + arg.Kinds, + arg.Signaltaskids, + arg.Signaltaskinsertedats, + arg.Signalexternalids, + arg.Signalkeys, + arg.Callbackdurabletaskids, + arg.Callbackdurabletaskinsertedats, + arg.Callbacknodeids, + arg.Callbackdurabletaskexternalids, + ) + if err != nil { + return nil, err + } + defer rows.Close() + var items []*V1Match + for rows.Next() { + var i V1Match + if err := rows.Scan( + &i.ID, + &i.TenantID, + &i.Kind, + &i.IsSatisfied, + &i.ExistingData, + &i.SignalTaskID, + &i.SignalTaskInsertedAt, + &i.SignalExternalID, + &i.SignalKey, + &i.TriggerDagID, + &i.TriggerDagInsertedAt, + &i.TriggerStepID, + &i.TriggerStepIndex, + &i.TriggerExternalID, + &i.TriggerWorkflowRunID, + &i.TriggerParentTaskExternalID, + &i.TriggerParentTaskID, + &i.TriggerParentTaskInsertedAt, + &i.TriggerChildIndex, + &i.TriggerChildKey, + &i.TriggerExistingTaskID, + &i.TriggerExistingTaskInsertedAt, + &i.TriggerPriority, + &i.DurableEventLogCallbackDurableTaskExternalID, + &i.DurableEventLogCallbackDurableTaskID, + &i.DurableEventLogCallbackDurableTaskInsertedAt, + &i.DurableEventLogCallbackNodeID, + ); err != nil { + return nil, err + } + items = append(items, &i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/pkg/repository/sqlcv1/matches.sql b/pkg/repository/sqlcv1/matches.sql index 22019aeba..3bd79e391 100644 --- a/pkg/repository/sqlcv1/matches.sql +++ b/pkg/repository/sqlcv1/matches.sql @@ -1,38 +1,3 @@ --- name: CreateMatchesForSignalTriggers :many -WITH input AS ( - SELECT - * - FROM - ( - SELECT - unnest(@tenantIds::uuid[]) AS tenant_id, - unnest(cast(@kinds::text[] as v1_match_kind[])) AS kind, - unnest(@signalTaskIds::bigint[]) AS signal_task_id, - unnest(@signalTaskInsertedAts::timestamptz[]) AS signal_task_inserted_at, - unnest(@signalExternalIds::uuid[]) AS signal_external_id, - unnest(@signalKeys::text[]) AS signal_key - ) AS subquery -) -INSERT INTO v1_match ( - tenant_id, - kind, - signal_task_id, - signal_task_inserted_at, - signal_external_id, - signal_key -) -SELECT - i.tenant_id, - i.kind, - i.signal_task_id, - i.signal_task_inserted_at, - i.signal_external_id, - i.signal_key -FROM - input i -RETURNING - *; - -- name: CreateMatchConditions :copyfrom INSERT INTO v1_match_condition ( v1_match_id, diff --git a/pkg/repository/sqlcv1/matches.sql.go b/pkg/repository/sqlcv1/matches.sql.go index e016a2d37..b50ee3229 100644 --- a/pkg/repository/sqlcv1/matches.sql.go +++ b/pkg/repository/sqlcv1/matches.sql.go @@ -49,102 +49,6 @@ type CreateMatchConditionsParams struct { Data []byte `json:"data"` } -const createMatchesForSignalTriggers = `-- name: CreateMatchesForSignalTriggers :many -WITH input AS ( - SELECT - tenant_id, kind, signal_task_id, signal_task_inserted_at, signal_external_id, signal_key - FROM - ( - SELECT - unnest($1::uuid[]) AS tenant_id, - unnest(cast($2::text[] as v1_match_kind[])) AS kind, - unnest($3::bigint[]) AS signal_task_id, - unnest($4::timestamptz[]) AS signal_task_inserted_at, - unnest($5::uuid[]) AS signal_external_id, - unnest($6::text[]) AS signal_key - ) AS subquery -) -INSERT INTO v1_match ( - tenant_id, - kind, - signal_task_id, - signal_task_inserted_at, - signal_external_id, - signal_key -) -SELECT - i.tenant_id, - i.kind, - i.signal_task_id, - i.signal_task_inserted_at, - i.signal_external_id, - i.signal_key -FROM - input i -RETURNING - id, tenant_id, kind, is_satisfied, existing_data, signal_task_id, signal_task_inserted_at, signal_external_id, signal_key, trigger_dag_id, trigger_dag_inserted_at, trigger_step_id, trigger_step_index, trigger_external_id, trigger_workflow_run_id, trigger_parent_task_external_id, trigger_parent_task_id, trigger_parent_task_inserted_at, trigger_child_index, trigger_child_key, trigger_existing_task_id, trigger_existing_task_inserted_at, trigger_priority -` - -type CreateMatchesForSignalTriggersParams struct { - Tenantids []uuid.UUID `json:"tenantids"` - Kinds []string `json:"kinds"` - Signaltaskids []int64 `json:"signaltaskids"` - Signaltaskinsertedats []pgtype.Timestamptz `json:"signaltaskinsertedats"` - Signalexternalids []uuid.UUID `json:"signalexternalids"` - Signalkeys []string `json:"signalkeys"` -} - -func (q *Queries) CreateMatchesForSignalTriggers(ctx context.Context, db DBTX, arg CreateMatchesForSignalTriggersParams) ([]*V1Match, error) { - rows, err := db.Query(ctx, createMatchesForSignalTriggers, - arg.Tenantids, - arg.Kinds, - arg.Signaltaskids, - arg.Signaltaskinsertedats, - arg.Signalexternalids, - arg.Signalkeys, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []*V1Match - for rows.Next() { - var i V1Match - if err := rows.Scan( - &i.ID, - &i.TenantID, - &i.Kind, - &i.IsSatisfied, - &i.ExistingData, - &i.SignalTaskID, - &i.SignalTaskInsertedAt, - &i.SignalExternalID, - &i.SignalKey, - &i.TriggerDagID, - &i.TriggerDagInsertedAt, - &i.TriggerStepID, - &i.TriggerStepIndex, - &i.TriggerExternalID, - &i.TriggerWorkflowRunID, - &i.TriggerParentTaskExternalID, - &i.TriggerParentTaskID, - &i.TriggerParentTaskInsertedAt, - &i.TriggerChildIndex, - &i.TriggerChildKey, - &i.TriggerExistingTaskID, - &i.TriggerExistingTaskInsertedAt, - &i.TriggerPriority, - ); err != nil { - return nil, err - } - items = append(items, &i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - const getSatisfiedMatchConditions = `-- name: GetSatisfiedMatchConditions :many WITH input AS ( SELECT @@ -323,7 +227,7 @@ WITH match_counts AS ( GROUP BY v1_match_id ), result_matches AS ( SELECT - m.id, m.tenant_id, m.kind, m.is_satisfied, m.existing_data, m.signal_task_id, m.signal_task_inserted_at, m.signal_external_id, m.signal_key, m.trigger_dag_id, m.trigger_dag_inserted_at, m.trigger_step_id, m.trigger_step_index, m.trigger_external_id, m.trigger_workflow_run_id, m.trigger_parent_task_external_id, m.trigger_parent_task_id, m.trigger_parent_task_inserted_at, m.trigger_child_index, m.trigger_child_key, m.trigger_existing_task_id, m.trigger_existing_task_inserted_at, m.trigger_priority, + m.id, m.tenant_id, m.kind, m.is_satisfied, m.existing_data, m.signal_task_id, m.signal_task_inserted_at, m.signal_external_id, m.signal_key, m.trigger_dag_id, m.trigger_dag_inserted_at, m.trigger_step_id, m.trigger_step_index, m.trigger_external_id, m.trigger_workflow_run_id, m.trigger_parent_task_external_id, m.trigger_parent_task_id, m.trigger_parent_task_inserted_at, m.trigger_child_index, m.trigger_child_key, m.trigger_existing_task_id, m.trigger_existing_task_inserted_at, m.trigger_priority, m.durable_event_log_callback_durable_task_external_id, m.durable_event_log_callback_durable_task_id, m.durable_event_log_callback_durable_task_inserted_at, m.durable_event_log_callback_node_id, CASE WHEN (mc.total_skip_groups > 0 AND mc.total_skip_groups = mc.satisfied_skip_groups) THEN 'SKIP' WHEN (mc.total_cancel_groups > 0 AND mc.total_cancel_groups = mc.satisfied_cancel_groups) THEN 'CANCEL' @@ -389,7 +293,7 @@ WITH match_counts AS ( id IN (SELECT id FROM deleted_conditions) ) SELECT - rm.id, rm.tenant_id, rm.kind, rm.is_satisfied, rm.existing_data, rm.signal_task_id, rm.signal_task_inserted_at, rm.signal_external_id, rm.signal_key, rm.trigger_dag_id, rm.trigger_dag_inserted_at, rm.trigger_step_id, rm.trigger_step_index, rm.trigger_external_id, rm.trigger_workflow_run_id, rm.trigger_parent_task_external_id, rm.trigger_parent_task_id, rm.trigger_parent_task_inserted_at, rm.trigger_child_index, rm.trigger_child_key, rm.trigger_existing_task_id, rm.trigger_existing_task_inserted_at, rm.trigger_priority, rm.action, + rm.id, rm.tenant_id, rm.kind, rm.is_satisfied, rm.existing_data, rm.signal_task_id, rm.signal_task_inserted_at, rm.signal_external_id, rm.signal_key, rm.trigger_dag_id, rm.trigger_dag_inserted_at, rm.trigger_step_id, rm.trigger_step_index, rm.trigger_external_id, rm.trigger_workflow_run_id, rm.trigger_parent_task_external_id, rm.trigger_parent_task_id, rm.trigger_parent_task_inserted_at, rm.trigger_child_index, rm.trigger_child_key, rm.trigger_existing_task_id, rm.trigger_existing_task_inserted_at, rm.trigger_priority, rm.durable_event_log_callback_durable_task_external_id, rm.durable_event_log_callback_durable_task_id, rm.durable_event_log_callback_durable_task_inserted_at, rm.durable_event_log_callback_node_id, rm.action, COALESCE(rm.existing_data || d.mc_aggregated_data, d.mc_aggregated_data)::jsonb AS mc_aggregated_data FROM result_matches rm @@ -398,31 +302,35 @@ LEFT JOIN ` type SaveSatisfiedMatchConditionsRow struct { - ID int64 `json:"id"` - TenantID uuid.UUID `json:"tenant_id"` - Kind V1MatchKind `json:"kind"` - IsSatisfied bool `json:"is_satisfied"` - ExistingData []byte `json:"existing_data"` - SignalTaskID pgtype.Int8 `json:"signal_task_id"` - SignalTaskInsertedAt pgtype.Timestamptz `json:"signal_task_inserted_at"` - SignalExternalID *uuid.UUID `json:"signal_external_id"` - SignalKey pgtype.Text `json:"signal_key"` - TriggerDagID pgtype.Int8 `json:"trigger_dag_id"` - TriggerDagInsertedAt pgtype.Timestamptz `json:"trigger_dag_inserted_at"` - TriggerStepID *uuid.UUID `json:"trigger_step_id"` - TriggerStepIndex pgtype.Int8 `json:"trigger_step_index"` - TriggerExternalID *uuid.UUID `json:"trigger_external_id"` - TriggerWorkflowRunID *uuid.UUID `json:"trigger_workflow_run_id"` - TriggerParentTaskExternalID *uuid.UUID `json:"trigger_parent_task_external_id"` - TriggerParentTaskID pgtype.Int8 `json:"trigger_parent_task_id"` - TriggerParentTaskInsertedAt pgtype.Timestamptz `json:"trigger_parent_task_inserted_at"` - TriggerChildIndex pgtype.Int8 `json:"trigger_child_index"` - TriggerChildKey pgtype.Text `json:"trigger_child_key"` - TriggerExistingTaskID pgtype.Int8 `json:"trigger_existing_task_id"` - TriggerExistingTaskInsertedAt pgtype.Timestamptz `json:"trigger_existing_task_inserted_at"` - TriggerPriority pgtype.Int4 `json:"trigger_priority"` - Action V1MatchConditionAction `json:"action"` - McAggregatedData []byte `json:"mc_aggregated_data"` + ID int64 `json:"id"` + TenantID uuid.UUID `json:"tenant_id"` + Kind V1MatchKind `json:"kind"` + IsSatisfied bool `json:"is_satisfied"` + ExistingData []byte `json:"existing_data"` + SignalTaskID pgtype.Int8 `json:"signal_task_id"` + SignalTaskInsertedAt pgtype.Timestamptz `json:"signal_task_inserted_at"` + SignalExternalID *uuid.UUID `json:"signal_external_id"` + SignalKey pgtype.Text `json:"signal_key"` + TriggerDagID pgtype.Int8 `json:"trigger_dag_id"` + TriggerDagInsertedAt pgtype.Timestamptz `json:"trigger_dag_inserted_at"` + TriggerStepID *uuid.UUID `json:"trigger_step_id"` + TriggerStepIndex pgtype.Int8 `json:"trigger_step_index"` + TriggerExternalID *uuid.UUID `json:"trigger_external_id"` + TriggerWorkflowRunID *uuid.UUID `json:"trigger_workflow_run_id"` + TriggerParentTaskExternalID *uuid.UUID `json:"trigger_parent_task_external_id"` + TriggerParentTaskID pgtype.Int8 `json:"trigger_parent_task_id"` + TriggerParentTaskInsertedAt pgtype.Timestamptz `json:"trigger_parent_task_inserted_at"` + TriggerChildIndex pgtype.Int8 `json:"trigger_child_index"` + TriggerChildKey pgtype.Text `json:"trigger_child_key"` + TriggerExistingTaskID pgtype.Int8 `json:"trigger_existing_task_id"` + TriggerExistingTaskInsertedAt pgtype.Timestamptz `json:"trigger_existing_task_inserted_at"` + TriggerPriority pgtype.Int4 `json:"trigger_priority"` + DurableEventLogCallbackDurableTaskExternalID *uuid.UUID `json:"durable_event_log_callback_durable_task_external_id"` + DurableEventLogCallbackDurableTaskID pgtype.Int8 `json:"durable_event_log_callback_durable_task_id"` + DurableEventLogCallbackDurableTaskInsertedAt pgtype.Timestamptz `json:"durable_event_log_callback_durable_task_inserted_at"` + DurableEventLogCallbackNodeID pgtype.Int8 `json:"durable_event_log_callback_node_id"` + Action V1MatchConditionAction `json:"action"` + McAggregatedData []byte `json:"mc_aggregated_data"` } // NOTE: we have to break this into a separate query because CTEs can't see modified rows @@ -462,6 +370,10 @@ func (q *Queries) SaveSatisfiedMatchConditions(ctx context.Context, db DBTX, mat &i.TriggerExistingTaskID, &i.TriggerExistingTaskInsertedAt, &i.TriggerPriority, + &i.DurableEventLogCallbackDurableTaskExternalID, + &i.DurableEventLogCallbackDurableTaskID, + &i.DurableEventLogCallbackDurableTaskInsertedAt, + &i.DurableEventLogCallbackNodeID, &i.Action, &i.McAggregatedData, ); err != nil { diff --git a/pkg/repository/sqlcv1/models.go b/pkg/repository/sqlcv1/models.go index 20b59458a..0b3337bce 100644 --- a/pkg/repository/sqlcv1/models.go +++ b/pkg/repository/sqlcv1/models.go @@ -987,90 +987,47 @@ func (ns NullV1ConcurrencyStrategy) Value() (driver.Value, error) { return string(ns.V1ConcurrencyStrategy), nil } -type V1DurableEventLogCallbackKind string +type V1DurableEventLogKind string const ( - V1DurableEventLogCallbackKindRUNCOMPLETED V1DurableEventLogCallbackKind = "RUN_COMPLETED" - V1DurableEventLogCallbackKindWAITFORCOMPLETED V1DurableEventLogCallbackKind = "WAIT_FOR_COMPLETED" - V1DurableEventLogCallbackKindMEMOCOMPLETED V1DurableEventLogCallbackKind = "MEMO_COMPLETED" + V1DurableEventLogKindRUN V1DurableEventLogKind = "RUN" + V1DurableEventLogKindWAITFOR V1DurableEventLogKind = "WAIT_FOR" + V1DurableEventLogKindMEMO V1DurableEventLogKind = "MEMO" ) -func (e *V1DurableEventLogCallbackKind) Scan(src interface{}) error { +func (e *V1DurableEventLogKind) Scan(src interface{}) error { switch s := src.(type) { case []byte: - *e = V1DurableEventLogCallbackKind(s) + *e = V1DurableEventLogKind(s) case string: - *e = V1DurableEventLogCallbackKind(s) + *e = V1DurableEventLogKind(s) default: - return fmt.Errorf("unsupported scan type for V1DurableEventLogCallbackKind: %T", src) + return fmt.Errorf("unsupported scan type for V1DurableEventLogKind: %T", src) } return nil } -type NullV1DurableEventLogCallbackKind struct { - V1DurableEventLogCallbackKind V1DurableEventLogCallbackKind `json:"v1_durable_event_log_callback_kind"` - Valid bool `json:"valid"` // Valid is true if V1DurableEventLogCallbackKind is not NULL +type NullV1DurableEventLogKind struct { + V1DurableEventLogKind V1DurableEventLogKind `json:"v1_durable_event_log_kind"` + Valid bool `json:"valid"` // Valid is true if V1DurableEventLogKind is not NULL } // Scan implements the Scanner interface. -func (ns *NullV1DurableEventLogCallbackKind) Scan(value interface{}) error { +func (ns *NullV1DurableEventLogKind) Scan(value interface{}) error { if value == nil { - ns.V1DurableEventLogCallbackKind, ns.Valid = "", false + ns.V1DurableEventLogKind, ns.Valid = "", false return nil } ns.Valid = true - return ns.V1DurableEventLogCallbackKind.Scan(value) + return ns.V1DurableEventLogKind.Scan(value) } // Value implements the driver Valuer interface. -func (ns NullV1DurableEventLogCallbackKind) Value() (driver.Value, error) { +func (ns NullV1DurableEventLogKind) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return string(ns.V1DurableEventLogCallbackKind), nil -} - -type V1DurableEventLogEntryKind string - -const ( - V1DurableEventLogEntryKindRUNTRIGGERED V1DurableEventLogEntryKind = "RUN_TRIGGERED" - V1DurableEventLogEntryKindWAITFORSTARTED V1DurableEventLogEntryKind = "WAIT_FOR_STARTED" - V1DurableEventLogEntryKindMEMOSTARTED V1DurableEventLogEntryKind = "MEMO_STARTED" -) - -func (e *V1DurableEventLogEntryKind) Scan(src interface{}) error { - switch s := src.(type) { - case []byte: - *e = V1DurableEventLogEntryKind(s) - case string: - *e = V1DurableEventLogEntryKind(s) - default: - return fmt.Errorf("unsupported scan type for V1DurableEventLogEntryKind: %T", src) - } - return nil -} - -type NullV1DurableEventLogEntryKind struct { - V1DurableEventLogEntryKind V1DurableEventLogEntryKind `json:"v1_durable_event_log_entry_kind"` - Valid bool `json:"valid"` // Valid is true if V1DurableEventLogEntryKind is not NULL -} - -// Scan implements the Scanner interface. -func (ns *NullV1DurableEventLogEntryKind) Scan(value interface{}) error { - if value == nil { - ns.V1DurableEventLogEntryKind, ns.Valid = "", false - return nil - } - ns.Valid = true - return ns.V1DurableEventLogEntryKind.Scan(value) -} - -// Value implements the driver Valuer interface. -func (ns NullV1DurableEventLogEntryKind) Value() (driver.Value, error) { - if !ns.Valid { - return nil, nil - } - return string(ns.V1DurableEventLogEntryKind), nil + return string(ns.V1DurableEventLogKind), nil } type V1EventType string @@ -1570,12 +1527,13 @@ func (ns NullV1PayloadLocationOlap) Value() (driver.Value, error) { type V1PayloadType string const ( - V1PayloadTypeTASKINPUT V1PayloadType = "TASK_INPUT" - V1PayloadTypeDAGINPUT V1PayloadType = "DAG_INPUT" - V1PayloadTypeTASKOUTPUT V1PayloadType = "TASK_OUTPUT" - V1PayloadTypeTASKEVENTDATA V1PayloadType = "TASK_EVENT_DATA" - V1PayloadTypeUSEREVENTINPUT V1PayloadType = "USER_EVENT_INPUT" - V1PayloadTypeDURABLEEVENTLOGENTRYDATA V1PayloadType = "DURABLE_EVENT_LOG_ENTRY_DATA" + V1PayloadTypeTASKINPUT V1PayloadType = "TASK_INPUT" + V1PayloadTypeDAGINPUT V1PayloadType = "DAG_INPUT" + V1PayloadTypeTASKOUTPUT V1PayloadType = "TASK_OUTPUT" + V1PayloadTypeTASKEVENTDATA V1PayloadType = "TASK_EVENT_DATA" + V1PayloadTypeUSEREVENTINPUT V1PayloadType = "USER_EVENT_INPUT" + V1PayloadTypeDURABLEEVENTLOGENTRYDATA V1PayloadType = "DURABLE_EVENT_LOG_ENTRY_DATA" + V1PayloadTypeDURABLEEVENTLOGCALLBACKRESULTDATA V1PayloadType = "DURABLE_EVENT_LOG_CALLBACK_RESULT_DATA" ) func (e *V1PayloadType) Scan(src interface{}) error { @@ -3104,34 +3062,37 @@ type V1DagsOlap struct { } type V1DurableEventLogCallback struct { - ExternalID uuid.UUID `json:"external_id"` - InsertedAt pgtype.Timestamptz `json:"inserted_at"` - ID int64 `json:"id"` - DurableTaskID int64 `json:"durable_task_id"` - DurableTaskInsertedAt pgtype.Timestamptz `json:"durable_task_inserted_at"` - Kind NullV1DurableEventLogCallbackKind `json:"kind"` - Key string `json:"key"` - NodeID int64 `json:"node_id"` - IsSatisfied bool `json:"is_satisfied"` + TenantID uuid.UUID `json:"tenant_id"` + ExternalID uuid.UUID `json:"external_id"` + InsertedAt pgtype.Timestamptz `json:"inserted_at"` + ID int64 `json:"id"` + DurableTaskID int64 `json:"durable_task_id"` + DurableTaskInsertedAt pgtype.Timestamptz `json:"durable_task_inserted_at"` + Kind V1DurableEventLogKind `json:"kind"` + NodeID int64 `json:"node_id"` + IsSatisfied bool `json:"is_satisfied"` } type V1DurableEventLogEntry struct { - ExternalID uuid.UUID `json:"external_id"` - InsertedAt pgtype.Timestamptz `json:"inserted_at"` - ID int64 `json:"id"` - DurableTaskID int64 `json:"durable_task_id"` - DurableTaskInsertedAt pgtype.Timestamptz `json:"durable_task_inserted_at"` - Kind NullV1DurableEventLogEntryKind `json:"kind"` - NodeID int64 `json:"node_id"` - ParentNodeID pgtype.Int8 `json:"parent_node_id"` - BranchID int64 `json:"branch_id"` - DataHash []byte `json:"data_hash"` - DataHashAlg pgtype.Text `json:"data_hash_alg"` + TenantID uuid.UUID `json:"tenant_id"` + ExternalID uuid.UUID `json:"external_id"` + InsertedAt pgtype.Timestamptz `json:"inserted_at"` + ID int64 `json:"id"` + DurableTaskID int64 `json:"durable_task_id"` + DurableTaskInsertedAt pgtype.Timestamptz `json:"durable_task_inserted_at"` + Kind V1DurableEventLogKind `json:"kind"` + NodeID int64 `json:"node_id"` + ParentNodeID pgtype.Int8 `json:"parent_node_id"` + BranchID int64 `json:"branch_id"` + DataHash []byte `json:"data_hash"` + DataHashAlg pgtype.Text `json:"data_hash_alg"` } type V1DurableEventLogFile struct { + TenantID uuid.UUID `json:"tenant_id"` DurableTaskID int64 `json:"durable_task_id"` DurableTaskInsertedAt pgtype.Timestamptz `json:"durable_task_inserted_at"` + LatestInvocationCount int64 `json:"latest_invocation_count"` LatestInsertedAt pgtype.Timestamptz `json:"latest_inserted_at"` LatestNodeID int64 `json:"latest_node_id"` LatestBranchID int64 `json:"latest_branch_id"` @@ -3277,29 +3238,33 @@ type V1LookupTableOlap struct { } type V1Match struct { - ID int64 `json:"id"` - TenantID uuid.UUID `json:"tenant_id"` - Kind V1MatchKind `json:"kind"` - IsSatisfied bool `json:"is_satisfied"` - ExistingData []byte `json:"existing_data"` - SignalTaskID pgtype.Int8 `json:"signal_task_id"` - SignalTaskInsertedAt pgtype.Timestamptz `json:"signal_task_inserted_at"` - SignalExternalID *uuid.UUID `json:"signal_external_id"` - SignalKey pgtype.Text `json:"signal_key"` - TriggerDagID pgtype.Int8 `json:"trigger_dag_id"` - TriggerDagInsertedAt pgtype.Timestamptz `json:"trigger_dag_inserted_at"` - TriggerStepID *uuid.UUID `json:"trigger_step_id"` - TriggerStepIndex pgtype.Int8 `json:"trigger_step_index"` - TriggerExternalID *uuid.UUID `json:"trigger_external_id"` - TriggerWorkflowRunID *uuid.UUID `json:"trigger_workflow_run_id"` - TriggerParentTaskExternalID *uuid.UUID `json:"trigger_parent_task_external_id"` - TriggerParentTaskID pgtype.Int8 `json:"trigger_parent_task_id"` - TriggerParentTaskInsertedAt pgtype.Timestamptz `json:"trigger_parent_task_inserted_at"` - TriggerChildIndex pgtype.Int8 `json:"trigger_child_index"` - TriggerChildKey pgtype.Text `json:"trigger_child_key"` - TriggerExistingTaskID pgtype.Int8 `json:"trigger_existing_task_id"` - TriggerExistingTaskInsertedAt pgtype.Timestamptz `json:"trigger_existing_task_inserted_at"` - TriggerPriority pgtype.Int4 `json:"trigger_priority"` + ID int64 `json:"id"` + TenantID uuid.UUID `json:"tenant_id"` + Kind V1MatchKind `json:"kind"` + IsSatisfied bool `json:"is_satisfied"` + ExistingData []byte `json:"existing_data"` + SignalTaskID pgtype.Int8 `json:"signal_task_id"` + SignalTaskInsertedAt pgtype.Timestamptz `json:"signal_task_inserted_at"` + SignalExternalID *uuid.UUID `json:"signal_external_id"` + SignalKey pgtype.Text `json:"signal_key"` + TriggerDagID pgtype.Int8 `json:"trigger_dag_id"` + TriggerDagInsertedAt pgtype.Timestamptz `json:"trigger_dag_inserted_at"` + TriggerStepID *uuid.UUID `json:"trigger_step_id"` + TriggerStepIndex pgtype.Int8 `json:"trigger_step_index"` + TriggerExternalID *uuid.UUID `json:"trigger_external_id"` + TriggerWorkflowRunID *uuid.UUID `json:"trigger_workflow_run_id"` + TriggerParentTaskExternalID *uuid.UUID `json:"trigger_parent_task_external_id"` + TriggerParentTaskID pgtype.Int8 `json:"trigger_parent_task_id"` + TriggerParentTaskInsertedAt pgtype.Timestamptz `json:"trigger_parent_task_inserted_at"` + TriggerChildIndex pgtype.Int8 `json:"trigger_child_index"` + TriggerChildKey pgtype.Text `json:"trigger_child_key"` + TriggerExistingTaskID pgtype.Int8 `json:"trigger_existing_task_id"` + TriggerExistingTaskInsertedAt pgtype.Timestamptz `json:"trigger_existing_task_inserted_at"` + TriggerPriority pgtype.Int4 `json:"trigger_priority"` + DurableEventLogCallbackDurableTaskExternalID *uuid.UUID `json:"durable_event_log_callback_durable_task_external_id"` + DurableEventLogCallbackDurableTaskID pgtype.Int8 `json:"durable_event_log_callback_durable_task_id"` + DurableEventLogCallbackDurableTaskInsertedAt pgtype.Timestamptz `json:"durable_event_log_callback_durable_task_inserted_at"` + DurableEventLogCallbackNodeID pgtype.Int8 `json:"durable_event_log_callback_node_id"` } type V1MatchCondition struct { @@ -3681,6 +3646,7 @@ type Worker struct { Os pgtype.Text `json:"os"` RuntimeExtra pgtype.Text `json:"runtimeExtra"` SdkVersion pgtype.Text `json:"sdkVersion"` + DurableTaskDispatcherId *uuid.UUID `json:"durableTaskDispatcherId"` } type WorkerAssignEvent struct { diff --git a/pkg/repository/sqlcv1/workers.sql b/pkg/repository/sqlcv1/workers.sql index dac663170..afff66f7d 100644 --- a/pkg/repository/sqlcv1/workers.sql +++ b/pkg/repository/sqlcv1/workers.sql @@ -385,3 +385,33 @@ VALUES ( @workerId::uuid ) ON CONFLICT DO NOTHING; + +-- name: UpdateWorkerDurableTaskDispatcherId :one +UPDATE "Worker" +SET + "durableTaskDispatcherId" = @dispatcherId::UUID, + "updatedAt" = CURRENT_TIMESTAMP +WHERE + "id" = @workerId::uuid + AND "tenantId" = @tenantId::uuid +RETURNING *; + +-- name: ListDurableTaskDispatcherIdsForTasks :many +WITH tasks AS ( + SELECT + UNNEST(@taskIds::BIGINT[]) AS task_id, + UNNEST(@taskInsertedAts::TIMESTAMPTZ[]) AS task_inserted_at +) + +SELECT + rt.*, + w."durableTaskDispatcherId" +FROM v1_task_runtime rt +JOIN "Worker" w ON rt.worker_id = w.id +WHERE + rt.tenant_id = @tenantId::uuid + AND (rt.task_id, rt.task_inserted_at) IN ( + SELECT task_id, task_inserted_at + FROM tasks + ) +; diff --git a/pkg/repository/sqlcv1/workers.sql.go b/pkg/repository/sqlcv1/workers.sql.go index 320401d7d..bd332b1de 100644 --- a/pkg/repository/sqlcv1/workers.sql.go +++ b/pkg/repository/sqlcv1/workers.sql.go @@ -43,7 +43,7 @@ INSERT INTO "Worker" ( $9::text, $10::text, $11::text -) RETURNING id, "createdAt", "updatedAt", "deletedAt", "tenantId", "lastHeartbeatAt", name, "dispatcherId", "maxRuns", "isActive", "lastListenerEstablished", "isPaused", type, "webhookId", language, "languageVersion", os, "runtimeExtra", "sdkVersion" +) RETURNING id, "createdAt", "updatedAt", "deletedAt", "tenantId", "lastHeartbeatAt", name, "dispatcherId", "maxRuns", "isActive", "lastListenerEstablished", "isPaused", type, "webhookId", language, "languageVersion", os, "runtimeExtra", "sdkVersion", "durableTaskDispatcherId" ` type CreateWorkerParams struct { @@ -95,6 +95,7 @@ func (q *Queries) CreateWorker(ctx context.Context, db DBTX, arg CreateWorkerPar &i.Os, &i.RuntimeExtra, &i.SdkVersion, + &i.DurableTaskDispatcherId, ) return &i, err } @@ -149,7 +150,7 @@ DELETE FROM "Worker" WHERE "id" = $1::uuid -RETURNING id, "createdAt", "updatedAt", "deletedAt", "tenantId", "lastHeartbeatAt", name, "dispatcherId", "maxRuns", "isActive", "lastListenerEstablished", "isPaused", type, "webhookId", language, "languageVersion", os, "runtimeExtra", "sdkVersion" +RETURNING id, "createdAt", "updatedAt", "deletedAt", "tenantId", "lastHeartbeatAt", name, "dispatcherId", "maxRuns", "isActive", "lastListenerEstablished", "isPaused", type, "webhookId", language, "languageVersion", os, "runtimeExtra", "sdkVersion", "durableTaskDispatcherId" ` func (q *Queries) DeleteWorker(ctx context.Context, db DBTX, id uuid.UUID) (*Worker, error) { @@ -175,13 +176,14 @@ func (q *Queries) DeleteWorker(ctx context.Context, db DBTX, id uuid.UUID) (*Wor &i.Os, &i.RuntimeExtra, &i.SdkVersion, + &i.DurableTaskDispatcherId, ) return &i, err } const getActiveWorkerById = `-- name: GetActiveWorkerById :one SELECT - w.id, w."createdAt", w."updatedAt", w."deletedAt", w."tenantId", w."lastHeartbeatAt", w.name, w."dispatcherId", w."maxRuns", w."isActive", w."lastListenerEstablished", w."isPaused", w.type, w."webhookId", w.language, w."languageVersion", w.os, w."runtimeExtra", w."sdkVersion", + w.id, w."createdAt", w."updatedAt", w."deletedAt", w."tenantId", w."lastHeartbeatAt", w.name, w."dispatcherId", w."maxRuns", w."isActive", w."lastListenerEstablished", w."isPaused", w.type, w."webhookId", w.language, w."languageVersion", w.os, w."runtimeExtra", w."sdkVersion", w."durableTaskDispatcherId", ww."url" AS "webhookUrl", w."maxRuns" - ( SELECT COUNT(*) @@ -237,6 +239,7 @@ func (q *Queries) GetActiveWorkerById(ctx context.Context, db DBTX, arg GetActiv &i.Worker.Os, &i.Worker.RuntimeExtra, &i.Worker.SdkVersion, + &i.Worker.DurableTaskDispatcherId, &i.WebhookUrl, &i.RemainingSlots, ) @@ -291,7 +294,7 @@ func (q *Queries) GetWorkerActionsByWorkerId(ctx context.Context, db DBTX, arg G const getWorkerById = `-- name: GetWorkerById :one SELECT - w.id, w."createdAt", w."updatedAt", w."deletedAt", w."tenantId", w."lastHeartbeatAt", w.name, w."dispatcherId", w."maxRuns", w."isActive", w."lastListenerEstablished", w."isPaused", w.type, w."webhookId", w.language, w."languageVersion", w.os, w."runtimeExtra", w."sdkVersion", + w.id, w."createdAt", w."updatedAt", w."deletedAt", w."tenantId", w."lastHeartbeatAt", w.name, w."dispatcherId", w."maxRuns", w."isActive", w."lastListenerEstablished", w."isPaused", w.type, w."webhookId", w.language, w."languageVersion", w.os, w."runtimeExtra", w."sdkVersion", w."durableTaskDispatcherId", ww."url" AS "webhookUrl", w."maxRuns" - ( SELECT COUNT(*) @@ -337,6 +340,7 @@ func (q *Queries) GetWorkerById(ctx context.Context, db DBTX, id uuid.UUID) (*Ge &i.Worker.Os, &i.Worker.RuntimeExtra, &i.Worker.SdkVersion, + &i.Worker.DurableTaskDispatcherId, &i.WebhookUrl, &i.RemainingSlots, ) @@ -611,6 +615,70 @@ func (q *Queries) ListDispatcherIdsForWorkers(ctx context.Context, db DBTX, arg return items, nil } +const listDurableTaskDispatcherIdsForTasks = `-- name: ListDurableTaskDispatcherIdsForTasks :many +WITH tasks AS ( + SELECT + UNNEST($2::BIGINT[]) AS task_id, + UNNEST($3::TIMESTAMPTZ[]) AS task_inserted_at +) + +SELECT + rt.task_id, rt.task_inserted_at, rt.retry_count, rt.worker_id, rt.tenant_id, rt.timeout_at, + w."durableTaskDispatcherId" +FROM v1_task_runtime rt +JOIN "Worker" w ON rt.worker_id = w.id +WHERE + rt.tenant_id = $1::uuid + AND (rt.task_id, rt.task_inserted_at) IN ( + SELECT task_id, task_inserted_at + FROM tasks + ) +` + +type ListDurableTaskDispatcherIdsForTasksParams struct { + Tenantid uuid.UUID `json:"tenantid"` + Taskids []int64 `json:"taskids"` + Taskinsertedats []pgtype.Timestamptz `json:"taskinsertedats"` +} + +type ListDurableTaskDispatcherIdsForTasksRow struct { + TaskID int64 `json:"task_id"` + TaskInsertedAt pgtype.Timestamptz `json:"task_inserted_at"` + RetryCount int32 `json:"retry_count"` + WorkerID *uuid.UUID `json:"worker_id"` + TenantID uuid.UUID `json:"tenant_id"` + TimeoutAt pgtype.Timestamp `json:"timeout_at"` + DurableTaskDispatcherId *uuid.UUID `json:"durableTaskDispatcherId"` +} + +func (q *Queries) ListDurableTaskDispatcherIdsForTasks(ctx context.Context, db DBTX, arg ListDurableTaskDispatcherIdsForTasksParams) ([]*ListDurableTaskDispatcherIdsForTasksRow, error) { + rows, err := db.Query(ctx, listDurableTaskDispatcherIdsForTasks, arg.Tenantid, arg.Taskids, arg.Taskinsertedats) + if err != nil { + return nil, err + } + defer rows.Close() + var items []*ListDurableTaskDispatcherIdsForTasksRow + for rows.Next() { + var i ListDurableTaskDispatcherIdsForTasksRow + if err := rows.Scan( + &i.TaskID, + &i.TaskInsertedAt, + &i.RetryCount, + &i.WorkerID, + &i.TenantID, + &i.TimeoutAt, + &i.DurableTaskDispatcherId, + ); err != nil { + return nil, err + } + items = append(items, &i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const listManyWorkerLabels = `-- name: ListManyWorkerLabels :many SELECT "id", @@ -878,7 +946,7 @@ func (q *Queries) ListWorkerLabels(ctx context.Context, db DBTX, workerid uuid.U const listWorkersWithSlotCount = `-- name: ListWorkersWithSlotCount :many SELECT - workers.id, workers."createdAt", workers."updatedAt", workers."deletedAt", workers."tenantId", workers."lastHeartbeatAt", workers.name, workers."dispatcherId", workers."maxRuns", workers."isActive", workers."lastListenerEstablished", workers."isPaused", workers.type, workers."webhookId", workers.language, workers."languageVersion", workers.os, workers."runtimeExtra", workers."sdkVersion", + workers.id, workers."createdAt", workers."updatedAt", workers."deletedAt", workers."tenantId", workers."lastHeartbeatAt", workers.name, workers."dispatcherId", workers."maxRuns", workers."isActive", workers."lastListenerEstablished", workers."isPaused", workers.type, workers."webhookId", workers.language, workers."languageVersion", workers.os, workers."runtimeExtra", workers."sdkVersion", workers."durableTaskDispatcherId", ww."url" AS "webhookUrl", ww."id" AS "webhookId", workers."maxRuns" - ( @@ -968,6 +1036,7 @@ func (q *Queries) ListWorkersWithSlotCount(ctx context.Context, db DBTX, arg Lis &i.Worker.Os, &i.Worker.RuntimeExtra, &i.Worker.SdkVersion, + &i.Worker.DurableTaskDispatcherId, &i.WebhookUrl, &i.WebhookId, &i.RemainingSlots, @@ -994,7 +1063,7 @@ SET "isPaused" = coalesce($5::boolean, "isPaused") WHERE "id" = $6::uuid -RETURNING id, "createdAt", "updatedAt", "deletedAt", "tenantId", "lastHeartbeatAt", name, "dispatcherId", "maxRuns", "isActive", "lastListenerEstablished", "isPaused", type, "webhookId", language, "languageVersion", os, "runtimeExtra", "sdkVersion" +RETURNING id, "createdAt", "updatedAt", "deletedAt", "tenantId", "lastHeartbeatAt", name, "dispatcherId", "maxRuns", "isActive", "lastListenerEstablished", "isPaused", type, "webhookId", language, "languageVersion", os, "runtimeExtra", "sdkVersion", "durableTaskDispatcherId" ` type UpdateWorkerParams struct { @@ -1036,6 +1105,7 @@ func (q *Queries) UpdateWorker(ctx context.Context, db DBTX, arg UpdateWorkerPar &i.Os, &i.RuntimeExtra, &i.SdkVersion, + &i.DurableTaskDispatcherId, ) return &i, err } @@ -1051,7 +1121,7 @@ WHERE "lastListenerEstablished" IS NULL OR "lastListenerEstablished" <= $2::timestamp ) -RETURNING id, "createdAt", "updatedAt", "deletedAt", "tenantId", "lastHeartbeatAt", name, "dispatcherId", "maxRuns", "isActive", "lastListenerEstablished", "isPaused", type, "webhookId", language, "languageVersion", os, "runtimeExtra", "sdkVersion" +RETURNING id, "createdAt", "updatedAt", "deletedAt", "tenantId", "lastHeartbeatAt", name, "dispatcherId", "maxRuns", "isActive", "lastListenerEstablished", "isPaused", type, "webhookId", language, "languageVersion", os, "runtimeExtra", "sdkVersion", "durableTaskDispatcherId" ` type UpdateWorkerActiveStatusParams struct { @@ -1083,6 +1153,52 @@ func (q *Queries) UpdateWorkerActiveStatus(ctx context.Context, db DBTX, arg Upd &i.Os, &i.RuntimeExtra, &i.SdkVersion, + &i.DurableTaskDispatcherId, + ) + return &i, err +} + +const updateWorkerDurableTaskDispatcherId = `-- name: UpdateWorkerDurableTaskDispatcherId :one +UPDATE "Worker" +SET + "durableTaskDispatcherId" = $1::UUID, + "updatedAt" = CURRENT_TIMESTAMP +WHERE + "id" = $2::uuid + AND "tenantId" = $3::uuid +RETURNING id, "createdAt", "updatedAt", "deletedAt", "tenantId", "lastHeartbeatAt", name, "dispatcherId", "maxRuns", "isActive", "lastListenerEstablished", "isPaused", type, "webhookId", language, "languageVersion", os, "runtimeExtra", "sdkVersion", "durableTaskDispatcherId" +` + +type UpdateWorkerDurableTaskDispatcherIdParams struct { + Dispatcherid uuid.UUID `json:"dispatcherid"` + Workerid uuid.UUID `json:"workerid"` + Tenantid uuid.UUID `json:"tenantid"` +} + +func (q *Queries) UpdateWorkerDurableTaskDispatcherId(ctx context.Context, db DBTX, arg UpdateWorkerDurableTaskDispatcherIdParams) (*Worker, error) { + row := db.QueryRow(ctx, updateWorkerDurableTaskDispatcherId, arg.Dispatcherid, arg.Workerid, arg.Tenantid) + var i Worker + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.TenantId, + &i.LastHeartbeatAt, + &i.Name, + &i.DispatcherId, + &i.MaxRuns, + &i.IsActive, + &i.LastListenerEstablished, + &i.IsPaused, + &i.Type, + &i.WebhookId, + &i.Language, + &i.LanguageVersion, + &i.Os, + &i.RuntimeExtra, + &i.SdkVersion, + &i.DurableTaskDispatcherId, ) return &i, err } @@ -1095,7 +1211,7 @@ SET "lastHeartbeatAt" = $1::timestamp WHERE "id" = $2::uuid -RETURNING id, "createdAt", "updatedAt", "deletedAt", "tenantId", "lastHeartbeatAt", name, "dispatcherId", "maxRuns", "isActive", "lastListenerEstablished", "isPaused", type, "webhookId", language, "languageVersion", os, "runtimeExtra", "sdkVersion" +RETURNING id, "createdAt", "updatedAt", "deletedAt", "tenantId", "lastHeartbeatAt", name, "dispatcherId", "maxRuns", "isActive", "lastListenerEstablished", "isPaused", type, "webhookId", language, "languageVersion", os, "runtimeExtra", "sdkVersion", "durableTaskDispatcherId" ` type UpdateWorkerHeartbeatParams struct { @@ -1126,6 +1242,7 @@ func (q *Queries) UpdateWorkerHeartbeat(ctx context.Context, db DBTX, arg Update &i.Os, &i.RuntimeExtra, &i.SdkVersion, + &i.DurableTaskDispatcherId, ) return &i, err } diff --git a/pkg/repository/trigger.go b/pkg/repository/trigger.go index d6df4cafc..248707502 100644 --- a/pkg/repository/trigger.go +++ b/pkg/repository/trigger.go @@ -11,11 +11,14 @@ import ( "github.com/google/uuid" "github.com/jackc/pgx/v5/pgtype" "github.com/rs/zerolog" + "go.opentelemetry.io/otel/attribute" "github.com/hatchet-dev/hatchet/internal/cel" + v1contracts "github.com/hatchet-dev/hatchet/internal/services/shared/proto/v1" "github.com/hatchet-dev/hatchet/pkg/constants" "github.com/hatchet-dev/hatchet/pkg/repository/sqlchelpers" "github.com/hatchet-dev/hatchet/pkg/repository/sqlcv1" + "github.com/hatchet-dev/hatchet/pkg/telemetry" ) type EventTriggerOpts struct { @@ -99,6 +102,8 @@ type TriggerRepository interface { PopulateExternalIdsForWorkflow(ctx context.Context, tenantId uuid.UUID, opts []*WorkflowNameTriggerOpts) error PreflightVerifyWorkflowNameOpts(ctx context.Context, tenantId uuid.UUID, opts []*WorkflowNameTriggerOpts) error + + NewTriggerTaskData(ctx context.Context, tenantId uuid.UUID, req *v1contracts.TriggerWorkflowRequest, parentTask *sqlcv1.FlattenExternalIdsRow) (*TriggerTaskData, error) } type TriggerRepositoryImpl struct { @@ -325,14 +330,36 @@ func getEventExternalIdToRuns(opts []EventTriggerOpts, externalIdToEventIdAndFil return eventExternalIdToRuns } -func (r *TriggerRepositoryImpl) TriggerFromWorkflowNames(ctx context.Context, tenantId uuid.UUID, opts []*WorkflowNameTriggerOpts) ([]*V1TaskWithPayload, []*DAGWithData, error) { - triggerOpts, err := r.prepareTriggerFromWorkflowNames(ctx, r.pool, tenantId, opts) +func (s *sharedRepository) triggerFromWorkflowNames(ctx context.Context, tx *OptimisticTx, tenantId uuid.UUID, opts []*WorkflowNameTriggerOpts) ([]*V1TaskWithPayload, []*DAGWithData, error) { + triggerOpts, err := s.prepareTriggerFromWorkflowNames(ctx, tx.tx, tenantId, opts) if err != nil { return nil, nil, fmt.Errorf("failed to prepare trigger from workflow names: %w", err) } - return r.triggerWorkflows(ctx, nil, tenantId, triggerOpts, nil) + return s.triggerWorkflows(ctx, tx, tenantId, triggerOpts, nil) +} + +func (r *TriggerRepositoryImpl) TriggerFromWorkflowNames(ctx context.Context, tenantId uuid.UUID, opts []*WorkflowNameTriggerOpts) ([]*V1TaskWithPayload, []*DAGWithData, error) { + tx, err := r.PrepareOptimisticTx(ctx) + + if err != nil { + return nil, nil, fmt.Errorf("failed to prepare tx: %w", err) + } + + defer tx.Rollback() + + tasks, dags, err := r.triggerFromWorkflowNames(ctx, tx, tenantId, opts) + + if err != nil { + return nil, nil, err + } + + if err := tx.Commit(ctx); err != nil { + return nil, nil, err + } + + return tasks, dags, nil } type ErrNamesNotFound struct { @@ -2166,3 +2193,76 @@ func (r *sharedRepository) prepareTriggerFromWorkflowNames(ctx context.Context, return triggerOpts, nil } + +type TriggerOptInvalidArgumentError struct { + Err error +} + +func (r *TriggerOptInvalidArgumentError) Error() string { + return fmt.Sprintf("err %v", r.Err) +} + +func (r *sharedRepository) NewTriggerTaskData( + ctx context.Context, + tenantId uuid.UUID, + req *v1contracts.TriggerWorkflowRequest, + parentTask *sqlcv1.FlattenExternalIdsRow, +) (*TriggerTaskData, error) { + ctx, span := telemetry.NewSpan(ctx, "sharedRepository.NewTriggerTaskData") + defer span.End() + + span.SetAttributes( + attribute.String("sharedRepository.NewTriggerTaskData.workflow_name", req.Name), + attribute.Int("sharedRepository.NewTriggerTaskData.payload_size", len(req.Input)), + attribute.Bool("sharedRepository.NewTriggerTaskData.is_child_workflow", req.ParentTaskRunExternalId != nil), + ) + + additionalMeta := "" + + if req.AdditionalMetadata != nil { + additionalMeta = *req.AdditionalMetadata + } + + var desiredWorkerId *uuid.UUID + if req.DesiredWorkerId != nil { + if *req.DesiredWorkerId != "" { + workerId, err := uuid.Parse(*req.DesiredWorkerId) + if err != nil { + return nil, &TriggerOptInvalidArgumentError{ + Err: fmt.Errorf("desiredWorkerId must be a valid UUID: %w", err), + } + } + desiredWorkerId = &workerId + } + } + + t := &TriggerTaskData{ + WorkflowName: req.Name, + Data: []byte(req.Input), + AdditionalMetadata: []byte(additionalMeta), + DesiredWorkerId: desiredWorkerId, + Priority: req.Priority, + } + + if req.Priority != nil { + if *req.Priority < 1 || *req.Priority > 3 { + return nil, &TriggerOptInvalidArgumentError{ + Err: fmt.Errorf("priority must be between 1 and 3, got %d", *req.Priority), + } + } + t.Priority = req.Priority + } + + if parentTask != nil { + parentExternalId := parentTask.ExternalID + childIndex := int64(*req.ChildIndex) + + t.ParentExternalId = &parentExternalId + t.ParentTaskId = &parentTask.ID + t.ParentTaskInsertedAt = &parentTask.InsertedAt.Time + t.ChildIndex = &childIndex + t.ChildKey = req.ChildKey + } + + return t, nil +} diff --git a/pkg/repository/worker.go b/pkg/repository/worker.go index 5e27ba520..c55e5404f 100644 --- a/pkg/repository/worker.go +++ b/pkg/repository/worker.go @@ -113,6 +113,10 @@ type WorkerRepository interface { DeleteOldWorkers(ctx context.Context, tenantId uuid.UUID, lastHeartbeatBefore time.Time) (bool, error) GetDispatcherIdsForWorkers(ctx context.Context, tenantId uuid.UUID, workerIds []uuid.UUID) (map[uuid.UUID]uuid.UUID, map[uuid.UUID]struct{}, error) + + UpdateWorkerDurableTaskDispatcherId(ctx context.Context, tenantId uuid.UUID, workerId uuid.UUID, dispatcherId uuid.UUID) error + + GetDurableDispatcherIdsForTasks(ctx context.Context, tenantId uuid.UUID, idInsertedAtTuples []IdInsertedAt) (map[IdInsertedAt]uuid.UUID, error) } type workerRepository struct { @@ -676,3 +680,48 @@ func (w *workerRepository) GetDispatcherIdsForWorkers(ctx context.Context, tenan return workerIdToDispatcherId, workerIdsWithoutDispatchers, nil } + +func (w *workerRepository) UpdateWorkerDurableTaskDispatcherId(ctx context.Context, tenantId uuid.UUID, workerId uuid.UUID, dispatcherId uuid.UUID) error { + _, err := w.queries.UpdateWorkerDurableTaskDispatcherId(ctx, w.pool, sqlcv1.UpdateWorkerDurableTaskDispatcherIdParams{ + Workerid: workerId, + Dispatcherid: dispatcherId, + Tenantid: tenantId, + }) + + return err +} + +func (w *workerRepository) GetDurableDispatcherIdsForTasks(ctx context.Context, tenantId uuid.UUID, idInsertedAtTuples []IdInsertedAt) (map[IdInsertedAt]uuid.UUID, error) { + taskIds := make([]int64, len(idInsertedAtTuples)) + taskInsertedAts := make([]pgtype.Timestamptz, len(idInsertedAtTuples)) + + for i, tuple := range idInsertedAtTuples { + taskIds[i] = tuple.ID + taskInsertedAts[i] = tuple.InsertedAt + } + + rows, err := w.queries.ListDurableTaskDispatcherIdsForTasks(ctx, w.pool, sqlcv1.ListDurableTaskDispatcherIdsForTasksParams{ + Tenantid: tenantId, + Taskids: taskIds, + Taskinsertedats: taskInsertedAts, + }) + + if err != nil { + return nil, fmt.Errorf("could not get durable dispatcher ids for tasks: %w", err) + } + + taskIdToDispatcherId := make(map[IdInsertedAt]uuid.UUID) + + for _, row := range rows { + if row.DurableTaskDispatcherId == nil { + continue + } + + taskIdToDispatcherId[IdInsertedAt{ + ID: row.TaskID, + InsertedAt: row.TaskInsertedAt, + }] = *row.DurableTaskDispatcherId + } + + return taskIdToDispatcherId, nil +} diff --git a/pkg/v1/workflow/declaration.go b/pkg/v1/workflow/declaration.go index ee5cfcd2e..f62f4a6b8 100644 --- a/pkg/v1/workflow/declaration.go +++ b/pkg/v1/workflow/declaration.go @@ -9,7 +9,7 @@ import ( "strings" "time" - admincontracts "github.com/hatchet-dev/hatchet/internal/services/admin/contracts" + v1contracts "github.com/hatchet-dev/hatchet/internal/services/shared/proto/v1" v0Client "github.com/hatchet-dev/hatchet/pkg/client" "github.com/hatchet-dev/hatchet/pkg/client/create" "github.com/hatchet-dev/hatchet/pkg/client/rest" @@ -713,7 +713,7 @@ func (w *workflowDeclarationImpl[I, O]) Cron(ctx context.Context, name string, c Input: inputMap, } - runOpts := &admincontracts.TriggerWorkflowRequest{} + runOpts := &v1contracts.TriggerWorkflowRequest{} for _, opt := range opts { opt(runOpts) @@ -755,7 +755,7 @@ func (w *workflowDeclarationImpl[I, O]) Schedule(ctx context.Context, triggerAt Input: inputMap, } - runOpts := &admincontracts.TriggerWorkflowRequest{} + runOpts := &v1contracts.TriggerWorkflowRequest{} for _, opt := range opts { opt(runOpts) @@ -1049,7 +1049,7 @@ func RunChildWorkflow[I any, O any]( spawnOpts := &worker.SpawnWorkflowOpts{} - runOpts := &admincontracts.TriggerWorkflowRequest{} + runOpts := &v1contracts.TriggerWorkflowRequest{} for _, opt := range opts { opt(runOpts) diff --git a/sdks/go/internal/declaration.go b/sdks/go/internal/declaration.go index 23398865e..905c751cc 100644 --- a/sdks/go/internal/declaration.go +++ b/sdks/go/internal/declaration.go @@ -9,7 +9,7 @@ import ( "strings" "time" - admincontracts "github.com/hatchet-dev/hatchet/internal/services/admin/contracts" + v1contracts "github.com/hatchet-dev/hatchet/internal/services/shared/proto/v1" v0Client "github.com/hatchet-dev/hatchet/pkg/client" "github.com/hatchet-dev/hatchet/pkg/client/create" "github.com/hatchet-dev/hatchet/pkg/client/rest" @@ -514,7 +514,7 @@ func (w *workflowDeclarationImpl[I, O]) Cron(ctx context.Context, name string, c Input: inputMap, } - runOpts := &admincontracts.TriggerWorkflowRequest{} + runOpts := &v1contracts.TriggerWorkflowRequest{} for _, opt := range opts { opt(runOpts) @@ -554,7 +554,7 @@ func (w *workflowDeclarationImpl[I, O]) Schedule(ctx context.Context, triggerAt Input: inputMap, } - runOpts := &admincontracts.TriggerWorkflowRequest{} + runOpts := &v1contracts.TriggerWorkflowRequest{} for _, opt := range opts { opt(runOpts) diff --git a/sdks/python/apply_patches.py b/sdks/python/apply_patches.py index e2b8efaf6..ef9dc26b7 100644 --- a/sdks/python/apply_patches.py +++ b/sdks/python/apply_patches.py @@ -52,7 +52,11 @@ def atomically_patch_file( def patch_contract_import_paths(content: str) -> str: - return apply_patch(content, r"\bfrom v1\b", "from hatchet_sdk.contracts.v1") + content = apply_patch(content, r"\bfrom v1\b", "from hatchet_sdk.contracts.v1") + content = apply_patch( + content, r"\bfrom workflows\b", "from hatchet_sdk.contracts.workflows" + ) + return content def patch_grpc_dispatcher_import(content: str) -> str: @@ -374,7 +378,6 @@ if __name__ == "__main__": patch_contract_import_paths, patch_grpc_dispatcher_import, patch_grpc_events_import, - patch_grpc_workflows_import, patch_grpc_init_signature, ] diff --git a/sdks/python/examples/dependency_injection/test_dependency_injection.py b/sdks/python/examples/dependency_injection/test_dependency_injection.py index 8813977d2..b81547f98 100644 --- a/sdks/python/examples/dependency_injection/test_dependency_injection.py +++ b/sdks/python/examples/dependency_injection/test_dependency_injection.py @@ -8,12 +8,9 @@ from examples.dependency_injection.worker import ( SYNC_CM_DEPENDENCY_VALUE, SYNC_DEPENDENCY_VALUE, Output, - async_dep, async_task_with_dependencies, di_workflow, durable_async_task_with_dependencies, - durable_sync_task_with_dependencies, - sync_dep, sync_task_with_dependencies, ) from hatchet_sdk import EmptyModel @@ -26,7 +23,6 @@ from hatchet_sdk.runnables.workflow import Standalone async_task_with_dependencies, sync_task_with_dependencies, durable_async_task_with_dependencies, - durable_sync_task_with_dependencies, ], ) @pytest.mark.asyncio(loop_scope="session") @@ -50,7 +46,7 @@ async def test_di_standalones( async def test_di_workflows() -> None: result = await di_workflow.aio_run() - assert len(result) == 4 + assert len(result) == 3 for output in result.values(): parsed = Output.model_validate(output) diff --git a/sdks/python/examples/dependency_injection/worker.py b/sdks/python/examples/dependency_injection/worker.py index 7ddbcf430..6ebdbaf36 100644 --- a/sdks/python/examples/dependency_injection/worker.py +++ b/sdks/python/examples/dependency_injection/worker.py @@ -155,27 +155,6 @@ async def durable_async_task_with_dependencies( ) -@hatchet.durable_task() -def durable_sync_task_with_dependencies( - _i: EmptyModel, - ctx: DurableContext, - async_dep: Annotated[str, Depends(async_dep)], - sync_dep: Annotated[str, Depends(sync_dep)], - async_cm_dep: Annotated[str, Depends(async_cm_dep)], - sync_cm_dep: Annotated[str, Depends(sync_cm_dep)], - chained_dep: Annotated[str, Depends(chained_dep)], - chained_async_dep: Annotated[str, Depends(chained_async_dep)], -) -> Output: - return Output( - sync_dep=sync_dep, - async_dep=async_dep, - async_cm_dep=async_cm_dep, - sync_cm_dep=sync_cm_dep, - chained_dep=chained_dep, - chained_async_dep=chained_async_dep, - ) - - di_workflow = hatchet.workflow( name="dependency-injection-workflow", ) @@ -244,27 +223,6 @@ async def wf_durable_async_task_with_dependencies( ) -@di_workflow.durable_task() -def wf_durable_sync_task_with_dependencies( - _i: EmptyModel, - ctx: DurableContext, - async_dep: Annotated[str, Depends(async_dep)], - sync_dep: Annotated[str, Depends(sync_dep)], - async_cm_dep: Annotated[str, Depends(async_cm_dep)], - sync_cm_dep: Annotated[str, Depends(sync_cm_dep)], - chained_dep: Annotated[str, Depends(chained_dep)], - chained_async_dep: Annotated[str, Depends(chained_async_dep)], -) -> Output: - return Output( - sync_dep=sync_dep, - async_dep=async_dep, - async_cm_dep=async_cm_dep, - sync_cm_dep=sync_cm_dep, - chained_dep=chained_dep, - chained_async_dep=chained_async_dep, - ) - - def main() -> None: worker = hatchet.worker( "dependency-injection-worker", @@ -272,7 +230,6 @@ def main() -> None: async_task_with_dependencies, sync_task_with_dependencies, durable_async_task_with_dependencies, - durable_sync_task_with_dependencies, di_workflow, ], ) diff --git a/sdks/python/examples/durable/test_durable.py b/sdks/python/examples/durable/test_durable.py index 562bb169e..06e9bf721 100644 --- a/sdks/python/examples/durable/test_durable.py +++ b/sdks/python/examples/durable/test_durable.py @@ -1,12 +1,16 @@ import asyncio +import time import pytest from examples.durable.worker import ( EVENT_KEY, SLEEP_TIME, + durable_sleep_event_spawn, + durable_with_spawn, durable_workflow, wait_for_sleep_twice, + durable_spawn_dag, ) from hatchet_sdk import Hatchet @@ -72,3 +76,66 @@ async def test_durable_sleep_cancel_replay(hatchet: Hatchet) -> None: """We've already slept for a little bit by the time the task is cancelled""" assert second_sleep_result["runtime"] <= SLEEP_TIME + + +@pytest.mark.asyncio(loop_scope="session") +async def test_durable_child_spawn() -> None: + result = await durable_with_spawn.aio_run() + + assert result["child_output"] == {"message": "hello from child"} + + +@pytest.mark.asyncio(loop_scope="session") +async def test_durable_sleep_event_spawn_replay(hatchet: Hatchet) -> None: + start = time.time() + ref = durable_sleep_event_spawn.run_no_wait() + + await asyncio.sleep(SLEEP_TIME + 5) + hatchet.event.push(EVENT_KEY, {"test": "test"}) + + result = await ref.aio_result() + first_elapsed = time.time() - start + + assert result["child_output"] == {"message": "hello from child"} + assert first_elapsed >= SLEEP_TIME + assert result["runtime"] >= SLEEP_TIME + + replay_start = time.time() + await hatchet.runs.aio_replay(ref.workflow_run_id) + replayed_result = await ref.aio_result() + replay_elapsed = time.time() - replay_start + + assert replayed_result["child_output"] == {"message": "hello from child"} + assert replay_elapsed < SLEEP_TIME + + +@pytest.mark.asyncio(loop_scope="session") +async def test_durable_completed_replay(hatchet: Hatchet) -> None: + ref = wait_for_sleep_twice.run_no_wait() + + start = time.time() + first_result = await ref.aio_result() + elapsed = time.time() - start + + assert first_result["runtime"] >= SLEEP_TIME + assert elapsed >= SLEEP_TIME + + start = time.time() + await hatchet.runs.aio_replay(ref.workflow_run_id) + replayed_result = await ref.aio_result() + elapsed = time.time() - start + + assert replayed_result["runtime"] >= 0 + assert replayed_result["runtime"] <= SLEEP_TIME + + assert elapsed < SLEEP_TIME + + +@pytest.mark.asyncio(loop_scope="session") +async def test_durable_spawn_dag() -> None: + result = await durable_spawn_dag.aio_run() + + assert result["sleep_duration"] >= 1 + assert result["sleep_duration"] <= 2 + assert result["spawn_duration"] >= 5 + assert result["spawn_duration"] <= 10 diff --git a/sdks/python/examples/durable/worker.py b/sdks/python/examples/durable/worker.py index 13fc2083a..1b9542db8 100644 --- a/sdks/python/examples/durable/worker.py +++ b/sdks/python/examples/durable/worker.py @@ -1,6 +1,7 @@ import asyncio import time from datetime import timedelta +from typing import Any from uuid import uuid4 from hatchet_sdk import ( @@ -15,6 +16,40 @@ from hatchet_sdk import ( hatchet = Hatchet(debug=True) + +dag_child_workflow = hatchet.workflow(name="dag-child-workflow") + + +@dag_child_workflow.task() +async def dag_child_1(input: EmptyModel, ctx: Context) -> dict[str, str]: + await asyncio.sleep(1) + return {"result": "child1"} + + +@dag_child_workflow.task() +async def dag_child_2(input: EmptyModel, ctx: Context) -> dict[str, str]: + await asyncio.sleep(5) + return {"result": "child2"} + + +@hatchet.durable_task() +async def durable_spawn_dag(input: EmptyModel, ctx: DurableContext) -> dict[str, Any]: + sleep_start = time.time() + sleep_result = await ctx.aio_sleep_for(timedelta(seconds=1)) + sleep_duration = time.time() - sleep_start + + spawn_start = time.time() + spawn_result = await dag_child_workflow.aio_run() + spawn_duration = time.time() - spawn_start + + return { + "sleep_duration": sleep_duration, + "sleep_result": sleep_result, + "spawn_duration": spawn_duration, + "spawn_result": spawn_result, + } + + # > Create a durable workflow durable_workflow = hatchet.workflow(name="DurableWorkflow") # !! @@ -145,10 +180,49 @@ async def wait_for_sleep_twice( return {"runtime": -1} +@hatchet.task() +def spawn_child_task(input: EmptyModel, ctx: Context) -> dict[str, str]: + return {"message": "hello from child"} + + +@hatchet.durable_task() +async def durable_with_spawn(input: EmptyModel, ctx: DurableContext) -> dict[str, Any]: + child_result = await spawn_child_task.aio_run() + return {"child_output": child_result} + + +@hatchet.durable_task() +async def durable_sleep_event_spawn( + input: EmptyModel, ctx: DurableContext +) -> dict[str, Any]: + start = time.time() + + await ctx.aio_sleep_for(timedelta(seconds=SLEEP_TIME)) + + await ctx.aio_wait_for( + "event", + UserEventCondition(event_key=EVENT_KEY, expression="true"), + ) + + child_result = await spawn_child_task.aio_run() + + return { + "runtime": int(time.time() - start), + "child_output": child_result, + } + + def main() -> None: worker = hatchet.worker( "durable-worker", - workflows=[durable_workflow, ephemeral_workflow, wait_for_sleep_twice], + workflows=[ + durable_workflow, + ephemeral_workflow, + wait_for_sleep_twice, + spawn_child_task, + durable_with_spawn, + durable_sleep_event_spawn, + ], ) worker.start() diff --git a/sdks/python/examples/simple/worker.py b/sdks/python/examples/simple/worker.py index 7986b21fd..97bdbb748 100644 --- a/sdks/python/examples/simple/worker.py +++ b/sdks/python/examples/simple/worker.py @@ -11,7 +11,8 @@ def simple(input: EmptyModel, ctx: Context) -> dict[str, str]: @hatchet.durable_task() -def simple_durable(input: EmptyModel, ctx: Context) -> dict[str, str]: +async def simple_durable(input: EmptyModel, ctx: Context) -> dict[str, str]: + # durable tasks should be async return {"result": "Hello, world!"} diff --git a/sdks/python/examples/worker.py b/sdks/python/examples/worker.py index 6265e26f1..fffd8337f 100644 --- a/sdks/python/examples/worker.py +++ b/sdks/python/examples/worker.py @@ -27,11 +27,18 @@ from examples.dependency_injection.worker import ( async_task_with_dependencies, di_workflow, durable_async_task_with_dependencies, - durable_sync_task_with_dependencies, sync_task_with_dependencies, ) from examples.dict_input.worker import say_hello_unsafely -from examples.durable.worker import durable_workflow, wait_for_sleep_twice +from examples.durable.worker import ( + durable_sleep_event_spawn, + durable_with_spawn, + durable_workflow, + spawn_child_task, + wait_for_sleep_twice, + dag_child_workflow, + durable_spawn_dag, +) from examples.events.worker import event_workflow from examples.fanout.worker import child_wf, parent_wf from examples.fanout_sync.worker import sync_fanout_child, sync_fanout_parent @@ -103,13 +110,17 @@ def main() -> None: return_exceptions_task, exception_parsing_workflow, wait_for_sleep_twice, + spawn_child_task, + durable_with_spawn, + durable_sleep_event_spawn, async_task_with_dependencies, sync_task_with_dependencies, durable_async_task_with_dependencies, - durable_sync_task_with_dependencies, say_hello, say_hello_unsafely, serde_workflow, + durable_spawn_dag, + dag_child_workflow, ], lifespan=lifespan, ) diff --git a/sdks/python/generate.sh b/sdks/python/generate.sh index f704e72c8..b4f04a74a 100755 --- a/sdks/python/generate.sh +++ b/sdks/python/generate.sh @@ -60,11 +60,13 @@ MIN_GRPCIO_VERSION=$(grep '^grpcio = ' pyproject.toml | cut -d'"' -f2 | tr -d '^ poetry add "grpcio@$MIN_GRPCIO_VERSION" "grpcio-tools@$MIN_GRPCIO_VERSION" + proto_paths=( "../../api-contracts/dispatcher dispatcher.proto" "../../api-contracts/events events.proto" - "../../api-contracts/workflows workflows.proto" + "../../api-contracts workflows/workflows.proto" "../../api-contracts v1/shared/condition.proto" + "../../api-contracts v1/shared/trigger.proto" "../../api-contracts v1/dispatcher.proto" "../../api-contracts v1/workflows.proto" ) diff --git a/sdks/python/hatchet_sdk/__init__.py b/sdks/python/hatchet_sdk/__init__.py index 32475544d..556f56331 100644 --- a/sdks/python/hatchet_sdk/__init__.py +++ b/sdks/python/hatchet_sdk/__init__.py @@ -4,9 +4,6 @@ from hatchet_sdk.clients.admin import ( TriggerWorkflowOptions, ) from hatchet_sdk.clients.events import PushEventOptions -from hatchet_sdk.clients.listeners.durable_event_listener import ( - RegisterDurableEventRequest, -) from hatchet_sdk.clients.listeners.run_event_listener import ( RunEventListener, StepRunEventType, @@ -149,7 +146,7 @@ from hatchet_sdk.conditions import ( from hatchet_sdk.config import ClientConfig, ClientTLSConfig, OpenTelemetryConfig from hatchet_sdk.context.context import Context, DurableContext from hatchet_sdk.context.worker_context import WorkerContext -from hatchet_sdk.contracts.workflows_pb2 import ( +from hatchet_sdk.contracts.workflows.workflows_pb2 import ( CreateWorkflowVersionOpts, RateLimitDuration, WorkerLabelComparator, diff --git a/sdks/python/hatchet_sdk/clients/admin.py b/sdks/python/hatchet_sdk/clients/admin.py index 4cb7dc49a..22c9803b6 100644 --- a/sdks/python/hatchet_sdk/clients/admin.py +++ b/sdks/python/hatchet_sdk/clients/admin.py @@ -15,10 +15,11 @@ from hatchet_sdk.clients.rest.models.v1_task_status import V1TaskStatus from hatchet_sdk.clients.rest.tenacity_utils import tenacity_retry from hatchet_sdk.config import ClientConfig from hatchet_sdk.connection import new_conn -from hatchet_sdk.contracts import workflows_pb2 as v0_workflow_protos from hatchet_sdk.contracts.v1 import workflows_pb2 as workflow_protos +from hatchet_sdk.contracts.v1.shared import trigger_pb2 as trigger_protos from hatchet_sdk.contracts.v1.workflows_pb2_grpc import AdminServiceStub -from hatchet_sdk.contracts.workflows_pb2_grpc import WorkflowServiceStub +from hatchet_sdk.contracts.workflows import workflows_pb2 as v0_workflow_protos +from hatchet_sdk.contracts.workflows.workflows_pb2_grpc import WorkflowServiceStub from hatchet_sdk.exceptions import DedupeViolationError from hatchet_sdk.metadata import get_metadata from hatchet_sdk.rate_limit import RateLimitDuration @@ -186,7 +187,7 @@ class AdminClient: workflow_name: str, input: JSONSerializableMapping, options: TriggerWorkflowOptions, - ) -> v0_workflow_protos.TriggerWorkflowRequest: + ) -> trigger_protos.TriggerWorkflowRequest: try: payload_data = json.dumps(input) except json.JSONDecodeError as e: @@ -194,7 +195,7 @@ class AdminClient: _options = self.TriggerWorkflowRequest.model_validate(options.model_dump()) - return v0_workflow_protos.TriggerWorkflowRequest( + return trigger_protos.TriggerWorkflowRequest( name=workflow_name, input=payload_data, parent_id=_options.parent_id, @@ -342,7 +343,7 @@ class AdminClient: workflow_name: str, input: JSONSerializableMapping, options: TriggerWorkflowOptions, - ) -> v0_workflow_protos.TriggerWorkflowRequest: + ) -> trigger_protos.TriggerWorkflowRequest: workflow_run_id = ctx_workflow_run_id.get() step_run_id = ctx_step_run_id.get() worker_id = ctx_worker_id.get() diff --git a/sdks/python/hatchet_sdk/clients/listeners/durable_event_listener.py b/sdks/python/hatchet_sdk/clients/listeners/durable_event_listener.py index 47957023d..badf09b1b 100644 --- a/sdks/python/hatchet_sdk/clients/listeners/durable_event_listener.py +++ b/sdks/python/hatchet_sdk/clients/listeners/durable_event_listener.py @@ -1,129 +1,281 @@ +import asyncio import json from collections.abc import AsyncIterator -from typing import Any, Literal, cast +from contextlib import suppress +from typing import Self, cast -import grpc import grpc.aio -from pydantic import BaseModel, ConfigDict +from pydantic import BaseModel -from hatchet_sdk.clients.listeners.pooled_listener import PooledListener -from hatchet_sdk.clients.rest.tenacity_utils import tenacity_retry -from hatchet_sdk.conditions import Condition, SleepCondition, UserEventCondition +from hatchet_sdk.clients.admin import AdminClient, TriggerWorkflowOptions from hatchet_sdk.config import ClientConfig from hatchet_sdk.connection import new_conn from hatchet_sdk.contracts.v1.dispatcher_pb2 import ( - DurableEvent, - ListenForDurableEventRequest, -) -from hatchet_sdk.contracts.v1.dispatcher_pb2 import ( - RegisterDurableEventRequest as RegisterDurableEventRequestProto, + DurableTaskAwaitedCallback, + DurableTaskCallbackCompletedResponse, + DurableTaskEventKind, + DurableTaskEventRequest, + DurableTaskRequest, + DurableTaskRequestRegisterWorker, + DurableTaskResponse, + DurableTaskWorkerStatusRequest, ) from hatchet_sdk.contracts.v1.dispatcher_pb2_grpc import V1DispatcherStub from hatchet_sdk.contracts.v1.shared.condition_pb2 import DurableEventListenerConditions +from hatchet_sdk.logger import logger from hatchet_sdk.metadata import get_metadata - -DEFAULT_DURABLE_EVENT_LISTENER_RETRY_INTERVAL = 3 # seconds -DEFAULT_DURABLE_EVENT_LISTENER_RETRY_COUNT = 5 -DEFAULT_DURABLE_EVENT_LISTENER_INTERRUPT_INTERVAL = 1800 # 30 minutes +from hatchet_sdk.utils.typing import JSONSerializableMapping -class RegisterDurableEventRequest(BaseModel): - model_config = ConfigDict(arbitrary_types_allowed=True) +class DurableTaskEventAck(BaseModel): + invocation_count: int + durable_task_external_id: str + node_id: int - task_id: str - signal_key: str - conditions: list[Condition] - config: ClientConfig - def to_proto(self) -> RegisterDurableEventRequestProto: - return RegisterDurableEventRequestProto( - task_id=self.task_id, - signal_key=self.signal_key, - conditions=DurableEventListenerConditions( - sleep_conditions=[ - c.to_proto(self.config) - for c in self.conditions - if isinstance(c, SleepCondition) - ], - user_event_conditions=[ - c.to_proto(self.config) - for c in self.conditions - if isinstance(c, UserEventCondition) - ], - ), +class DurableTaskCallbackResult(BaseModel): + durable_task_external_id: str + node_id: int + payload: JSONSerializableMapping | None + + @classmethod + def from_proto(cls, proto: DurableTaskCallbackCompletedResponse) -> Self: + payload: JSONSerializableMapping | None = None + if proto.payload: + payload = json.loads(proto.payload.decode("utf-8")) + + return cls( + durable_task_external_id=proto.durable_task_external_id, + node_id=proto.node_id, + payload=payload, ) -class ParsedKey(BaseModel): - task_id: str - signal_key: str +class DurableEventListener: + def __init__(self, config: ClientConfig, admin_client: AdminClient): + self.config = config + self.token = config.token + self.admin_client = admin_client + self._worker_id: str | None = None -class DurableEventListener( - PooledListener[ListenForDurableEventRequest, DurableEvent, V1DispatcherStub] -): - def _generate_key(self, task_id: str, signal_key: str) -> str: - return task_id + ":" + signal_key + self._conn: grpc.aio.Channel | None = None + self._stub: V1DispatcherStub | None = None + self._stream: ( + grpc.aio.StreamStreamCall[DurableTaskRequest, DurableTaskResponse] | None + ) = None - def generate_key(self, response: DurableEvent) -> str: - return self._generate_key( - task_id=response.task_id, - signal_key=response.signal_key, + self._request_queue: asyncio.Queue[DurableTaskRequest] | None = None + self._pending_event_acks: dict[ + tuple[str, int], asyncio.Future[DurableTaskEventAck] + ] = {} + self._pending_callbacks: dict[ + tuple[str, int], asyncio.Future[DurableTaskCallbackResult] + ] = {} + + self._receive_task: asyncio.Task[None] | None = None + self._send_task: asyncio.Task[None] | None = None + self._running = False + self._start_lock = asyncio.Lock() + + @property + def worker_id(self) -> str | None: + return self._worker_id + + async def start(self, worker_id: str) -> None: + async with self._start_lock: + if self._running: + return + + self._worker_id = worker_id + self._running = True + self._request_queue = asyncio.Queue() + + self._conn = new_conn(self.config, aio=True) + self._stub = V1DispatcherStub(self._conn) + + self._stream = cast( + grpc.aio.StreamStreamCall[DurableTaskRequest, DurableTaskResponse], + self._stub.DurableTask( + self._request_iterator(), # type: ignore[arg-type] + metadata=get_metadata(self.token), + ), + ) + + self._receive_task = asyncio.create_task(self._receive_loop()) + self._send_task = asyncio.create_task(self._send_loop()) + + await self._register_worker() + + async def ensure_started(self, worker_id: str) -> None: + if not self._running: + await self.start(worker_id) + + async def stop(self) -> None: + self._running = False + + if self._receive_task: + self._receive_task.cancel() + with suppress(asyncio.CancelledError): + await self._receive_task + + if self._send_task: + self._send_task.cancel() + with suppress(asyncio.CancelledError): + await self._send_task + + if self._conn: + await self._conn.close() + + async def _request_iterator(self) -> AsyncIterator[DurableTaskRequest]: + if not self._request_queue: + raise RuntimeError("Request queue not initialized") + + while self._running: + with suppress(asyncio.TimeoutError): + yield await asyncio.wait_for(self._request_queue.get(), timeout=1.0) + + async def _send_loop(self) -> None: + while self._running: + await asyncio.sleep(1) + await self._poll_worker_status() + + async def _poll_worker_status(self) -> None: + if self._request_queue is None or self._worker_id is None: + return + + if not self._pending_callbacks: + return + + waiting = [ + DurableTaskAwaitedCallback( + durable_task_external_id=task_ext_id, + node_id=node_id, + ) + for (task_ext_id, node_id) in self._pending_callbacks + ] + + request = DurableTaskRequest( + worker_status=DurableTaskWorkerStatusRequest( + worker_id=self._worker_id, + waiting_callbacks=waiting, + ) ) + await self._request_queue.put(request) - def parse_key(self, key: str) -> ParsedKey: - task_id, signal_key = key.split(":", maxsplit=1) + async def _receive_loop(self) -> None: + if not self._stream: + return - return ParsedKey( - task_id=task_id, - signal_key=signal_key, + try: + async for response in self._stream: + await self._handle_response(response) + except grpc.aio.AioRpcError as e: + if e.code() != grpc.StatusCode.CANCELLED: + logger.error( + f"DurableTask stream error: code={e.code()}, details={e.details()}" + ) + except asyncio.CancelledError: + logger.debug("Receive loop cancelled") + except Exception as e: + logger.exception(f"Unexpected error in receive loop: {e}") + + async def _handle_response(self, response: DurableTaskResponse) -> None: + if response.HasField("register_worker"): + logger.info( + f"Registered durable task worker: {response.register_worker.worker_id}" + ) + elif response.HasField("trigger_ack"): + trigger_ack = response.trigger_ack + event_key = ( + trigger_ack.durable_task_external_id, + trigger_ack.invocation_count, + ) + if event_key in self._pending_event_acks: + self._pending_event_acks[event_key].set_result( + DurableTaskEventAck( + invocation_count=trigger_ack.invocation_count, + durable_task_external_id=trigger_ack.durable_task_external_id, + node_id=trigger_ack.node_id, + ) + ) + del self._pending_event_acks[event_key] + elif response.HasField("callback_completed"): + completed = response.callback_completed + completed_key = ( + completed.durable_task_external_id, + completed.node_id, + ) + if completed_key in self._pending_callbacks: + future = self._pending_callbacks[completed_key] + if not future.done(): + future.set_result(DurableTaskCallbackResult.from_proto(completed)) + del self._pending_callbacks[completed_key] + + async def _register_worker(self) -> None: + if self._request_queue is None or self._worker_id is None: + raise RuntimeError("Client not started") + + request = DurableTaskRequest( + register_worker=DurableTaskRequestRegisterWorker(worker_id=self._worker_id) ) + await self._request_queue.put(request) - async def create_subscription( + async def send_event( self, - request: AsyncIterator[ListenForDurableEventRequest], - metadata: tuple[tuple[str, str]], - ) -> grpc.aio.UnaryStreamCall[ListenForDurableEventRequest, DurableEvent]: - if self.client is None: - conn = new_conn(self.config, True) - self.client = V1DispatcherStub(conn) + durable_task_external_id: str, + invocation_count: int, + kind: DurableTaskEventKind, + payload: JSONSerializableMapping | None = None, + wait_for_conditions: DurableEventListenerConditions | None = None, + # todo: combine these? or separate methods? or overload? + workflow_name: str | None = None, + trigger_workflow_opts: TriggerWorkflowOptions | None = None, + ) -> DurableTaskEventAck: + if self._request_queue is None: + raise RuntimeError("Client not started") - return cast( - grpc.aio.UnaryStreamCall[ListenForDurableEventRequest, DurableEvent], - self.client.ListenForDurableEvent( - request, # type: ignore[arg-type] - metadata=metadata, - ), + key = (durable_task_external_id, invocation_count) + future: asyncio.Future[DurableTaskEventAck] = asyncio.Future() + self._pending_event_acks[key] = future + + _trigger_opts = ( + self.admin_client._create_workflow_run_request( + workflow_name=workflow_name, + input=payload or {}, + options=trigger_workflow_opts or TriggerWorkflowOptions(), + ) + if workflow_name + else None ) - def create_request_body(self, item: str) -> ListenForDurableEventRequest: - key = self.parse_key(item) - return ListenForDurableEventRequest( - task_id=key.task_id, - signal_key=key.signal_key, + event_request = DurableTaskEventRequest( + durable_task_external_id=durable_task_external_id, + invocation_count=invocation_count, + kind=kind, + trigger_opts=_trigger_opts, ) - def register_durable_event( - self, request: RegisterDurableEventRequest - ) -> Literal[True]: - conn = new_conn(self.config, True) - client = V1DispatcherStub(conn) + if payload is not None: + event_request.payload = json.dumps(payload).encode("utf-8") - register_durable_event = tenacity_retry( - client.RegisterDurableEvent, self.config.tenacity - ) + if wait_for_conditions is not None: + event_request.wait_for_conditions.CopyFrom(wait_for_conditions) - register_durable_event( - request.to_proto(), - timeout=5, - metadata=get_metadata(self.token), - ) + request = DurableTaskRequest(event=event_request) + await self._request_queue.put(request) - return True + return await future - async def result(self, task_id: str, signal_key: str) -> dict[str, Any]: - key = self._generate_key(task_id, signal_key) + async def wait_for_callback( + self, + durable_task_external_id: str, + node_id: int, + ) -> DurableTaskCallbackResult: + key = (durable_task_external_id, node_id) - event = await self.subscribe(key) + if key not in self._pending_callbacks: + future: asyncio.Future[DurableTaskCallbackResult] = asyncio.Future() + self._pending_callbacks[key] = future - return cast(dict[str, Any], json.loads(event.data.decode("utf-8"))) + return await self._pending_callbacks[key] diff --git a/sdks/python/hatchet_sdk/conditions.py b/sdks/python/hatchet_sdk/conditions.py index 2852d9455..2d23bb990 100644 --- a/sdks/python/hatchet_sdk/conditions.py +++ b/sdks/python/hatchet_sdk/conditions.py @@ -10,6 +10,7 @@ from hatchet_sdk.config import ClientConfig from hatchet_sdk.contracts.v1.shared.condition_pb2 import Action as ProtoAction from hatchet_sdk.contracts.v1.shared.condition_pb2 import ( BaseMatchCondition, + DurableEventListenerConditions, ParentOverrideMatchCondition, SleepMatchCondition, UserEventMatchCondition, @@ -155,3 +156,16 @@ def flatten_conditions(conditions: list[Condition | OrGroup]) -> list[Condition] flattened.append(condition) return flattened + + +def build_conditions_proto( + conditions: list[Condition], config: ClientConfig +) -> DurableEventListenerConditions: + return DurableEventListenerConditions( + sleep_conditions=[ + c.to_proto(config) for c in conditions if isinstance(c, SleepCondition) + ], + user_event_conditions=[ + c.to_proto(config) for c in conditions if isinstance(c, UserEventCondition) + ], + ) diff --git a/sdks/python/hatchet_sdk/context/context.py b/sdks/python/hatchet_sdk/context/context.py index 549d7dc7f..b27cc2137 100644 --- a/sdks/python/hatchet_sdk/context/context.py +++ b/sdks/python/hatchet_sdk/context/context.py @@ -4,7 +4,7 @@ from datetime import timedelta from typing import TYPE_CHECKING, Any, cast from warnings import warn -from hatchet_sdk.clients.admin import AdminClient +from hatchet_sdk.clients.admin import AdminClient, TriggerWorkflowOptions from hatchet_sdk.clients.dispatcher.dispatcher import ( # type: ignore[attr-defined] Action, DispatcherClient, @@ -12,25 +12,27 @@ from hatchet_sdk.clients.dispatcher.dispatcher import ( # type: ignore[attr-def from hatchet_sdk.clients.events import EventClient from hatchet_sdk.clients.listeners.durable_event_listener import ( DurableEventListener, - RegisterDurableEventRequest, ) from hatchet_sdk.conditions import ( OrGroup, SleepCondition, UserEventCondition, + build_conditions_proto, flatten_conditions, ) from hatchet_sdk.context.worker_context import WorkerContext +from hatchet_sdk.contracts.v1.dispatcher_pb2 import DurableTaskEventKind from hatchet_sdk.exceptions import TaskRunError from hatchet_sdk.features.runs import RunsClient from hatchet_sdk.logger import logger +from hatchet_sdk.runnables.types import EmptyModel, R, TWorkflowInput from hatchet_sdk.utils.timedelta_to_expression import Duration, timedelta_to_expr from hatchet_sdk.utils.typing import JSONSerializableMapping, LogLevel from hatchet_sdk.worker.runner.utils.capture_logs import AsyncLogSender, LogRecord if TYPE_CHECKING: from hatchet_sdk.runnables.task import Task - from hatchet_sdk.runnables.types import R, TWorkflowInput + from hatchet_sdk.runnables.workflow import BaseWorkflow class Context: @@ -474,6 +476,7 @@ class DurableContext(Context): return index + ## todo: instrumentor for this async def aio_wait_for( self, signal_key: str, @@ -486,26 +489,37 @@ class DurableContext(Context): :param *conditions: The conditions to wait for. Can be a SleepCondition or UserEventCondition. :return: A dictionary containing the results of the wait. - :raises ValueError: If the durable event listener is not available. + :raises ValueError: If the durable task client is not available. """ if self.durable_event_listener is None: - raise ValueError("Durable event listener is not available") + raise ValueError("Durable task client is not available") - task_id = self.step_run_id + from hatchet_sdk.contracts.v1.dispatcher_pb2 import DurableTaskEventKind - request = RegisterDurableEventRequest( - task_id=task_id, - signal_key=signal_key, - conditions=flatten_conditions(list(conditions)), - config=self.runs_client.client_config, + await self._ensure_stream_started() + + flat_conditions = flatten_conditions(list(conditions)) + conditions_proto = build_conditions_proto( + flat_conditions, self.runs_client.client_config + ) + invocation_count = self.attempt_number + + ack = await self.durable_event_listener.send_event( + durable_task_external_id=self.step_run_id, + ## todo: figure out how to store this invocation count properly + invocation_count=invocation_count, + kind=DurableTaskEventKind.DURABLE_TASK_TRIGGER_KIND_WAIT_FOR, + payload=None, + wait_for_conditions=conditions_proto, + ) + node_id = ack.node_id + + result = await self.durable_event_listener.wait_for_callback( + durable_task_external_id=self.step_run_id, + node_id=node_id, ) - self.durable_event_listener.register_durable_event(request) - - return await self.durable_event_listener.result( - task_id, - signal_key, - ) + return result.payload or {} async def aio_sleep_for(self, duration: Duration) -> dict[str, Any]: """ @@ -520,3 +534,39 @@ class DurableContext(Context): f"sleep:{timedelta_to_expr(duration)}-{wait_index}", SleepCondition(duration=duration), ) + + ## todo: instrumentor for this + async def _spawn_child( + self, + workflow: "BaseWorkflow[TWorkflowInput]", + input: TWorkflowInput = cast(Any, EmptyModel()), + options: "TriggerWorkflowOptions" | None = None, + ) -> dict[str, Any]: + if self.durable_event_listener is None: + raise ValueError("Durable task client is not available") + + await self._ensure_stream_started() + + ack = await self.durable_event_listener.send_event( + durable_task_external_id=self.step_run_id, + invocation_count=self.attempt_number, + kind=DurableTaskEventKind.DURABLE_TASK_TRIGGER_KIND_RUN, + payload=workflow._serialize_input(input), + workflow_name=workflow.config.name, + trigger_workflow_opts=options, + ) + + node_id = ack.node_id + + result = await self.durable_event_listener.wait_for_callback( + durable_task_external_id=self.step_run_id, + node_id=node_id, + ) + + return result.payload or {} + + async def _ensure_stream_started(self) -> None: + if self.durable_event_listener is None: + raise ValueError("Durable task client is not available") + + await self.durable_event_listener.ensure_started(self.action.worker_id) diff --git a/sdks/python/hatchet_sdk/contracts/v1/dispatcher_pb2.py b/sdks/python/hatchet_sdk/contracts/v1/dispatcher_pb2.py index 988661d82..913222772 100644 --- a/sdks/python/hatchet_sdk/contracts/v1/dispatcher_pb2.py +++ b/sdks/python/hatchet_sdk/contracts/v1/dispatcher_pb2.py @@ -23,9 +23,10 @@ _sym_db = _symbol_database.Default() from hatchet_sdk.contracts.v1.shared import condition_pb2 as v1_dot_shared_dot_condition__pb2 +from hatchet_sdk.contracts.v1.shared import trigger_pb2 as v1_dot_shared_dot_trigger__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13v1/dispatcher.proto\x12\x02v1\x1a\x19v1/shared/condition.proto\"z\n\x1bRegisterDurableEventRequest\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x12\n\nsignal_key\x18\x02 \x01(\t\x12\x36\n\nconditions\x18\x03 \x01(\x0b\x32\".v1.DurableEventListenerConditions\"\x1e\n\x1cRegisterDurableEventResponse\"C\n\x1cListenForDurableEventRequest\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x12\n\nsignal_key\x18\x02 \x01(\t\"A\n\x0c\x44urableEvent\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x12\n\nsignal_key\x18\x02 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c\x32\xbe\x01\n\x0cV1Dispatcher\x12[\n\x14RegisterDurableEvent\x12\x1f.v1.RegisterDurableEventRequest\x1a .v1.RegisterDurableEventResponse\"\x00\x12Q\n\x15ListenForDurableEvent\x12 .v1.ListenForDurableEventRequest\x1a\x10.v1.DurableEvent\"\x00(\x01\x30\x01\x42\x42Z@github.com/hatchet-dev/hatchet/internal/services/shared/proto/v1b\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13v1/dispatcher.proto\x12\x02v1\x1a\x19v1/shared/condition.proto\x1a\x17v1/shared/trigger.proto\"5\n DurableTaskRequestRegisterWorker\x12\x11\n\tworker_id\x18\x01 \x01(\t\"6\n!DurableTaskResponseRegisterWorker\x12\x11\n\tworker_id\x18\x01 \x01(\t\"\xc5\x02\n\x17\x44urableTaskEventRequest\x12\x18\n\x10invocation_count\x18\x01 \x01(\x03\x12 \n\x18\x64urable_task_external_id\x18\x02 \x01(\t\x12&\n\x04kind\x18\x03 \x01(\x0e\x32\x18.v1.DurableTaskEventKind\x12\x14\n\x07payload\x18\x04 \x01(\x0cH\x00\x88\x01\x01\x12\x44\n\x13wait_for_conditions\x18\x05 \x01(\x0b\x32\".v1.DurableEventListenerConditionsH\x01\x88\x01\x01\x12\x35\n\x0ctrigger_opts\x18\x06 \x01(\x0b\x32\x1a.v1.TriggerWorkflowRequestH\x02\x88\x01\x01\x42\n\n\x08_payloadB\x16\n\x14_wait_for_conditionsB\x0f\n\r_trigger_opts\"j\n\x1b\x44urableTaskEventAckResponse\x12\x18\n\x10invocation_count\x18\x01 \x01(\x03\x12 \n\x18\x64urable_task_external_id\x18\x02 \x01(\t\x12\x0f\n\x07node_id\x18\x03 \x01(\x03\"j\n$DurableTaskCallbackCompletedResponse\x12 \n\x18\x64urable_task_external_id\x18\x01 \x01(\t\x12\x0f\n\x07node_id\x18\x02 \x01(\x03\x12\x0f\n\x07payload\x18\x03 \x01(\x0c\"_\n!DurableTaskEvictInvocationRequest\x12\x18\n\x10invocation_count\x18\x01 \x01(\x03\x12 \n\x18\x64urable_task_external_id\x18\x02 \x01(\t\"O\n\x1a\x44urableTaskAwaitedCallback\x12 \n\x18\x64urable_task_external_id\x18\x01 \x01(\t\x12\x0f\n\x07node_id\x18\x02 \x01(\x03\"\x92\x01\n\x1e\x44urableTaskWorkerStatusRequest\x12\x11\n\tworker_id\x18\x01 \x01(\t\x12\x0f\n\x07node_id\x18\x02 \x01(\x03\x12\x11\n\tbranch_id\x18\x03 \x01(\x03\x12\x39\n\x11waiting_callbacks\x18\x04 \x03(\x0b\x32\x1e.v1.DurableTaskAwaitedCallback\"\x8e\x02\n\x12\x44urableTaskRequest\x12?\n\x0fregister_worker\x18\x01 \x01(\x0b\x32$.v1.DurableTaskRequestRegisterWorkerH\x00\x12,\n\x05\x65vent\x18\x02 \x01(\x0b\x32\x1b.v1.DurableTaskEventRequestH\x00\x12\x41\n\x10\x65vict_invocation\x18\x03 \x01(\x0b\x32%.v1.DurableTaskEvictInvocationRequestH\x00\x12;\n\rworker_status\x18\x04 \x01(\x0b\x32\".v1.DurableTaskWorkerStatusRequestH\x00\x42\t\n\x07message\"\xe2\x01\n\x13\x44urableTaskResponse\x12@\n\x0fregister_worker\x18\x01 \x01(\x0b\x32%.v1.DurableTaskResponseRegisterWorkerH\x00\x12\x36\n\x0btrigger_ack\x18\x02 \x01(\x0b\x32\x1f.v1.DurableTaskEventAckResponseH\x00\x12\x46\n\x12\x63\x61llback_completed\x18\x03 \x01(\x0b\x32(.v1.DurableTaskCallbackCompletedResponseH\x00\x42\t\n\x07message\"z\n\x1bRegisterDurableEventRequest\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x12\n\nsignal_key\x18\x02 \x01(\t\x12\x36\n\nconditions\x18\x03 \x01(\x0b\x32\".v1.DurableEventListenerConditions\"\x1e\n\x1cRegisterDurableEventResponse\"C\n\x1cListenForDurableEventRequest\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x12\n\nsignal_key\x18\x02 \x01(\t\"A\n\x0c\x44urableEvent\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x12\n\nsignal_key\x18\x02 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c*\xb0\x01\n\x14\x44urableTaskEventKind\x12)\n%DURABLE_TASK_TRIGGER_KIND_UNSPECIFIED\x10\x00\x12!\n\x1d\x44URABLE_TASK_TRIGGER_KIND_RUN\x10\x01\x12&\n\"DURABLE_TASK_TRIGGER_KIND_WAIT_FOR\x10\x02\x12\"\n\x1e\x44URABLE_TASK_TRIGGER_KIND_MEMO\x10\x03\x32\x84\x02\n\x0cV1Dispatcher\x12\x44\n\x0b\x44urableTask\x12\x16.v1.DurableTaskRequest\x1a\x17.v1.DurableTaskResponse\"\x00(\x01\x30\x01\x12[\n\x14RegisterDurableEvent\x12\x1f.v1.RegisterDurableEventRequest\x1a .v1.RegisterDurableEventResponse\"\x00\x12Q\n\x15ListenForDurableEvent\x12 .v1.ListenForDurableEventRequest\x1a\x10.v1.DurableEvent\"\x00(\x01\x30\x01\x42\x42Z@github.com/hatchet-dev/hatchet/internal/services/shared/proto/v1b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -33,14 +34,36 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'v1.dispatcher_pb2', _global if not _descriptor._USE_C_DESCRIPTORS: _globals['DESCRIPTOR']._loaded_options = None _globals['DESCRIPTOR']._serialized_options = b'Z@github.com/hatchet-dev/hatchet/internal/services/shared/proto/v1' - _globals['_REGISTERDURABLEEVENTREQUEST']._serialized_start=54 - _globals['_REGISTERDURABLEEVENTREQUEST']._serialized_end=176 - _globals['_REGISTERDURABLEEVENTRESPONSE']._serialized_start=178 - _globals['_REGISTERDURABLEEVENTRESPONSE']._serialized_end=208 - _globals['_LISTENFORDURABLEEVENTREQUEST']._serialized_start=210 - _globals['_LISTENFORDURABLEEVENTREQUEST']._serialized_end=277 - _globals['_DURABLEEVENT']._serialized_start=279 - _globals['_DURABLEEVENT']._serialized_end=344 - _globals['_V1DISPATCHER']._serialized_start=347 - _globals['_V1DISPATCHER']._serialized_end=537 + _globals['_DURABLETASKEVENTKIND']._serialized_start=1856 + _globals['_DURABLETASKEVENTKIND']._serialized_end=2032 + _globals['_DURABLETASKREQUESTREGISTERWORKER']._serialized_start=79 + _globals['_DURABLETASKREQUESTREGISTERWORKER']._serialized_end=132 + _globals['_DURABLETASKRESPONSEREGISTERWORKER']._serialized_start=134 + _globals['_DURABLETASKRESPONSEREGISTERWORKER']._serialized_end=188 + _globals['_DURABLETASKEVENTREQUEST']._serialized_start=191 + _globals['_DURABLETASKEVENTREQUEST']._serialized_end=516 + _globals['_DURABLETASKEVENTACKRESPONSE']._serialized_start=518 + _globals['_DURABLETASKEVENTACKRESPONSE']._serialized_end=624 + _globals['_DURABLETASKCALLBACKCOMPLETEDRESPONSE']._serialized_start=626 + _globals['_DURABLETASKCALLBACKCOMPLETEDRESPONSE']._serialized_end=732 + _globals['_DURABLETASKEVICTINVOCATIONREQUEST']._serialized_start=734 + _globals['_DURABLETASKEVICTINVOCATIONREQUEST']._serialized_end=829 + _globals['_DURABLETASKAWAITEDCALLBACK']._serialized_start=831 + _globals['_DURABLETASKAWAITEDCALLBACK']._serialized_end=910 + _globals['_DURABLETASKWORKERSTATUSREQUEST']._serialized_start=913 + _globals['_DURABLETASKWORKERSTATUSREQUEST']._serialized_end=1059 + _globals['_DURABLETASKREQUEST']._serialized_start=1062 + _globals['_DURABLETASKREQUEST']._serialized_end=1332 + _globals['_DURABLETASKRESPONSE']._serialized_start=1335 + _globals['_DURABLETASKRESPONSE']._serialized_end=1561 + _globals['_REGISTERDURABLEEVENTREQUEST']._serialized_start=1563 + _globals['_REGISTERDURABLEEVENTREQUEST']._serialized_end=1685 + _globals['_REGISTERDURABLEEVENTRESPONSE']._serialized_start=1687 + _globals['_REGISTERDURABLEEVENTRESPONSE']._serialized_end=1717 + _globals['_LISTENFORDURABLEEVENTREQUEST']._serialized_start=1719 + _globals['_LISTENFORDURABLEEVENTREQUEST']._serialized_end=1786 + _globals['_DURABLEEVENT']._serialized_start=1788 + _globals['_DURABLEEVENT']._serialized_end=1853 + _globals['_V1DISPATCHER']._serialized_start=2035 + _globals['_V1DISPATCHER']._serialized_end=2295 # @@protoc_insertion_point(module_scope) diff --git a/sdks/python/hatchet_sdk/contracts/v1/dispatcher_pb2.pyi b/sdks/python/hatchet_sdk/contracts/v1/dispatcher_pb2.pyi index c8b3ddc79..2c1ad677c 100644 --- a/sdks/python/hatchet_sdk/contracts/v1/dispatcher_pb2.pyi +++ b/sdks/python/hatchet_sdk/contracts/v1/dispatcher_pb2.pyi @@ -1,11 +1,123 @@ from hatchet_sdk.contracts.v1.shared import condition_pb2 as _condition_pb2 +from hatchet_sdk.contracts.v1.shared import trigger_pb2 as _trigger_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message -from collections.abc import Mapping as _Mapping +from collections.abc import Iterable as _Iterable, Mapping as _Mapping from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor +class DurableTaskEventKind(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + DURABLE_TASK_TRIGGER_KIND_UNSPECIFIED: _ClassVar[DurableTaskEventKind] + DURABLE_TASK_TRIGGER_KIND_RUN: _ClassVar[DurableTaskEventKind] + DURABLE_TASK_TRIGGER_KIND_WAIT_FOR: _ClassVar[DurableTaskEventKind] + DURABLE_TASK_TRIGGER_KIND_MEMO: _ClassVar[DurableTaskEventKind] +DURABLE_TASK_TRIGGER_KIND_UNSPECIFIED: DurableTaskEventKind +DURABLE_TASK_TRIGGER_KIND_RUN: DurableTaskEventKind +DURABLE_TASK_TRIGGER_KIND_WAIT_FOR: DurableTaskEventKind +DURABLE_TASK_TRIGGER_KIND_MEMO: DurableTaskEventKind + +class DurableTaskRequestRegisterWorker(_message.Message): + __slots__ = ("worker_id",) + WORKER_ID_FIELD_NUMBER: _ClassVar[int] + worker_id: str + def __init__(self, worker_id: _Optional[str] = ...) -> None: ... + +class DurableTaskResponseRegisterWorker(_message.Message): + __slots__ = ("worker_id",) + WORKER_ID_FIELD_NUMBER: _ClassVar[int] + worker_id: str + def __init__(self, worker_id: _Optional[str] = ...) -> None: ... + +class DurableTaskEventRequest(_message.Message): + __slots__ = ("invocation_count", "durable_task_external_id", "kind", "payload", "wait_for_conditions", "trigger_opts") + INVOCATION_COUNT_FIELD_NUMBER: _ClassVar[int] + DURABLE_TASK_EXTERNAL_ID_FIELD_NUMBER: _ClassVar[int] + KIND_FIELD_NUMBER: _ClassVar[int] + PAYLOAD_FIELD_NUMBER: _ClassVar[int] + WAIT_FOR_CONDITIONS_FIELD_NUMBER: _ClassVar[int] + TRIGGER_OPTS_FIELD_NUMBER: _ClassVar[int] + invocation_count: int + durable_task_external_id: str + kind: DurableTaskEventKind + payload: bytes + wait_for_conditions: _condition_pb2.DurableEventListenerConditions + trigger_opts: _trigger_pb2.TriggerWorkflowRequest + def __init__(self, invocation_count: _Optional[int] = ..., durable_task_external_id: _Optional[str] = ..., kind: _Optional[_Union[DurableTaskEventKind, str]] = ..., payload: _Optional[bytes] = ..., wait_for_conditions: _Optional[_Union[_condition_pb2.DurableEventListenerConditions, _Mapping]] = ..., trigger_opts: _Optional[_Union[_trigger_pb2.TriggerWorkflowRequest, _Mapping]] = ...) -> None: ... + +class DurableTaskEventAckResponse(_message.Message): + __slots__ = ("invocation_count", "durable_task_external_id", "node_id") + INVOCATION_COUNT_FIELD_NUMBER: _ClassVar[int] + DURABLE_TASK_EXTERNAL_ID_FIELD_NUMBER: _ClassVar[int] + NODE_ID_FIELD_NUMBER: _ClassVar[int] + invocation_count: int + durable_task_external_id: str + node_id: int + def __init__(self, invocation_count: _Optional[int] = ..., durable_task_external_id: _Optional[str] = ..., node_id: _Optional[int] = ...) -> None: ... + +class DurableTaskCallbackCompletedResponse(_message.Message): + __slots__ = ("durable_task_external_id", "node_id", "payload") + DURABLE_TASK_EXTERNAL_ID_FIELD_NUMBER: _ClassVar[int] + NODE_ID_FIELD_NUMBER: _ClassVar[int] + PAYLOAD_FIELD_NUMBER: _ClassVar[int] + durable_task_external_id: str + node_id: int + payload: bytes + def __init__(self, durable_task_external_id: _Optional[str] = ..., node_id: _Optional[int] = ..., payload: _Optional[bytes] = ...) -> None: ... + +class DurableTaskEvictInvocationRequest(_message.Message): + __slots__ = ("invocation_count", "durable_task_external_id") + INVOCATION_COUNT_FIELD_NUMBER: _ClassVar[int] + DURABLE_TASK_EXTERNAL_ID_FIELD_NUMBER: _ClassVar[int] + invocation_count: int + durable_task_external_id: str + def __init__(self, invocation_count: _Optional[int] = ..., durable_task_external_id: _Optional[str] = ...) -> None: ... + +class DurableTaskAwaitedCallback(_message.Message): + __slots__ = ("durable_task_external_id", "node_id") + DURABLE_TASK_EXTERNAL_ID_FIELD_NUMBER: _ClassVar[int] + NODE_ID_FIELD_NUMBER: _ClassVar[int] + durable_task_external_id: str + node_id: int + def __init__(self, durable_task_external_id: _Optional[str] = ..., node_id: _Optional[int] = ...) -> None: ... + +class DurableTaskWorkerStatusRequest(_message.Message): + __slots__ = ("worker_id", "node_id", "branch_id", "waiting_callbacks") + WORKER_ID_FIELD_NUMBER: _ClassVar[int] + NODE_ID_FIELD_NUMBER: _ClassVar[int] + BRANCH_ID_FIELD_NUMBER: _ClassVar[int] + WAITING_CALLBACKS_FIELD_NUMBER: _ClassVar[int] + worker_id: str + node_id: int + branch_id: int + waiting_callbacks: _containers.RepeatedCompositeFieldContainer[DurableTaskAwaitedCallback] + def __init__(self, worker_id: _Optional[str] = ..., node_id: _Optional[int] = ..., branch_id: _Optional[int] = ..., waiting_callbacks: _Optional[_Iterable[_Union[DurableTaskAwaitedCallback, _Mapping]]] = ...) -> None: ... + +class DurableTaskRequest(_message.Message): + __slots__ = ("register_worker", "event", "evict_invocation", "worker_status") + REGISTER_WORKER_FIELD_NUMBER: _ClassVar[int] + EVENT_FIELD_NUMBER: _ClassVar[int] + EVICT_INVOCATION_FIELD_NUMBER: _ClassVar[int] + WORKER_STATUS_FIELD_NUMBER: _ClassVar[int] + register_worker: DurableTaskRequestRegisterWorker + event: DurableTaskEventRequest + evict_invocation: DurableTaskEvictInvocationRequest + worker_status: DurableTaskWorkerStatusRequest + def __init__(self, register_worker: _Optional[_Union[DurableTaskRequestRegisterWorker, _Mapping]] = ..., event: _Optional[_Union[DurableTaskEventRequest, _Mapping]] = ..., evict_invocation: _Optional[_Union[DurableTaskEvictInvocationRequest, _Mapping]] = ..., worker_status: _Optional[_Union[DurableTaskWorkerStatusRequest, _Mapping]] = ...) -> None: ... + +class DurableTaskResponse(_message.Message): + __slots__ = ("register_worker", "trigger_ack", "callback_completed") + REGISTER_WORKER_FIELD_NUMBER: _ClassVar[int] + TRIGGER_ACK_FIELD_NUMBER: _ClassVar[int] + CALLBACK_COMPLETED_FIELD_NUMBER: _ClassVar[int] + register_worker: DurableTaskResponseRegisterWorker + trigger_ack: DurableTaskEventAckResponse + callback_completed: DurableTaskCallbackCompletedResponse + def __init__(self, register_worker: _Optional[_Union[DurableTaskResponseRegisterWorker, _Mapping]] = ..., trigger_ack: _Optional[_Union[DurableTaskEventAckResponse, _Mapping]] = ..., callback_completed: _Optional[_Union[DurableTaskCallbackCompletedResponse, _Mapping]] = ...) -> None: ... + class RegisterDurableEventRequest(_message.Message): __slots__ = ("task_id", "signal_key", "conditions") TASK_ID_FIELD_NUMBER: _ClassVar[int] diff --git a/sdks/python/hatchet_sdk/contracts/v1/dispatcher_pb2_grpc.py b/sdks/python/hatchet_sdk/contracts/v1/dispatcher_pb2_grpc.py index 74d39ceec..96f2b25dd 100644 --- a/sdks/python/hatchet_sdk/contracts/v1/dispatcher_pb2_grpc.py +++ b/sdks/python/hatchet_sdk/contracts/v1/dispatcher_pb2_grpc.py @@ -34,6 +34,11 @@ class V1DispatcherStub(object): Args: channel: A grpc.Channel. """ + self.DurableTask = channel.stream_stream( + '/v1.V1Dispatcher/DurableTask', + request_serializer=v1_dot_dispatcher__pb2.DurableTaskRequest.SerializeToString, + response_deserializer=v1_dot_dispatcher__pb2.DurableTaskResponse.FromString, + _registered_method=True) self.RegisterDurableEvent = channel.unary_unary( '/v1.V1Dispatcher/RegisterDurableEvent', request_serializer=v1_dot_dispatcher__pb2.RegisterDurableEventRequest.SerializeToString, @@ -49,12 +54,19 @@ class V1DispatcherStub(object): class V1DispatcherServicer(object): """Missing associated documentation comment in .proto file.""" - def RegisterDurableEvent(self, request, context): + def DurableTask(self, request_iterator, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def RegisterDurableEvent(self, request, context): + """NOTE: deprecated after DurableEventLog is implemented + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def ListenForDurableEvent(self, request_iterator, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) @@ -64,6 +76,11 @@ class V1DispatcherServicer(object): def add_V1DispatcherServicer_to_server(servicer, server): rpc_method_handlers = { + 'DurableTask': grpc.stream_stream_rpc_method_handler( + servicer.DurableTask, + request_deserializer=v1_dot_dispatcher__pb2.DurableTaskRequest.FromString, + response_serializer=v1_dot_dispatcher__pb2.DurableTaskResponse.SerializeToString, + ), 'RegisterDurableEvent': grpc.unary_unary_rpc_method_handler( servicer.RegisterDurableEvent, request_deserializer=v1_dot_dispatcher__pb2.RegisterDurableEventRequest.FromString, @@ -85,6 +102,33 @@ def add_V1DispatcherServicer_to_server(servicer, server): class V1Dispatcher(object): """Missing associated documentation comment in .proto file.""" + @staticmethod + def DurableTask(request_iterator, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.stream_stream( + request_iterator, + target, + '/v1.V1Dispatcher/DurableTask', + v1_dot_dispatcher__pb2.DurableTaskRequest.SerializeToString, + v1_dot_dispatcher__pb2.DurableTaskResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + @staticmethod def RegisterDurableEvent(request, target, diff --git a/sdks/python/hatchet_sdk/contracts/v1/shared/trigger_pb2.py b/sdks/python/hatchet_sdk/contracts/v1/shared/trigger_pb2.py new file mode 100644 index 000000000..ef76834f8 --- /dev/null +++ b/sdks/python/hatchet_sdk/contracts/v1/shared/trigger_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: v1/shared/trigger.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'v1/shared/trigger.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17v1/shared/trigger.proto\x12\x02v1\"\x89\x03\n\x16TriggerWorkflowRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05input\x18\x02 \x01(\t\x12\x16\n\tparent_id\x18\x03 \x01(\tH\x00\x88\x01\x01\x12(\n\x1bparent_task_run_external_id\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0b\x63hild_index\x18\x05 \x01(\x05H\x02\x88\x01\x01\x12\x16\n\tchild_key\x18\x06 \x01(\tH\x03\x88\x01\x01\x12 \n\x13\x61\x64\x64itional_metadata\x18\x07 \x01(\tH\x04\x88\x01\x01\x12\x1e\n\x11\x64\x65sired_worker_id\x18\x08 \x01(\tH\x05\x88\x01\x01\x12\x15\n\x08priority\x18\t \x01(\x05H\x06\x88\x01\x01\x42\x0c\n\n_parent_idB\x1e\n\x1c_parent_task_run_external_idB\x0e\n\x0c_child_indexB\x0c\n\n_child_keyB\x16\n\x14_additional_metadataB\x14\n\x12_desired_worker_idB\x0b\n\t_priorityBBZ@github.com/hatchet-dev/hatchet/internal/services/shared/proto/v1b\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'v1.shared.trigger_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z@github.com/hatchet-dev/hatchet/internal/services/shared/proto/v1' + _globals['_TRIGGERWORKFLOWREQUEST']._serialized_start=32 + _globals['_TRIGGERWORKFLOWREQUEST']._serialized_end=425 +# @@protoc_insertion_point(module_scope) diff --git a/sdks/python/hatchet_sdk/contracts/v1/shared/trigger_pb2.pyi b/sdks/python/hatchet_sdk/contracts/v1/shared/trigger_pb2.pyi new file mode 100644 index 000000000..dde30ed3c --- /dev/null +++ b/sdks/python/hatchet_sdk/contracts/v1/shared/trigger_pb2.pyi @@ -0,0 +1,27 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class TriggerWorkflowRequest(_message.Message): + __slots__ = ("name", "input", "parent_id", "parent_task_run_external_id", "child_index", "child_key", "additional_metadata", "desired_worker_id", "priority") + NAME_FIELD_NUMBER: _ClassVar[int] + INPUT_FIELD_NUMBER: _ClassVar[int] + PARENT_ID_FIELD_NUMBER: _ClassVar[int] + PARENT_TASK_RUN_EXTERNAL_ID_FIELD_NUMBER: _ClassVar[int] + CHILD_INDEX_FIELD_NUMBER: _ClassVar[int] + CHILD_KEY_FIELD_NUMBER: _ClassVar[int] + ADDITIONAL_METADATA_FIELD_NUMBER: _ClassVar[int] + DESIRED_WORKER_ID_FIELD_NUMBER: _ClassVar[int] + PRIORITY_FIELD_NUMBER: _ClassVar[int] + name: str + input: str + parent_id: str + parent_task_run_external_id: str + child_index: int + child_key: str + additional_metadata: str + desired_worker_id: str + priority: int + def __init__(self, name: _Optional[str] = ..., input: _Optional[str] = ..., parent_id: _Optional[str] = ..., parent_task_run_external_id: _Optional[str] = ..., child_index: _Optional[int] = ..., child_key: _Optional[str] = ..., additional_metadata: _Optional[str] = ..., desired_worker_id: _Optional[str] = ..., priority: _Optional[int] = ...) -> None: ... diff --git a/sdks/python/hatchet_sdk/contracts/v1/shared/trigger_pb2_grpc.py b/sdks/python/hatchet_sdk/contracts/v1/shared/trigger_pb2_grpc.py new file mode 100644 index 000000000..d3ccb592d --- /dev/null +++ b/sdks/python/hatchet_sdk/contracts/v1/shared/trigger_pb2_grpc.py @@ -0,0 +1,24 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + + +GRPC_GENERATED_VERSION = '1.76.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + ' but the generated code in v1/shared/trigger_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) diff --git a/sdks/python/hatchet_sdk/contracts/workflows/__init__.py b/sdks/python/hatchet_sdk/contracts/workflows/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/sdks/python/hatchet_sdk/contracts/workflows/workflows_pb2.py b/sdks/python/hatchet_sdk/contracts/workflows/workflows_pb2.py new file mode 100644 index 000000000..477aec7da --- /dev/null +++ b/sdks/python/hatchet_sdk/contracts/workflows/workflows_pb2.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: workflows/workflows.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'workflows/workflows.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 +from hatchet_sdk.contracts.v1.shared import trigger_pb2 as v1_dot_shared_dot_trigger__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19workflows/workflows.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x17v1/shared/trigger.proto\">\n\x12PutWorkflowRequest\x12(\n\x04opts\x18\x01 \x01(\x0b\x32\x1a.CreateWorkflowVersionOpts\"\xbf\x04\n\x19\x43reateWorkflowVersionOpts\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x16\n\x0e\x65vent_triggers\x18\x04 \x03(\t\x12\x15\n\rcron_triggers\x18\x05 \x03(\t\x12\x36\n\x12scheduled_triggers\x18\x06 \x03(\x0b\x32\x1a.google.protobuf.Timestamp\x12$\n\x04jobs\x18\x07 \x03(\x0b\x32\x16.CreateWorkflowJobOpts\x12-\n\x0b\x63oncurrency\x18\x08 \x01(\x0b\x32\x18.WorkflowConcurrencyOpts\x12\x1d\n\x10schedule_timeout\x18\t \x01(\tH\x00\x88\x01\x01\x12\x17\n\ncron_input\x18\n \x01(\tH\x01\x88\x01\x01\x12\x33\n\x0eon_failure_job\x18\x0b \x01(\x0b\x32\x16.CreateWorkflowJobOptsH\x02\x88\x01\x01\x12$\n\x06sticky\x18\x0c \x01(\x0e\x32\x0f.StickyStrategyH\x03\x88\x01\x01\x12 \n\x04kind\x18\r \x01(\x0e\x32\r.WorkflowKindH\x04\x88\x01\x01\x12\x1d\n\x10\x64\x65\x66\x61ult_priority\x18\x0e \x01(\x05H\x05\x88\x01\x01\x42\x13\n\x11_schedule_timeoutB\r\n\x0b_cron_inputB\x11\n\x0f_on_failure_jobB\t\n\x07_stickyB\x07\n\x05_kindB\x13\n\x11_default_priority\"\xd0\x01\n\x17WorkflowConcurrencyOpts\x12\x13\n\x06\x61\x63tion\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08max_runs\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12\x36\n\x0elimit_strategy\x18\x03 \x01(\x0e\x32\x19.ConcurrencyLimitStrategyH\x02\x88\x01\x01\x12\x17\n\nexpression\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\t\n\x07_actionB\x0b\n\t_max_runsB\x11\n\x0f_limit_strategyB\r\n\x0b_expression\"h\n\x15\x43reateWorkflowJobOpts\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12&\n\x05steps\x18\x04 \x03(\x0b\x32\x17.CreateWorkflowStepOptsJ\x04\x08\x03\x10\x04\"\xe5\x01\n\x13\x44\x65siredWorkerLabels\x12\x16\n\tstr_value\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tint_value\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12\x15\n\x08required\x18\x03 \x01(\x08H\x02\x88\x01\x01\x12/\n\ncomparator\x18\x04 \x01(\x0e\x32\x16.WorkerLabelComparatorH\x03\x88\x01\x01\x12\x13\n\x06weight\x18\x05 \x01(\x05H\x04\x88\x01\x01\x42\x0c\n\n_str_valueB\x0c\n\n_int_valueB\x0b\n\t_requiredB\r\n\x0b_comparatorB\t\n\x07_weight\"\xb5\x03\n\x16\x43reateWorkflowStepOpts\x12\x13\n\x0breadable_id\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tion\x18\x02 \x01(\t\x12\x0f\n\x07timeout\x18\x03 \x01(\t\x12\x0e\n\x06inputs\x18\x04 \x01(\t\x12\x0f\n\x07parents\x18\x05 \x03(\t\x12\x11\n\tuser_data\x18\x06 \x01(\t\x12\x0f\n\x07retries\x18\x07 \x01(\x05\x12)\n\x0brate_limits\x18\x08 \x03(\x0b\x32\x14.CreateStepRateLimit\x12@\n\rworker_labels\x18\t \x03(\x0b\x32).CreateWorkflowStepOpts.WorkerLabelsEntry\x12\x1b\n\x0e\x62\x61\x63koff_factor\x18\n \x01(\x02H\x00\x88\x01\x01\x12 \n\x13\x62\x61\x63koff_max_seconds\x18\x0b \x01(\x05H\x01\x88\x01\x01\x1aI\n\x11WorkerLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12#\n\x05value\x18\x02 \x01(\x0b\x32\x14.DesiredWorkerLabels:\x02\x38\x01\x42\x11\n\x0f_backoff_factorB\x16\n\x14_backoff_max_seconds\"\xfa\x01\n\x13\x43reateStepRateLimit\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x12\n\x05units\x18\x02 \x01(\x05H\x00\x88\x01\x01\x12\x15\n\x08key_expr\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nunits_expr\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1e\n\x11limit_values_expr\x18\x05 \x01(\tH\x03\x88\x01\x01\x12)\n\x08\x64uration\x18\x06 \x01(\x0e\x32\x12.RateLimitDurationH\x04\x88\x01\x01\x42\x08\n\x06_unitsB\x0b\n\t_key_exprB\r\n\x0b_units_exprB\x14\n\x12_limit_values_exprB\x0b\n\t_duration\"\x16\n\x14ListWorkflowsRequest\"\x83\x03\n\x17ScheduleWorkflowRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12-\n\tschedules\x18\x02 \x03(\x0b\x32\x1a.google.protobuf.Timestamp\x12\r\n\x05input\x18\x03 \x01(\t\x12\x16\n\tparent_id\x18\x04 \x01(\tH\x00\x88\x01\x01\x12(\n\x1bparent_task_run_external_id\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0b\x63hild_index\x18\x06 \x01(\x05H\x02\x88\x01\x01\x12\x16\n\tchild_key\x18\x07 \x01(\tH\x03\x88\x01\x01\x12 \n\x13\x61\x64\x64itional_metadata\x18\x08 \x01(\tH\x04\x88\x01\x01\x12\x15\n\x08priority\x18\t \x01(\x05H\x05\x88\x01\x01\x42\x0c\n\n_parent_idB\x1e\n\x1c_parent_task_run_external_idB\x0e\n\x0c_child_indexB\x0c\n\n_child_keyB\x16\n\x14_additional_metadataB\x0b\n\t_priority\"O\n\x11ScheduledWorkflow\x12\n\n\x02id\x18\x01 \x01(\t\x12.\n\ntrigger_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"\xe3\x01\n\x0fWorkflowVersion\x12\n\n\x02id\x18\x01 \x01(\t\x12.\n\ncreated_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nupdated_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0f\n\x07version\x18\x05 \x01(\t\x12\r\n\x05order\x18\x06 \x01(\x03\x12\x13\n\x0bworkflow_id\x18\x07 \x01(\t\x12/\n\x13scheduled_workflows\x18\x08 \x03(\x0b\x32\x12.ScheduledWorkflow\"?\n\x17WorkflowTriggerEventRef\x12\x11\n\tparent_id\x18\x01 \x01(\t\x12\x11\n\tevent_key\x18\x02 \x01(\t\"9\n\x16WorkflowTriggerCronRef\x12\x11\n\tparent_id\x18\x01 \x01(\t\x12\x0c\n\x04\x63ron\x18\x02 \x01(\t\"K\n\x1a\x42ulkTriggerWorkflowRequest\x12-\n\tworkflows\x18\x01 \x03(\x0b\x32\x1a.v1.TriggerWorkflowRequest\"7\n\x1b\x42ulkTriggerWorkflowResponse\x12\x18\n\x10workflow_run_ids\x18\x01 \x03(\t\"2\n\x17TriggerWorkflowResponse\x12\x17\n\x0fworkflow_run_id\x18\x01 \x01(\t\"W\n\x13PutRateLimitRequest\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05limit\x18\x02 \x01(\x05\x12$\n\x08\x64uration\x18\x03 \x01(\x0e\x32\x12.RateLimitDuration\"\x16\n\x14PutRateLimitResponse*$\n\x0eStickyStrategy\x12\x08\n\x04SOFT\x10\x00\x12\x08\n\x04HARD\x10\x01*2\n\x0cWorkflowKind\x12\x0c\n\x08\x46UNCTION\x10\x00\x12\x0b\n\x07\x44URABLE\x10\x01\x12\x07\n\x03\x44\x41G\x10\x02*\x7f\n\x18\x43oncurrencyLimitStrategy\x12\x16\n\x12\x43\x41NCEL_IN_PROGRESS\x10\x00\x12\x0f\n\x0b\x44ROP_NEWEST\x10\x01\x12\x10\n\x0cQUEUE_NEWEST\x10\x02\x12\x15\n\x11GROUP_ROUND_ROBIN\x10\x03\x12\x11\n\rCANCEL_NEWEST\x10\x04*\x85\x01\n\x15WorkerLabelComparator\x12\t\n\x05\x45QUAL\x10\x00\x12\r\n\tNOT_EQUAL\x10\x01\x12\x10\n\x0cGREATER_THAN\x10\x02\x12\x19\n\x15GREATER_THAN_OR_EQUAL\x10\x03\x12\r\n\tLESS_THAN\x10\x04\x12\x16\n\x12LESS_THAN_OR_EQUAL\x10\x05*]\n\x11RateLimitDuration\x12\n\n\x06SECOND\x10\x00\x12\n\n\x06MINUTE\x10\x01\x12\x08\n\x04HOUR\x10\x02\x12\x07\n\x03\x44\x41Y\x10\x03\x12\x08\n\x04WEEK\x10\x04\x12\t\n\x05MONTH\x10\x05\x12\x08\n\x04YEAR\x10\x06\x32\xdf\x02\n\x0fWorkflowService\x12\x34\n\x0bPutWorkflow\x12\x13.PutWorkflowRequest\x1a\x10.WorkflowVersion\x12>\n\x10ScheduleWorkflow\x12\x18.ScheduleWorkflowRequest\x1a\x10.WorkflowVersion\x12G\n\x0fTriggerWorkflow\x12\x1a.v1.TriggerWorkflowRequest\x1a\x18.TriggerWorkflowResponse\x12P\n\x13\x42ulkTriggerWorkflow\x12\x1b.BulkTriggerWorkflowRequest\x1a\x1c.BulkTriggerWorkflowResponse\x12;\n\x0cPutRateLimit\x12\x14.PutRateLimitRequest\x1a\x15.PutRateLimitResponseBBZ@github.com/hatchet-dev/hatchet/internal/services/admin/contractsb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'workflows.workflows_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z@github.com/hatchet-dev/hatchet/internal/services/admin/contracts' + _globals['_CREATEWORKFLOWSTEPOPTS_WORKERLABELSENTRY']._loaded_options = None + _globals['_CREATEWORKFLOWSTEPOPTS_WORKERLABELSENTRY']._serialized_options = b'8\001' + _globals['_STICKYSTRATEGY']._serialized_start=3119 + _globals['_STICKYSTRATEGY']._serialized_end=3155 + _globals['_WORKFLOWKIND']._serialized_start=3157 + _globals['_WORKFLOWKIND']._serialized_end=3207 + _globals['_CONCURRENCYLIMITSTRATEGY']._serialized_start=3209 + _globals['_CONCURRENCYLIMITSTRATEGY']._serialized_end=3336 + _globals['_WORKERLABELCOMPARATOR']._serialized_start=3339 + _globals['_WORKERLABELCOMPARATOR']._serialized_end=3472 + _globals['_RATELIMITDURATION']._serialized_start=3474 + _globals['_RATELIMITDURATION']._serialized_end=3567 + _globals['_PUTWORKFLOWREQUEST']._serialized_start=87 + _globals['_PUTWORKFLOWREQUEST']._serialized_end=149 + _globals['_CREATEWORKFLOWVERSIONOPTS']._serialized_start=152 + _globals['_CREATEWORKFLOWVERSIONOPTS']._serialized_end=727 + _globals['_WORKFLOWCONCURRENCYOPTS']._serialized_start=730 + _globals['_WORKFLOWCONCURRENCYOPTS']._serialized_end=938 + _globals['_CREATEWORKFLOWJOBOPTS']._serialized_start=940 + _globals['_CREATEWORKFLOWJOBOPTS']._serialized_end=1044 + _globals['_DESIREDWORKERLABELS']._serialized_start=1047 + _globals['_DESIREDWORKERLABELS']._serialized_end=1276 + _globals['_CREATEWORKFLOWSTEPOPTS']._serialized_start=1279 + _globals['_CREATEWORKFLOWSTEPOPTS']._serialized_end=1716 + _globals['_CREATEWORKFLOWSTEPOPTS_WORKERLABELSENTRY']._serialized_start=1600 + _globals['_CREATEWORKFLOWSTEPOPTS_WORKERLABELSENTRY']._serialized_end=1673 + _globals['_CREATESTEPRATELIMIT']._serialized_start=1719 + _globals['_CREATESTEPRATELIMIT']._serialized_end=1969 + _globals['_LISTWORKFLOWSREQUEST']._serialized_start=1971 + _globals['_LISTWORKFLOWSREQUEST']._serialized_end=1993 + _globals['_SCHEDULEWORKFLOWREQUEST']._serialized_start=1996 + _globals['_SCHEDULEWORKFLOWREQUEST']._serialized_end=2383 + _globals['_SCHEDULEDWORKFLOW']._serialized_start=2385 + _globals['_SCHEDULEDWORKFLOW']._serialized_end=2464 + _globals['_WORKFLOWVERSION']._serialized_start=2467 + _globals['_WORKFLOWVERSION']._serialized_end=2694 + _globals['_WORKFLOWTRIGGEREVENTREF']._serialized_start=2696 + _globals['_WORKFLOWTRIGGEREVENTREF']._serialized_end=2759 + _globals['_WORKFLOWTRIGGERCRONREF']._serialized_start=2761 + _globals['_WORKFLOWTRIGGERCRONREF']._serialized_end=2818 + _globals['_BULKTRIGGERWORKFLOWREQUEST']._serialized_start=2820 + _globals['_BULKTRIGGERWORKFLOWREQUEST']._serialized_end=2895 + _globals['_BULKTRIGGERWORKFLOWRESPONSE']._serialized_start=2897 + _globals['_BULKTRIGGERWORKFLOWRESPONSE']._serialized_end=2952 + _globals['_TRIGGERWORKFLOWRESPONSE']._serialized_start=2954 + _globals['_TRIGGERWORKFLOWRESPONSE']._serialized_end=3004 + _globals['_PUTRATELIMITREQUEST']._serialized_start=3006 + _globals['_PUTRATELIMITREQUEST']._serialized_end=3093 + _globals['_PUTRATELIMITRESPONSE']._serialized_start=3095 + _globals['_PUTRATELIMITRESPONSE']._serialized_end=3117 + _globals['_WORKFLOWSERVICE']._serialized_start=3570 + _globals['_WORKFLOWSERVICE']._serialized_end=3921 +# @@protoc_insertion_point(module_scope) diff --git a/sdks/python/hatchet_sdk/contracts/workflows_pb2.pyi b/sdks/python/hatchet_sdk/contracts/workflows/workflows_pb2.pyi similarity index 91% rename from sdks/python/hatchet_sdk/contracts/workflows_pb2.pyi rename to sdks/python/hatchet_sdk/contracts/workflows/workflows_pb2.pyi index 686b99e33..2458b155d 100644 --- a/sdks/python/hatchet_sdk/contracts/workflows_pb2.pyi +++ b/sdks/python/hatchet_sdk/contracts/workflows/workflows_pb2.pyi @@ -1,6 +1,7 @@ import datetime from google.protobuf import timestamp_pb2 as _timestamp_pb2 +from hatchet_sdk.contracts.v1.shared import trigger_pb2 as _trigger_pb2 from google.protobuf.internal import containers as _containers from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper from google.protobuf import descriptor as _descriptor @@ -265,8 +266,8 @@ class WorkflowTriggerCronRef(_message.Message): class BulkTriggerWorkflowRequest(_message.Message): __slots__ = ("workflows",) WORKFLOWS_FIELD_NUMBER: _ClassVar[int] - workflows: _containers.RepeatedCompositeFieldContainer[TriggerWorkflowRequest] - def __init__(self, workflows: _Optional[_Iterable[_Union[TriggerWorkflowRequest, _Mapping]]] = ...) -> None: ... + workflows: _containers.RepeatedCompositeFieldContainer[_trigger_pb2.TriggerWorkflowRequest] + def __init__(self, workflows: _Optional[_Iterable[_Union[_trigger_pb2.TriggerWorkflowRequest, _Mapping]]] = ...) -> None: ... class BulkTriggerWorkflowResponse(_message.Message): __slots__ = ("workflow_run_ids",) @@ -274,28 +275,6 @@ class BulkTriggerWorkflowResponse(_message.Message): workflow_run_ids: _containers.RepeatedScalarFieldContainer[str] def __init__(self, workflow_run_ids: _Optional[_Iterable[str]] = ...) -> None: ... -class TriggerWorkflowRequest(_message.Message): - __slots__ = ("name", "input", "parent_id", "parent_task_run_external_id", "child_index", "child_key", "additional_metadata", "desired_worker_id", "priority") - NAME_FIELD_NUMBER: _ClassVar[int] - INPUT_FIELD_NUMBER: _ClassVar[int] - PARENT_ID_FIELD_NUMBER: _ClassVar[int] - PARENT_TASK_RUN_EXTERNAL_ID_FIELD_NUMBER: _ClassVar[int] - CHILD_INDEX_FIELD_NUMBER: _ClassVar[int] - CHILD_KEY_FIELD_NUMBER: _ClassVar[int] - ADDITIONAL_METADATA_FIELD_NUMBER: _ClassVar[int] - DESIRED_WORKER_ID_FIELD_NUMBER: _ClassVar[int] - PRIORITY_FIELD_NUMBER: _ClassVar[int] - name: str - input: str - parent_id: str - parent_task_run_external_id: str - child_index: int - child_key: str - additional_metadata: str - desired_worker_id: str - priority: int - def __init__(self, name: _Optional[str] = ..., input: _Optional[str] = ..., parent_id: _Optional[str] = ..., parent_task_run_external_id: _Optional[str] = ..., child_index: _Optional[int] = ..., child_key: _Optional[str] = ..., additional_metadata: _Optional[str] = ..., desired_worker_id: _Optional[str] = ..., priority: _Optional[int] = ...) -> None: ... - class TriggerWorkflowResponse(_message.Message): __slots__ = ("workflow_run_id",) WORKFLOW_RUN_ID_FIELD_NUMBER: _ClassVar[int] diff --git a/sdks/python/hatchet_sdk/contracts/workflows_pb2_grpc.py b/sdks/python/hatchet_sdk/contracts/workflows/workflows_pb2_grpc.py similarity index 71% rename from sdks/python/hatchet_sdk/contracts/workflows_pb2_grpc.py rename to sdks/python/hatchet_sdk/contracts/workflows/workflows_pb2_grpc.py index aa944e5df..4fe0b398f 100644 --- a/sdks/python/hatchet_sdk/contracts/workflows_pb2_grpc.py +++ b/sdks/python/hatchet_sdk/contracts/workflows/workflows_pb2_grpc.py @@ -3,7 +3,8 @@ import grpc import warnings -from hatchet_sdk.contracts import workflows_pb2 as workflows__pb2 +from hatchet_sdk.contracts.v1.shared import trigger_pb2 as v1_dot_shared_dot_trigger__pb2 +from hatchet_sdk.contracts.workflows import workflows_pb2 as workflows_dot_workflows__pb2 GRPC_GENERATED_VERSION = '1.76.0' GRPC_VERSION = grpc.__version__ @@ -18,7 +19,7 @@ except ImportError: if _version_not_supported: raise RuntimeError( f'The grpc package installed is at version {GRPC_VERSION},' - + ' but the generated code in workflows_pb2_grpc.py depends on' + + ' but the generated code in workflows/workflows_pb2_grpc.py depends on' + f' grpcio>={GRPC_GENERATED_VERSION}.' + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' @@ -37,28 +38,28 @@ class WorkflowServiceStub(object): """ self.PutWorkflow = channel.unary_unary( '/WorkflowService/PutWorkflow', - request_serializer=workflows__pb2.PutWorkflowRequest.SerializeToString, - response_deserializer=workflows__pb2.WorkflowVersion.FromString, + request_serializer=workflows_dot_workflows__pb2.PutWorkflowRequest.SerializeToString, + response_deserializer=workflows_dot_workflows__pb2.WorkflowVersion.FromString, _registered_method=True) self.ScheduleWorkflow = channel.unary_unary( '/WorkflowService/ScheduleWorkflow', - request_serializer=workflows__pb2.ScheduleWorkflowRequest.SerializeToString, - response_deserializer=workflows__pb2.WorkflowVersion.FromString, + request_serializer=workflows_dot_workflows__pb2.ScheduleWorkflowRequest.SerializeToString, + response_deserializer=workflows_dot_workflows__pb2.WorkflowVersion.FromString, _registered_method=True) self.TriggerWorkflow = channel.unary_unary( '/WorkflowService/TriggerWorkflow', - request_serializer=workflows__pb2.TriggerWorkflowRequest.SerializeToString, - response_deserializer=workflows__pb2.TriggerWorkflowResponse.FromString, + request_serializer=v1_dot_shared_dot_trigger__pb2.TriggerWorkflowRequest.SerializeToString, + response_deserializer=workflows_dot_workflows__pb2.TriggerWorkflowResponse.FromString, _registered_method=True) self.BulkTriggerWorkflow = channel.unary_unary( '/WorkflowService/BulkTriggerWorkflow', - request_serializer=workflows__pb2.BulkTriggerWorkflowRequest.SerializeToString, - response_deserializer=workflows__pb2.BulkTriggerWorkflowResponse.FromString, + request_serializer=workflows_dot_workflows__pb2.BulkTriggerWorkflowRequest.SerializeToString, + response_deserializer=workflows_dot_workflows__pb2.BulkTriggerWorkflowResponse.FromString, _registered_method=True) self.PutRateLimit = channel.unary_unary( '/WorkflowService/PutRateLimit', - request_serializer=workflows__pb2.PutRateLimitRequest.SerializeToString, - response_deserializer=workflows__pb2.PutRateLimitResponse.FromString, + request_serializer=workflows_dot_workflows__pb2.PutRateLimitRequest.SerializeToString, + response_deserializer=workflows_dot_workflows__pb2.PutRateLimitResponse.FromString, _registered_method=True) @@ -101,28 +102,28 @@ def add_WorkflowServiceServicer_to_server(servicer, server): rpc_method_handlers = { 'PutWorkflow': grpc.unary_unary_rpc_method_handler( servicer.PutWorkflow, - request_deserializer=workflows__pb2.PutWorkflowRequest.FromString, - response_serializer=workflows__pb2.WorkflowVersion.SerializeToString, + request_deserializer=workflows_dot_workflows__pb2.PutWorkflowRequest.FromString, + response_serializer=workflows_dot_workflows__pb2.WorkflowVersion.SerializeToString, ), 'ScheduleWorkflow': grpc.unary_unary_rpc_method_handler( servicer.ScheduleWorkflow, - request_deserializer=workflows__pb2.ScheduleWorkflowRequest.FromString, - response_serializer=workflows__pb2.WorkflowVersion.SerializeToString, + request_deserializer=workflows_dot_workflows__pb2.ScheduleWorkflowRequest.FromString, + response_serializer=workflows_dot_workflows__pb2.WorkflowVersion.SerializeToString, ), 'TriggerWorkflow': grpc.unary_unary_rpc_method_handler( servicer.TriggerWorkflow, - request_deserializer=workflows__pb2.TriggerWorkflowRequest.FromString, - response_serializer=workflows__pb2.TriggerWorkflowResponse.SerializeToString, + request_deserializer=v1_dot_shared_dot_trigger__pb2.TriggerWorkflowRequest.FromString, + response_serializer=workflows_dot_workflows__pb2.TriggerWorkflowResponse.SerializeToString, ), 'BulkTriggerWorkflow': grpc.unary_unary_rpc_method_handler( servicer.BulkTriggerWorkflow, - request_deserializer=workflows__pb2.BulkTriggerWorkflowRequest.FromString, - response_serializer=workflows__pb2.BulkTriggerWorkflowResponse.SerializeToString, + request_deserializer=workflows_dot_workflows__pb2.BulkTriggerWorkflowRequest.FromString, + response_serializer=workflows_dot_workflows__pb2.BulkTriggerWorkflowResponse.SerializeToString, ), 'PutRateLimit': grpc.unary_unary_rpc_method_handler( servicer.PutRateLimit, - request_deserializer=workflows__pb2.PutRateLimitRequest.FromString, - response_serializer=workflows__pb2.PutRateLimitResponse.SerializeToString, + request_deserializer=workflows_dot_workflows__pb2.PutRateLimitRequest.FromString, + response_serializer=workflows_dot_workflows__pb2.PutRateLimitResponse.SerializeToString, ), } generic_handler = grpc.method_handlers_generic_handler( @@ -151,8 +152,8 @@ class WorkflowService(object): request, target, '/WorkflowService/PutWorkflow', - workflows__pb2.PutWorkflowRequest.SerializeToString, - workflows__pb2.WorkflowVersion.FromString, + workflows_dot_workflows__pb2.PutWorkflowRequest.SerializeToString, + workflows_dot_workflows__pb2.WorkflowVersion.FromString, options, channel_credentials, insecure, @@ -178,8 +179,8 @@ class WorkflowService(object): request, target, '/WorkflowService/ScheduleWorkflow', - workflows__pb2.ScheduleWorkflowRequest.SerializeToString, - workflows__pb2.WorkflowVersion.FromString, + workflows_dot_workflows__pb2.ScheduleWorkflowRequest.SerializeToString, + workflows_dot_workflows__pb2.WorkflowVersion.FromString, options, channel_credentials, insecure, @@ -205,8 +206,8 @@ class WorkflowService(object): request, target, '/WorkflowService/TriggerWorkflow', - workflows__pb2.TriggerWorkflowRequest.SerializeToString, - workflows__pb2.TriggerWorkflowResponse.FromString, + v1_dot_shared_dot_trigger__pb2.TriggerWorkflowRequest.SerializeToString, + workflows_dot_workflows__pb2.TriggerWorkflowResponse.FromString, options, channel_credentials, insecure, @@ -232,8 +233,8 @@ class WorkflowService(object): request, target, '/WorkflowService/BulkTriggerWorkflow', - workflows__pb2.BulkTriggerWorkflowRequest.SerializeToString, - workflows__pb2.BulkTriggerWorkflowResponse.FromString, + workflows_dot_workflows__pb2.BulkTriggerWorkflowRequest.SerializeToString, + workflows_dot_workflows__pb2.BulkTriggerWorkflowResponse.FromString, options, channel_credentials, insecure, @@ -259,8 +260,8 @@ class WorkflowService(object): request, target, '/WorkflowService/PutRateLimit', - workflows__pb2.PutRateLimitRequest.SerializeToString, - workflows__pb2.PutRateLimitResponse.FromString, + workflows_dot_workflows__pb2.PutRateLimitRequest.SerializeToString, + workflows_dot_workflows__pb2.PutRateLimitResponse.FromString, options, channel_credentials, insecure, diff --git a/sdks/python/hatchet_sdk/contracts/workflows_pb2.py b/sdks/python/hatchet_sdk/contracts/workflows_pb2.py deleted file mode 100644 index 0a7e6d53f..000000000 --- a/sdks/python/hatchet_sdk/contracts/workflows_pb2.py +++ /dev/null @@ -1,90 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# NO CHECKED-IN PROTOBUF GENCODE -# source: workflows.proto -# Protobuf Python Version: 6.31.1 -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import runtime_version as _runtime_version -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder -_runtime_version.ValidateProtobufRuntimeVersion( - _runtime_version.Domain.PUBLIC, - 6, - 31, - 1, - '', - 'workflows.proto' -) -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0fworkflows.proto\x1a\x1fgoogle/protobuf/timestamp.proto\">\n\x12PutWorkflowRequest\x12(\n\x04opts\x18\x01 \x01(\x0b\x32\x1a.CreateWorkflowVersionOpts\"\xbf\x04\n\x19\x43reateWorkflowVersionOpts\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x16\n\x0e\x65vent_triggers\x18\x04 \x03(\t\x12\x15\n\rcron_triggers\x18\x05 \x03(\t\x12\x36\n\x12scheduled_triggers\x18\x06 \x03(\x0b\x32\x1a.google.protobuf.Timestamp\x12$\n\x04jobs\x18\x07 \x03(\x0b\x32\x16.CreateWorkflowJobOpts\x12-\n\x0b\x63oncurrency\x18\x08 \x01(\x0b\x32\x18.WorkflowConcurrencyOpts\x12\x1d\n\x10schedule_timeout\x18\t \x01(\tH\x00\x88\x01\x01\x12\x17\n\ncron_input\x18\n \x01(\tH\x01\x88\x01\x01\x12\x33\n\x0eon_failure_job\x18\x0b \x01(\x0b\x32\x16.CreateWorkflowJobOptsH\x02\x88\x01\x01\x12$\n\x06sticky\x18\x0c \x01(\x0e\x32\x0f.StickyStrategyH\x03\x88\x01\x01\x12 \n\x04kind\x18\r \x01(\x0e\x32\r.WorkflowKindH\x04\x88\x01\x01\x12\x1d\n\x10\x64\x65\x66\x61ult_priority\x18\x0e \x01(\x05H\x05\x88\x01\x01\x42\x13\n\x11_schedule_timeoutB\r\n\x0b_cron_inputB\x11\n\x0f_on_failure_jobB\t\n\x07_stickyB\x07\n\x05_kindB\x13\n\x11_default_priority\"\xd0\x01\n\x17WorkflowConcurrencyOpts\x12\x13\n\x06\x61\x63tion\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08max_runs\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12\x36\n\x0elimit_strategy\x18\x03 \x01(\x0e\x32\x19.ConcurrencyLimitStrategyH\x02\x88\x01\x01\x12\x17\n\nexpression\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\t\n\x07_actionB\x0b\n\t_max_runsB\x11\n\x0f_limit_strategyB\r\n\x0b_expression\"h\n\x15\x43reateWorkflowJobOpts\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12&\n\x05steps\x18\x04 \x03(\x0b\x32\x17.CreateWorkflowStepOptsJ\x04\x08\x03\x10\x04\"\xe5\x01\n\x13\x44\x65siredWorkerLabels\x12\x16\n\tstr_value\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tint_value\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12\x15\n\x08required\x18\x03 \x01(\x08H\x02\x88\x01\x01\x12/\n\ncomparator\x18\x04 \x01(\x0e\x32\x16.WorkerLabelComparatorH\x03\x88\x01\x01\x12\x13\n\x06weight\x18\x05 \x01(\x05H\x04\x88\x01\x01\x42\x0c\n\n_str_valueB\x0c\n\n_int_valueB\x0b\n\t_requiredB\r\n\x0b_comparatorB\t\n\x07_weight\"\xb5\x03\n\x16\x43reateWorkflowStepOpts\x12\x13\n\x0breadable_id\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tion\x18\x02 \x01(\t\x12\x0f\n\x07timeout\x18\x03 \x01(\t\x12\x0e\n\x06inputs\x18\x04 \x01(\t\x12\x0f\n\x07parents\x18\x05 \x03(\t\x12\x11\n\tuser_data\x18\x06 \x01(\t\x12\x0f\n\x07retries\x18\x07 \x01(\x05\x12)\n\x0brate_limits\x18\x08 \x03(\x0b\x32\x14.CreateStepRateLimit\x12@\n\rworker_labels\x18\t \x03(\x0b\x32).CreateWorkflowStepOpts.WorkerLabelsEntry\x12\x1b\n\x0e\x62\x61\x63koff_factor\x18\n \x01(\x02H\x00\x88\x01\x01\x12 \n\x13\x62\x61\x63koff_max_seconds\x18\x0b \x01(\x05H\x01\x88\x01\x01\x1aI\n\x11WorkerLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12#\n\x05value\x18\x02 \x01(\x0b\x32\x14.DesiredWorkerLabels:\x02\x38\x01\x42\x11\n\x0f_backoff_factorB\x16\n\x14_backoff_max_seconds\"\xfa\x01\n\x13\x43reateStepRateLimit\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x12\n\x05units\x18\x02 \x01(\x05H\x00\x88\x01\x01\x12\x15\n\x08key_expr\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nunits_expr\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1e\n\x11limit_values_expr\x18\x05 \x01(\tH\x03\x88\x01\x01\x12)\n\x08\x64uration\x18\x06 \x01(\x0e\x32\x12.RateLimitDurationH\x04\x88\x01\x01\x42\x08\n\x06_unitsB\x0b\n\t_key_exprB\r\n\x0b_units_exprB\x14\n\x12_limit_values_exprB\x0b\n\t_duration\"\x16\n\x14ListWorkflowsRequest\"\x83\x03\n\x17ScheduleWorkflowRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12-\n\tschedules\x18\x02 \x03(\x0b\x32\x1a.google.protobuf.Timestamp\x12\r\n\x05input\x18\x03 \x01(\t\x12\x16\n\tparent_id\x18\x04 \x01(\tH\x00\x88\x01\x01\x12(\n\x1bparent_task_run_external_id\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0b\x63hild_index\x18\x06 \x01(\x05H\x02\x88\x01\x01\x12\x16\n\tchild_key\x18\x07 \x01(\tH\x03\x88\x01\x01\x12 \n\x13\x61\x64\x64itional_metadata\x18\x08 \x01(\tH\x04\x88\x01\x01\x12\x15\n\x08priority\x18\t \x01(\x05H\x05\x88\x01\x01\x42\x0c\n\n_parent_idB\x1e\n\x1c_parent_task_run_external_idB\x0e\n\x0c_child_indexB\x0c\n\n_child_keyB\x16\n\x14_additional_metadataB\x0b\n\t_priority\"O\n\x11ScheduledWorkflow\x12\n\n\x02id\x18\x01 \x01(\t\x12.\n\ntrigger_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"\xe3\x01\n\x0fWorkflowVersion\x12\n\n\x02id\x18\x01 \x01(\t\x12.\n\ncreated_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nupdated_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0f\n\x07version\x18\x05 \x01(\t\x12\r\n\x05order\x18\x06 \x01(\x03\x12\x13\n\x0bworkflow_id\x18\x07 \x01(\t\x12/\n\x13scheduled_workflows\x18\x08 \x03(\x0b\x32\x12.ScheduledWorkflow\"?\n\x17WorkflowTriggerEventRef\x12\x11\n\tparent_id\x18\x01 \x01(\t\x12\x11\n\tevent_key\x18\x02 \x01(\t\"9\n\x16WorkflowTriggerCronRef\x12\x11\n\tparent_id\x18\x01 \x01(\t\x12\x0c\n\x04\x63ron\x18\x02 \x01(\t\"H\n\x1a\x42ulkTriggerWorkflowRequest\x12*\n\tworkflows\x18\x01 \x03(\x0b\x32\x17.TriggerWorkflowRequest\"7\n\x1b\x42ulkTriggerWorkflowResponse\x12\x18\n\x10workflow_run_ids\x18\x01 \x03(\t\"\x89\x03\n\x16TriggerWorkflowRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05input\x18\x02 \x01(\t\x12\x16\n\tparent_id\x18\x03 \x01(\tH\x00\x88\x01\x01\x12(\n\x1bparent_task_run_external_id\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0b\x63hild_index\x18\x05 \x01(\x05H\x02\x88\x01\x01\x12\x16\n\tchild_key\x18\x06 \x01(\tH\x03\x88\x01\x01\x12 \n\x13\x61\x64\x64itional_metadata\x18\x07 \x01(\tH\x04\x88\x01\x01\x12\x1e\n\x11\x64\x65sired_worker_id\x18\x08 \x01(\tH\x05\x88\x01\x01\x12\x15\n\x08priority\x18\t \x01(\x05H\x06\x88\x01\x01\x42\x0c\n\n_parent_idB\x1e\n\x1c_parent_task_run_external_idB\x0e\n\x0c_child_indexB\x0c\n\n_child_keyB\x16\n\x14_additional_metadataB\x14\n\x12_desired_worker_idB\x0b\n\t_priority\"2\n\x17TriggerWorkflowResponse\x12\x17\n\x0fworkflow_run_id\x18\x01 \x01(\t\"W\n\x13PutRateLimitRequest\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05limit\x18\x02 \x01(\x05\x12$\n\x08\x64uration\x18\x03 \x01(\x0e\x32\x12.RateLimitDuration\"\x16\n\x14PutRateLimitResponse*$\n\x0eStickyStrategy\x12\x08\n\x04SOFT\x10\x00\x12\x08\n\x04HARD\x10\x01*2\n\x0cWorkflowKind\x12\x0c\n\x08\x46UNCTION\x10\x00\x12\x0b\n\x07\x44URABLE\x10\x01\x12\x07\n\x03\x44\x41G\x10\x02*\x7f\n\x18\x43oncurrencyLimitStrategy\x12\x16\n\x12\x43\x41NCEL_IN_PROGRESS\x10\x00\x12\x0f\n\x0b\x44ROP_NEWEST\x10\x01\x12\x10\n\x0cQUEUE_NEWEST\x10\x02\x12\x15\n\x11GROUP_ROUND_ROBIN\x10\x03\x12\x11\n\rCANCEL_NEWEST\x10\x04*\x85\x01\n\x15WorkerLabelComparator\x12\t\n\x05\x45QUAL\x10\x00\x12\r\n\tNOT_EQUAL\x10\x01\x12\x10\n\x0cGREATER_THAN\x10\x02\x12\x19\n\x15GREATER_THAN_OR_EQUAL\x10\x03\x12\r\n\tLESS_THAN\x10\x04\x12\x16\n\x12LESS_THAN_OR_EQUAL\x10\x05*]\n\x11RateLimitDuration\x12\n\n\x06SECOND\x10\x00\x12\n\n\x06MINUTE\x10\x01\x12\x08\n\x04HOUR\x10\x02\x12\x07\n\x03\x44\x41Y\x10\x03\x12\x08\n\x04WEEK\x10\x04\x12\t\n\x05MONTH\x10\x05\x12\x08\n\x04YEAR\x10\x06\x32\xdc\x02\n\x0fWorkflowService\x12\x34\n\x0bPutWorkflow\x12\x13.PutWorkflowRequest\x1a\x10.WorkflowVersion\x12>\n\x10ScheduleWorkflow\x12\x18.ScheduleWorkflowRequest\x1a\x10.WorkflowVersion\x12\x44\n\x0fTriggerWorkflow\x12\x17.TriggerWorkflowRequest\x1a\x18.TriggerWorkflowResponse\x12P\n\x13\x42ulkTriggerWorkflow\x12\x1b.BulkTriggerWorkflowRequest\x1a\x1c.BulkTriggerWorkflowResponse\x12;\n\x0cPutRateLimit\x12\x14.PutRateLimitRequest\x1a\x15.PutRateLimitResponseBBZ@github.com/hatchet-dev/hatchet/internal/services/admin/contractsb\x06proto3') - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'workflows_pb2', _globals) -if not _descriptor._USE_C_DESCRIPTORS: - _globals['DESCRIPTOR']._loaded_options = None - _globals['DESCRIPTOR']._serialized_options = b'Z@github.com/hatchet-dev/hatchet/internal/services/admin/contracts' - _globals['_CREATEWORKFLOWSTEPOPTS_WORKERLABELSENTRY']._loaded_options = None - _globals['_CREATEWORKFLOWSTEPOPTS_WORKERLABELSENTRY']._serialized_options = b'8\001' - _globals['_STICKYSTRATEGY']._serialized_start=3477 - _globals['_STICKYSTRATEGY']._serialized_end=3513 - _globals['_WORKFLOWKIND']._serialized_start=3515 - _globals['_WORKFLOWKIND']._serialized_end=3565 - _globals['_CONCURRENCYLIMITSTRATEGY']._serialized_start=3567 - _globals['_CONCURRENCYLIMITSTRATEGY']._serialized_end=3694 - _globals['_WORKERLABELCOMPARATOR']._serialized_start=3697 - _globals['_WORKERLABELCOMPARATOR']._serialized_end=3830 - _globals['_RATELIMITDURATION']._serialized_start=3832 - _globals['_RATELIMITDURATION']._serialized_end=3925 - _globals['_PUTWORKFLOWREQUEST']._serialized_start=52 - _globals['_PUTWORKFLOWREQUEST']._serialized_end=114 - _globals['_CREATEWORKFLOWVERSIONOPTS']._serialized_start=117 - _globals['_CREATEWORKFLOWVERSIONOPTS']._serialized_end=692 - _globals['_WORKFLOWCONCURRENCYOPTS']._serialized_start=695 - _globals['_WORKFLOWCONCURRENCYOPTS']._serialized_end=903 - _globals['_CREATEWORKFLOWJOBOPTS']._serialized_start=905 - _globals['_CREATEWORKFLOWJOBOPTS']._serialized_end=1009 - _globals['_DESIREDWORKERLABELS']._serialized_start=1012 - _globals['_DESIREDWORKERLABELS']._serialized_end=1241 - _globals['_CREATEWORKFLOWSTEPOPTS']._serialized_start=1244 - _globals['_CREATEWORKFLOWSTEPOPTS']._serialized_end=1681 - _globals['_CREATEWORKFLOWSTEPOPTS_WORKERLABELSENTRY']._serialized_start=1565 - _globals['_CREATEWORKFLOWSTEPOPTS_WORKERLABELSENTRY']._serialized_end=1638 - _globals['_CREATESTEPRATELIMIT']._serialized_start=1684 - _globals['_CREATESTEPRATELIMIT']._serialized_end=1934 - _globals['_LISTWORKFLOWSREQUEST']._serialized_start=1936 - _globals['_LISTWORKFLOWSREQUEST']._serialized_end=1958 - _globals['_SCHEDULEWORKFLOWREQUEST']._serialized_start=1961 - _globals['_SCHEDULEWORKFLOWREQUEST']._serialized_end=2348 - _globals['_SCHEDULEDWORKFLOW']._serialized_start=2350 - _globals['_SCHEDULEDWORKFLOW']._serialized_end=2429 - _globals['_WORKFLOWVERSION']._serialized_start=2432 - _globals['_WORKFLOWVERSION']._serialized_end=2659 - _globals['_WORKFLOWTRIGGEREVENTREF']._serialized_start=2661 - _globals['_WORKFLOWTRIGGEREVENTREF']._serialized_end=2724 - _globals['_WORKFLOWTRIGGERCRONREF']._serialized_start=2726 - _globals['_WORKFLOWTRIGGERCRONREF']._serialized_end=2783 - _globals['_BULKTRIGGERWORKFLOWREQUEST']._serialized_start=2785 - _globals['_BULKTRIGGERWORKFLOWREQUEST']._serialized_end=2857 - _globals['_BULKTRIGGERWORKFLOWRESPONSE']._serialized_start=2859 - _globals['_BULKTRIGGERWORKFLOWRESPONSE']._serialized_end=2914 - _globals['_TRIGGERWORKFLOWREQUEST']._serialized_start=2917 - _globals['_TRIGGERWORKFLOWREQUEST']._serialized_end=3310 - _globals['_TRIGGERWORKFLOWRESPONSE']._serialized_start=3312 - _globals['_TRIGGERWORKFLOWRESPONSE']._serialized_end=3362 - _globals['_PUTRATELIMITREQUEST']._serialized_start=3364 - _globals['_PUTRATELIMITREQUEST']._serialized_end=3451 - _globals['_PUTRATELIMITRESPONSE']._serialized_start=3453 - _globals['_PUTRATELIMITRESPONSE']._serialized_end=3475 - _globals['_WORKFLOWSERVICE']._serialized_start=3928 - _globals['_WORKFLOWSERVICE']._serialized_end=4276 -# @@protoc_insertion_point(module_scope) diff --git a/sdks/python/hatchet_sdk/features/rate_limits.py b/sdks/python/hatchet_sdk/features/rate_limits.py index 418a44a82..12c60ebc1 100644 --- a/sdks/python/hatchet_sdk/features/rate_limits.py +++ b/sdks/python/hatchet_sdk/features/rate_limits.py @@ -3,9 +3,9 @@ import asyncio from hatchet_sdk.clients.rest.tenacity_utils import tenacity_retry from hatchet_sdk.clients.v1.api_client import BaseRestClient from hatchet_sdk.connection import new_conn -from hatchet_sdk.contracts import workflows_pb2 as v0_workflow_protos from hatchet_sdk.contracts.v1 import workflows_pb2 as workflow_protos -from hatchet_sdk.contracts.workflows_pb2_grpc import WorkflowServiceStub +from hatchet_sdk.contracts.workflows import workflows_pb2 as v0_workflow_protos +from hatchet_sdk.contracts.workflows.workflows_pb2_grpc import WorkflowServiceStub from hatchet_sdk.metadata import get_metadata from hatchet_sdk.rate_limit import RateLimitDuration from hatchet_sdk.utils.proto_enums import convert_python_enum_to_proto diff --git a/sdks/python/hatchet_sdk/opentelemetry/instrumentor.py b/sdks/python/hatchet_sdk/opentelemetry/instrumentor.py index f2848cfe8..e6de5101b 100644 --- a/sdks/python/hatchet_sdk/opentelemetry/instrumentor.py +++ b/sdks/python/hatchet_sdk/opentelemetry/instrumentor.py @@ -3,7 +3,7 @@ from collections.abc import Callable, Collection, Coroutine from importlib.metadata import version from typing import Any, cast -from hatchet_sdk.contracts import workflows_pb2 as v0_workflow_protos +from hatchet_sdk.contracts.workflows import workflows_pb2 as v0_workflow_protos from hatchet_sdk.utils.typing import JSONSerializableMapping try: diff --git a/sdks/python/hatchet_sdk/runnables/contextvars.py b/sdks/python/hatchet_sdk/runnables/contextvars.py index 0d3c9d490..43a2525c1 100644 --- a/sdks/python/hatchet_sdk/runnables/contextvars.py +++ b/sdks/python/hatchet_sdk/runnables/contextvars.py @@ -2,10 +2,15 @@ import asyncio import threading from collections import Counter from contextvars import ContextVar +from typing import TYPE_CHECKING from hatchet_sdk.runnables.action import ActionKey from hatchet_sdk.utils.typing import JSONSerializableMapping +if TYPE_CHECKING: + from hatchet_sdk.clients.admin import AdminClient + from hatchet_sdk.context.context import DurableContext + ctx_workflow_run_id: ContextVar[str | None] = ContextVar( "ctx_workflow_run_id", default=None ) @@ -20,6 +25,12 @@ ctx_additional_metadata: ContextVar[JSONSerializableMapping | None] = ContextVar ctx_task_retry_count: ContextVar[int | None] = ContextVar( "ctx_task_retry_count", default=0 ) +ctx_durable_context: "ContextVar[DurableContext | None]" = ContextVar( + "ctx_durable_context", default=None +) +ctx_admin_client: "ContextVar[AdminClient | None]" = ContextVar( + "ctx_admin_client", default=None +) workflow_spawn_indices = Counter[ActionKey]() spawn_index_lock = asyncio.Lock() diff --git a/sdks/python/hatchet_sdk/runnables/task.py b/sdks/python/hatchet_sdk/runnables/task.py index c14d69174..5a9ccd773 100644 --- a/sdks/python/hatchet_sdk/runnables/task.py +++ b/sdks/python/hatchet_sdk/runnables/task.py @@ -43,6 +43,7 @@ from hatchet_sdk.contracts.v1.workflows_pb2 import ( DesiredWorkerLabels, ) from hatchet_sdk.exceptions import InvalidDependencyError +from hatchet_sdk.logger import logger from hatchet_sdk.runnables.types import ( ConcurrencyExpression, R, @@ -180,6 +181,11 @@ class Task(Generic[TWorkflowInput, R]): step_output=TypeAdapter(normalize_validator(return_type)), ) + if not self.is_async_function and self.is_durable: + logger.warning( + f"{self.fn.__name__} is defined as a synchronous, durable task. in the future, durable tasks will only support `async`. please update this durable task to be async, or make it non-durable." + ) + async def _parse_maybe_cm_param( self, parsed: DependencyToInject, diff --git a/sdks/python/hatchet_sdk/runnables/types.py b/sdks/python/hatchet_sdk/runnables/types.py index e8f51c98e..1bcc08184 100644 --- a/sdks/python/hatchet_sdk/runnables/types.py +++ b/sdks/python/hatchet_sdk/runnables/types.py @@ -1,13 +1,19 @@ -import asyncio import inspect import json from collections.abc import Callable, Mapping from enum import Enum -from typing import Any, ParamSpec, TypeAlias, TypeGuard, TypeVar, overload +from typing import ( + TYPE_CHECKING, + Any, + ParamSpec, + TypeAlias, + TypeGuard, + TypeVar, + overload, +) from pydantic import BaseModel, ConfigDict, Field, TypeAdapter -from hatchet_sdk.context.context import Context, DurableContext from hatchet_sdk.contracts.v1.workflows_pb2 import Concurrency from hatchet_sdk.contracts.v1.workflows_pb2 import DefaultFilter as DefaultFilterProto from hatchet_sdk.utils.timedelta_to_expression import Duration @@ -17,6 +23,10 @@ from hatchet_sdk.utils.typing import ( JSONSerializableMapping, ) +if TYPE_CHECKING: + from hatchet_sdk.context.context import Context, DurableContext + + ValidTaskReturnType = BaseModel | Mapping[str, Any] | DataclassInstance | None R = TypeVar("R", bound=ValidTaskReturnType) @@ -140,15 +150,15 @@ class StepType(str, Enum): ON_SUCCESS = "on_success" -AsyncFunc = Callable[[TWorkflowInput, Context], AwaitableLike[R]] -SyncFunc = Callable[[TWorkflowInput, Context], R] +AsyncFunc = Callable[[TWorkflowInput, "Context"], AwaitableLike[R]] +SyncFunc = Callable[[TWorkflowInput, "Context"], R] TaskFunc = AsyncFunc[TWorkflowInput, R] | SyncFunc[TWorkflowInput, R] def is_async_fn( fn: TaskFunc[TWorkflowInput, R], ) -> TypeGuard[AsyncFunc[TWorkflowInput, R]]: - return asyncio.iscoroutinefunction(fn) + return inspect.iscoroutinefunction(fn) def is_sync_fn( @@ -157,8 +167,8 @@ def is_sync_fn( return not inspect.iscoroutinefunction(fn) -DurableAsyncFunc = Callable[[TWorkflowInput, DurableContext], AwaitableLike[R]] -DurableSyncFunc = Callable[[TWorkflowInput, DurableContext], R] +DurableAsyncFunc = Callable[[TWorkflowInput, "DurableContext"], AwaitableLike[R]] +DurableSyncFunc = Callable[[TWorkflowInput, "DurableContext"], R] DurableTaskFunc = ( DurableAsyncFunc[TWorkflowInput, R] | DurableSyncFunc[TWorkflowInput, R] ) diff --git a/sdks/python/hatchet_sdk/runnables/workflow.py b/sdks/python/hatchet_sdk/runnables/workflow.py index 6eed17bbe..47bfb90d0 100644 --- a/sdks/python/hatchet_sdk/runnables/workflow.py +++ b/sdks/python/hatchet_sdk/runnables/workflow.py @@ -36,9 +36,10 @@ from hatchet_sdk.contracts.v1.workflows_pb2 import ( DesiredWorkerLabels, ) from hatchet_sdk.contracts.v1.workflows_pb2 import StickyStrategy as StickyStrategyProto -from hatchet_sdk.contracts.workflows_pb2 import WorkflowVersion +from hatchet_sdk.contracts.workflows.workflows_pb2 import WorkflowVersion from hatchet_sdk.labels import DesiredWorkerLabel from hatchet_sdk.rate_limit import RateLimit +from hatchet_sdk.runnables.contextvars import ctx_durable_context from hatchet_sdk.runnables.task import Task from hatchet_sdk.runnables.types import ( ConcurrencyExpression, @@ -705,6 +706,10 @@ class Workflow(BaseWorkflow[TWorkflowInput]): :returns: The result of the workflow execution as a dictionary. """ + durable_ctx = ctx_durable_context.get() + if durable_ctx is not None: + return await durable_ctx._spawn_child(self, input, options) + ref = await self.client._client.admin.aio_run_workflow( workflow_name=self.config.name, input=self._serialize_input(input), @@ -1311,6 +1316,19 @@ class Standalone(BaseWorkflow[TWorkflowInput], Generic[TWorkflowInput, R]): :returns: The extracted result of the workflow execution. """ + from hatchet_sdk.runnables.contextvars import ctx_durable_context + from hatchet_sdk.serde import HATCHET_PYDANTIC_SENTINEL + + durable_ctx = ctx_durable_context.get() + if durable_ctx is not None: + raw = await durable_ctx._spawn_child(self, input, options) + return cast( + R, + self._output_validator.validate_python( + raw, context=HATCHET_PYDANTIC_SENTINEL + ), + ) + result = await self._workflow.aio_run(input, options) return self._extract_result(result) diff --git a/sdks/python/hatchet_sdk/worker/runner/run_loop_manager.py b/sdks/python/hatchet_sdk/worker/runner/run_loop_manager.py index 154b5f22c..f2e2dc57a 100644 --- a/sdks/python/hatchet_sdk/worker/runner/run_loop_manager.py +++ b/sdks/python/hatchet_sdk/worker/runner/run_loop_manager.py @@ -30,6 +30,7 @@ class WorkerActionRunLoopManager: debug: bool, labels: dict[str, str | int] | None, lifespan_context: Any | None, + is_durable: bool = False, ) -> None: self.name = name self.action_registry = action_registry @@ -46,6 +47,7 @@ class WorkerActionRunLoopManager: if self.debug: logger.setLevel(logging.DEBUG) + self.is_durable = is_durable self.killing = False self.runner: Runner | None = None @@ -96,6 +98,7 @@ class WorkerActionRunLoopManager: self.labels, self.lifespan_context, self.log_sender, + is_durable=self.is_durable, ) logger.debug(f"'{self.name}' waiting for {list(self.action_registry.keys())}") diff --git a/sdks/python/hatchet_sdk/worker/runner/runner.py b/sdks/python/hatchet_sdk/worker/runner/runner.py index d853858b4..87461a100 100644 --- a/sdks/python/hatchet_sdk/worker/runner/runner.py +++ b/sdks/python/hatchet_sdk/worker/runner/runner.py @@ -17,7 +17,9 @@ from hatchet_sdk.client import Client from hatchet_sdk.clients.admin import AdminClient from hatchet_sdk.clients.dispatcher.dispatcher import DispatcherClient from hatchet_sdk.clients.events import EventClient -from hatchet_sdk.clients.listeners.durable_event_listener import DurableEventListener +from hatchet_sdk.clients.listeners.durable_event_listener import ( + DurableEventListener, +) from hatchet_sdk.clients.listeners.run_event_listener import RunEventListenerClient from hatchet_sdk.clients.listeners.workflow_listener import PooledWorkflowRunListener from hatchet_sdk.config import ClientConfig @@ -39,6 +41,8 @@ from hatchet_sdk.runnables.action import Action, ActionKey, ActionType from hatchet_sdk.runnables.contextvars import ( ctx_action_key, ctx_additional_metadata, + ctx_admin_client, + ctx_durable_context, ctx_step_run_id, ctx_task_retry_count, ctx_worker_id, @@ -82,6 +86,7 @@ class Runner: labels: dict[str, str | int] | None, lifespan_context: Any | None, log_sender: AsyncLogSender, + is_durable: bool = False, ): # We store the config so we can dynamically create clients for the dispatcher client. self.config = config @@ -101,6 +106,7 @@ class Runner: self.killing = False self.handle_kill = handle_kill + self.is_durable = is_durable self.dispatcher_client = DispatcherClient(self.config) self.workflow_run_event_listener = RunEventListenerClient(self.config) @@ -118,7 +124,9 @@ class Runner: admin_client=self.admin_client, ) self.event_client = EventClient(self.config) - self.durable_event_listener = DurableEventListener(self.config) + self.durable_event_listener = DurableEventListener( + self.config, admin_client=self.admin_client + ) self.worker_context = WorkerContext( labels=labels or {}, client=Client(config=config).dispatcher @@ -136,6 +144,10 @@ class Runner: def run(self, action: Action) -> None: if self.worker_context.id() is None: self.worker_context._worker_id = action.worker_id + if self.is_durable: + self.durable_event_listener_task = asyncio.create_task( + self.durable_event_listener.ensure_started(action.worker_id) + ) t: asyncio.Task[Exception | None] | None = None match action.action_type: @@ -251,6 +263,10 @@ class Runner: ctx_action_key.set(action.key) ctx_additional_metadata.set(action.additional_metadata) ctx_task_retry_count.set(action.retry_count) + ctx_admin_client.set(self.admin_client) + ctx_durable_context.set( + ctx if isinstance(ctx, DurableContext) and task.is_durable else None + ) async with task._unpack_dependencies_with_cleanup(ctx) as dependencies: try: diff --git a/sdks/python/hatchet_sdk/worker/worker.py b/sdks/python/hatchet_sdk/worker/worker.py index 331af7504..af1741b91 100644 --- a/sdks/python/hatchet_sdk/worker/worker.py +++ b/sdks/python/hatchet_sdk/worker/worker.py @@ -287,6 +287,7 @@ class Worker: self.client.debug, self.labels, lifespan_context, + is_durable=is_durable, ) raise RuntimeError("event loop not set, cannot start action runner") diff --git a/sdks/python/poetry.lock b/sdks/python/poetry.lock index 52e04fa20..0a2e8e57d 100644 --- a/sdks/python/poetry.lock +++ b/sdks/python/poetry.lock @@ -228,6 +228,19 @@ files = [ {file = "attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11"}, ] +[[package]] +name = "backports-asyncio-runner" +version = "1.2.0" +description = "Backport of asyncio.Runner, a context manager that controls event loop life cycle." +optional = false +python-versions = "<3.11,>=3.8" +groups = ["test"] +markers = "python_version < \"3.11\"" +files = [ + {file = "backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5"}, + {file = "backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162"}, +] + [[package]] name = "beautifulsoup4" version = "4.14.3" @@ -2516,18 +2529,20 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests [[package]] name = "pytest-asyncio" -version = "0.25.3" +version = "1.3.0" description = "Pytest support for asyncio" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["test"] files = [ - {file = "pytest_asyncio-0.25.3-py3-none-any.whl", hash = "sha256:9e89518e0f9bd08928f97a3482fdc4e244df17529460bc038291ccaf8f85c7c3"}, - {file = "pytest_asyncio-0.25.3.tar.gz", hash = "sha256:fc1da2cf9f125ada7e710b4ddad05518d4cee187ae9412e9ac9271003497f07a"}, + {file = "pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5"}, + {file = "pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5"}, ] [package.dependencies] -pytest = ">=8.2,<9" +backports-asyncio-runner = {version = ">=1.1,<2", markers = "python_version < \"3.11\""} +pytest = ">=8.2,<10" +typing-extensions = {version = ">=4.12", markers = "python_version < \"3.13\""} [package.extras] docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1)"] @@ -3470,4 +3485,4 @@ v0-sdk = [] [metadata] lock-version = "2.1" python-versions = "^3.10" -content-hash = "258e0c23b44c9d791c366483aa941e1ffda6ae89af9f18e35b1adb8c70f139ff" +content-hash = "7a99f44ebe9b146b5d8be33aaa758a0860f0481a575e850011726d1b2f54e663" diff --git a/sdks/python/pyproject.toml b/sdks/python/pyproject.toml index 89c49d4ef..a2d84e8d1 100644 --- a/sdks/python/pyproject.toml +++ b/sdks/python/pyproject.toml @@ -46,7 +46,7 @@ types-grpcio = "^1.0.0" [tool.poetry.group.test.dependencies] pytest = "^8.3.5" -pytest-asyncio = "^0.25.3" +pytest-asyncio = "^1.3.0" pytest-env = "^1.1.5" pytest-retry = "^1.7.0" psycopg = { extras = ["pool"], version = "^3.2.6" } diff --git a/sql/schema/v0.sql b/sql/schema/v0.sql index 08ce1d671..b75766808 100644 --- a/sql/schema/v0.sql +++ b/sql/schema/v0.sql @@ -861,6 +861,7 @@ CREATE TABLE "Worker" ( "os" TEXT, "runtimeExtra" TEXT, "sdkVersion" TEXT, + "durableTaskDispatcherId" UUID, CONSTRAINT "Worker_pkey" PRIMARY KEY ("id") ); diff --git a/sql/schema/v1-core.sql b/sql/schema/v1-core.sql index 92a4bc553..2005e812f 100644 --- a/sql/schema/v1-core.sql +++ b/sql/schema/v1-core.sql @@ -504,6 +504,10 @@ CREATE TABLE v1_match ( trigger_existing_task_id bigint, trigger_existing_task_inserted_at timestamptz, trigger_priority integer, + durable_event_log_callback_durable_task_external_id uuid, + durable_event_log_callback_durable_task_id bigint, + durable_event_log_callback_durable_task_inserted_at timestamptz, + durable_event_log_callback_node_id bigint, CONSTRAINT v1_match_pkey PRIMARY KEY (id) ); @@ -1650,7 +1654,7 @@ CREATE TABLE v1_durable_sleep ( PRIMARY KEY (tenant_id, sleep_until, id) ); -CREATE TYPE v1_payload_type AS ENUM ('TASK_INPUT', 'DAG_INPUT', 'TASK_OUTPUT', 'TASK_EVENT_DATA', 'USER_EVENT_INPUT', 'DURABLE_EVENT_LOG_ENTRY_DATA'); +CREATE TYPE v1_payload_type AS ENUM ('TASK_INPUT', 'DAG_INPUT', 'TASK_OUTPUT', 'TASK_EVENT_DATA', 'USER_EVENT_INPUT', 'DURABLE_EVENT_LOG_ENTRY_DATA', 'DURABLE_EVENT_LOG_CALLBACK_RESULT_DATA'); -- IMPORTANT: Keep these values in sync with `v1_payload_type_olap` in the OLAP db CREATE TYPE v1_payload_location AS ENUM ('INLINE', 'EXTERNAL'); @@ -2225,9 +2229,14 @@ CREATE TABLE v1_event_to_run ( -- -- Important: writers to v1_durable_event_log_entry should lock this row to increment the sequence value. CREATE TABLE v1_durable_event_log_file ( + tenant_id UUID NOT NULL, + -- The id and inserted_at of the durable task which created this entry durable_task_id BIGINT NOT NULL, durable_task_inserted_at TIMESTAMPTZ NOT NULL, + + latest_invocation_count BIGINT NOT NULL, + latest_inserted_at TIMESTAMPTZ NOT NULL, -- A monotonically increasing node id for this durable event log scoped to the durable task. -- Starts at 0 and increments by 1 for each new entry. @@ -2239,13 +2248,15 @@ CREATE TABLE v1_durable_event_log_file ( CONSTRAINT v1_durable_event_log_file_pkey PRIMARY KEY (durable_task_id, durable_task_inserted_at) ) PARTITION BY RANGE(durable_task_inserted_at); -CREATE TYPE v1_durable_event_log_entry_kind AS ENUM ( - 'RUN_TRIGGERED', - 'WAIT_FOR_STARTED', - 'MEMO_STARTED' +CREATE TYPE v1_durable_event_log_kind AS ENUM ( + 'RUN', + 'WAIT_FOR', + 'MEMO' ); CREATE TABLE v1_durable_event_log_entry ( + tenant_id UUID NOT NULL, + -- need an external id for consistency with the payload store logic (unfortunately) external_id UUID NOT NULL, -- The id and inserted_at of the durable task which created this entry @@ -2256,7 +2267,8 @@ CREATE TABLE v1_durable_event_log_entry ( durable_task_id BIGINT NOT NULL, durable_task_inserted_at TIMESTAMPTZ NOT NULL, - kind v1_durable_event_log_entry_kind, + + kind v1_durable_event_log_kind NOT NULL, -- The node number in the durable event log. This represents a monotonically increasing -- sequence value generated from v1_durable_event_log_file.latest_node_id node_id BIGINT NOT NULL, @@ -2276,17 +2288,10 @@ CREATE TABLE v1_durable_event_log_entry ( -- Definite: we'll query directly for the node_id when a durable task is replaying its log -- Possible: we may want to query a range of node_ids for a durable task -- Possible: we may want to query a range of inserted_ats for a durable task + CONSTRAINT v1_durable_event_log_entry_pkey PRIMARY KEY (durable_task_id, durable_task_inserted_at, node_id) ) PARTITION BY RANGE(durable_task_inserted_at); -CREATE TYPE v1_durable_event_log_callback_kind AS ENUM ( - 'RUN_COMPLETED', - -- WAIT_FOR_COMPLETED can represent a durable sleep, an event, or some boolean combination of - -- these. - 'WAIT_FOR_COMPLETED', - 'MEMO_COMPLETED' -); - -- v1_durable_event_log_callback stores callbacks that complete a durable event log entry. This needs to be stateful -- so that it persists across worker restarts for the same durable task. -- @@ -2298,6 +2303,8 @@ CREATE TYPE v1_durable_event_log_callback_kind AS ENUM ( -- and direct queries from the engine side to mark a callback as satisfied when we've satisfied a v1_match. Because -- of this, we likely need to add a `callback_key` field to the v1_match table. CREATE TABLE v1_durable_event_log_callback ( + tenant_id UUID NOT NULL, + external_id UUID NOT NULL, -- The inserted_at time of this callback from a DB clock perspective. -- Important: for consistency, this should always be auto-generated via the CURRENT_TIMESTAMP! @@ -2306,18 +2313,16 @@ CREATE TABLE v1_durable_event_log_callback ( durable_task_id BIGINT NOT NULL, durable_task_inserted_at TIMESTAMPTZ NOT NULL, - kind v1_durable_event_log_callback_kind, - -- A unique, generated key for this callback. This key will change dependent on the callback kind. - -- Important: this key should be easily queryable directly from the durable log writers but also the controllers - -- that are checking if callbacks are satisfied. - key TEXT NOT NULL, + kind v1_durable_event_log_kind NOT NULL, + -- The associated log node id that this callback references. node_id BIGINT NOT NULL, -- Whether this callback has been seen by the engine or not. Note that is_satisfied _may_ change multiple -- times through the lifecycle of a callback, and readers should not assume that once it's true it will always be true. is_satisfied BOOLEAN NOT NULL DEFAULT FALSE, + -- Access patterns: -- Definite: we'll query directly for the key when a worker is checking if a callback is satisfied -- Definite: we'll query directly for the key when a v1_match has been satisfied and we need to mark the callback as satisfied - CONSTRAINT v1_durable_event_log_callback_pkey PRIMARY KEY (durable_task_id, durable_task_inserted_at, key) + CONSTRAINT v1_durable_event_log_callback_pkey PRIMARY KEY (durable_task_id, durable_task_inserted_at, node_id) ) PARTITION BY RANGE(durable_task_inserted_at);