syntax = "proto3";

package temporal.api.taskqueue.v1;

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

import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/wrappers.proto";

import "temporal/api/enums/v1/task_queue.proto";
import "temporal/api/common/v1/message.proto";
import "temporal/api/deployment/v1/message.proto";

// See https://docs.temporal.io/docs/concepts/task-queues/
message TaskQueue {
    string name = 1;
    // Default: TASK_QUEUE_KIND_NORMAL.
    temporal.api.enums.v1.TaskQueueKind kind = 2;
    // Iff kind == TASK_QUEUE_KIND_STICKY, then this field contains the name of
    // the normal task queue that the sticky worker is running on.
    string normal_name = 3;
}

// Only applies to activity task queues
message TaskQueueMetadata {
    // Allows throttling dispatch of tasks from this queue
    google.protobuf.DoubleValue max_tasks_per_second = 1;
}

// Experimental. Worker Deployments are experimental and might significantly change in the future.
message TaskQueueVersioningInfo {
    // Specifies which Deployment Version should receive new workflow executions and tasks of
    // existing unversioned or AutoUpgrade workflows.
    // Nil value represents all the unversioned workers (those with `UNVERSIONED` (or unspecified) `WorkerVersioningMode`.)
    // Note: Current Version is overridden by the Ramping Version for a portion of traffic when ramp percentage
    // is non-zero (see `ramping_deployment_version` and `ramping_version_percentage`).
    temporal.api.deployment.v1.WorkerDeploymentVersion current_deployment_version = 7;
    // Deprecated. Use `current_deployment_version`.
    string current_version = 1 [deprecated = true];

    // When ramp percentage is non-zero, that portion of traffic is shifted from the Current Version to the Ramping Version.
    // Must always be different from `current_deployment_version` unless both are nil.
    // Nil value represents all the unversioned workers (those with `UNVERSIONED` (or unspecified) `WorkerVersioningMode`.)
    // Note that it is possible to ramp from one Version to another Version, or from unversioned
    // workers to a particular Version, or from a particular Version to unversioned workers.
    temporal.api.deployment.v1.WorkerDeploymentVersion ramping_deployment_version = 9;
    // Deprecated. Use `ramping_deployment_version`.
    string ramping_version = 2 [deprecated = true];

    // Percentage of tasks that are routed to the Ramping Version instead of the Current Version.
    // Valid range: [0, 100]. A 100% value means the Ramping Version is receiving full traffic but
    // not yet "promoted" to be the Current Version, likely due to pending validations.
    // A 0% value means the Ramping Version is receiving no traffic.
    float ramping_version_percentage = 3;
    // Last time versioning information of this Task Queue changed.
    google.protobuf.Timestamp update_time = 4;
}

// Used for specifying versions the caller is interested in.
message TaskQueueVersionSelection {
    // Include specific Build IDs.
    repeated string build_ids = 1;
    // Include the unversioned queue.
    bool unversioned = 2;
    // Include all active versions. A version is considered active if, in the last few minutes,
    // it has had new tasks or polls, or it has been the subject of certain task queue API calls.
    bool all_active = 3;
}

message TaskQueueVersionInfo {
    // Task Queue info per Task Type. Key is the numerical value of the temporal.api.enums.v1.TaskQueueType enum.
    map<int32, TaskQueueTypeInfo> types_info = 1;

    // Task Reachability is eventually consistent; there may be a delay until it converges to the most
    // accurate value but it is designed in a way to take the more conservative side until it converges.
    // For example REACHABLE is more conservative than CLOSED_WORKFLOWS_ONLY.
    //
    // Note: future activities who inherit their workflow's Build ID but not its Task Queue will not be
    // accounted for reachability as server cannot know if they'll happen as they do not use
    // assignment rules of their Task Queue. Same goes for Child Workflows or Continue-As-New Workflows
    // who inherit the parent/previous workflow's Build ID but not its Task Queue. In those cases, make
    // sure to query reachability for the parent/previous workflow's Task Queue as well.
    temporal.api.enums.v1.BuildIdTaskReachability task_reachability = 2;
}

message TaskQueueTypeInfo {
    // Unversioned workers (with `useVersioning=false`) are reported in unversioned result even if they set a Build ID.
    repeated PollerInfo pollers = 1;
    TaskQueueStats stats = 2;
}

// TaskQueueStats contains statistics about task queue backlog and activity.
//
// For workflow task queue type, this result is partial because tasks sent to sticky queues are not included. Read
// comments above each metric to understand the impact of sticky queue exclusion on that metric accuracy.
message TaskQueueStats {
    // The approximate number of tasks backlogged in this task queue. May count expired tasks but eventually
    // converges to the right value. Can be relied upon for scaling decisions.
    //
    // Special note for workflow task queue type: this metric does not count sticky queue tasks. However, because
    // those tasks only remain valid for a few seconds, the inaccuracy becomes less significant as the backlog size
    // grows.
    int64 approximate_backlog_count = 1;
    // Approximate age of the oldest task in the backlog based on the creation time of the task at the head of
    // the queue. Can be relied upon for scaling decisions.
    //
    // Special note for workflow task queue type: this metric does not count sticky queue tasks. However, because
    // those tasks only remain valid for a few seconds, they should not affect the result when backlog is older than
    // few seconds.
    google.protobuf.Duration approximate_backlog_age = 2;
    // The approximate tasks per second added to the task queue, averaging the last 30 seconds. These includes tasks
    // whether or not they were added to/dispatched from the backlog or they were dispatched immediately without going
    // to the backlog (sync-matched).
    //
    // The difference between `tasks_add_rate` and `tasks_dispatch_rate` is a reliable metric for the rate at which
    // backlog grows/shrinks.
    //
    // Note: the actual tasks delivered to the workers may significantly be higher than the numbers reported by
    // tasks_add_rate, because:
    // - Tasks can be sent to workers without going to the task queue. This is called Eager dispatch. Eager dispatch is
    //   enable for activities by default in the latest SDKs.
    // - Tasks going to Sticky queue are not accounted for. Note that, typically, only the first workflow task of each
    //   workflow goes to a normal queue, and the rest workflow tasks go to the Sticky queue associated with a specific
    //   worker instance.
    float tasks_add_rate = 3;
    // The approximate tasks per second dispatched from the task queue, averaging the last 30 seconds. These includes
    // tasks whether or not they were added to/dispatched from the backlog or they were dispatched immediately without
    // going to the backlog (sync-matched).
    //
    // The difference between `tasks_add_rate` and `tasks_dispatch_rate` is a reliable metric for the rate at which
    // backlog grows/shrinks.
    //
    // Note: the actual tasks delivered to the workers may significantly be higher than the numbers reported by
    // tasks_dispatch_rate, because:
    // - Tasks can be sent to workers without going to the task queue. This is called Eager dispatch. Eager dispatch is
    //   enable for activities by default in the latest SDKs.
    // - Tasks going to Sticky queue are not accounted for. Note that, typically, only the first workflow task of each
    //   workflow goes to a normal queue, and the rest workflow tasks go to the Sticky queue associated with a specific
    //   worker instance.
    float tasks_dispatch_rate = 4;
}

// Deprecated. Use `InternalTaskQueueStatus`. This is kept until `DescribeTaskQueue` supports legacy behavior.
message TaskQueueStatus {
    int64 backlog_count_hint = 1;
    int64 read_level = 2;
    int64 ack_level = 3;
    double rate_per_second = 4;
    TaskIdBlock task_id_block = 5;
}

message TaskIdBlock {
    int64 start_id = 1;
    int64 end_id = 2;
}

message TaskQueuePartitionMetadata {
    string key = 1;
    string owner_host_name = 2;
}

message PollerInfo {
    google.protobuf.Timestamp last_access_time = 1;
    string identity = 2;
    double rate_per_second = 3;
    // If a worker has opted into the worker versioning feature while polling, its capabilities will
    // appear here.
    // Deprecated. Replaced by deployment_options.
    temporal.api.common.v1.WorkerVersionCapabilities worker_version_capabilities = 4 [deprecated = true];
    // Worker deployment options that SDK sent to server.
    temporal.api.deployment.v1.WorkerDeploymentOptions deployment_options = 5;
}

message StickyExecutionAttributes {
    TaskQueue worker_task_queue = 1;
    // (-- api-linter: core::0140::prepositions=disabled
    //     aip.dev/not-precedent: "to" is used to indicate interval. --)
    google.protobuf.Duration schedule_to_start_timeout = 2;
}

// Used by the worker versioning APIs, represents an unordered set of one or more versions which are
// considered to be compatible with each other. Currently the versions are always worker build IDs.
message CompatibleVersionSet {
    // All the compatible versions, unordered, except for the last element, which is considered the set "default".
    repeated string build_ids = 1;
}

// Reachability of tasks for a worker on a single task queue.
message TaskQueueReachability {
    string task_queue = 1;
    // Task reachability for a worker in a single task queue.
    // See the TaskReachability docstring for information about each enum variant.
    // If reachability is empty, this worker is considered unreachable in this task queue.
    repeated temporal.api.enums.v1.TaskReachability reachability = 2;
}

// Reachability of tasks for a worker by build id, in one or more task queues.
message BuildIdReachability {
    // A build id or empty if unversioned.
    string build_id = 1;
    // Reachability per task queue.
    repeated TaskQueueReachability task_queue_reachability = 2;
}

message RampByPercentage {
    // Acceptable range is [0,100).
    float ramp_percentage = 1;
}

// Assignment rules are applied to *new* Workflow and Activity executions at
// schedule time to assign them to a Build ID.
//
// Assignment rules will not be used in the following cases:
//    - Child Workflows or Continue-As-New Executions who inherit their
//      parent/previous Workflow's assigned Build ID (by setting the
//      `inherit_build_id` flag - default behavior in SDKs when the same Task Queue
//      is used.)
//    - An Activity that inherits the assigned Build ID of its Workflow (by
//      setting the `use_workflow_build_id` flag - default behavior in SDKs
//      when the same Task Queue is used.)
//
// In absence of (applicable) redirect rules (`CompatibleBuildIdRedirectRule`s)
// the task will be dispatched to Workers of the Build ID determined by the
// assignment rules (or inherited). Otherwise, the final Build ID will be
// determined by the redirect rules.
//
// Once a Workflow completes its first Workflow Task in a particular Build ID it
// stays in that Build ID regardless of changes to assignment rules. Redirect
// rules can be used to move the workflow to another compatible Build ID.
//
// When using Worker Versioning on a Task Queue, in the steady state,
// there should typically be a single assignment rule to send all new executions
// to the latest Build ID. Existence of at least one such "unconditional"
// rule at all times is enforces by the system, unless the `force` flag is used
// by the user when replacing/deleting these rules (for exceptional cases).
//
// During a deployment, one or more additional rules can be added to assign a
// subset of the tasks to a new Build ID based on a "ramp percentage".
//
// When there are multiple assignment rules for a Task Queue, the rules are
// evaluated in order, starting from index 0. The first applicable rule will be
// applied and the rest will be ignored.
//
// In the event that no assignment rule is applicable on a task (or the Task
// Queue is simply not versioned), the tasks will be dispatched to an
// unversioned Worker.
message BuildIdAssignmentRule {
    string target_build_id = 1;

    // If a ramp is provided, this rule will be applied only to a sample of
    // tasks according to the provided percentage.
    // This option can be used only on "terminal" Build IDs (the ones not used
    // as source in any redirect rules).
    oneof ramp {
        // This ramp is useful for gradual Blue/Green deployments (and similar)
        // where you want to send a certain portion of the traffic to the target
        // Build ID.
        RampByPercentage percentage_ramp = 3;
    }
}

// These rules apply to tasks assigned to a particular Build ID
// (`source_build_id`) to redirect them to another *compatible* Build ID
// (`target_build_id`).
//
// It is user's responsibility to ensure that the target Build ID is compatible
// with the source Build ID (e.g. by using the Patching API).
//
// Most deployments are not expected to need these rules, however following
// situations can greatly benefit from redirects:
//  - Need to move long-running Workflow Executions from an old Build ID to a
//    newer one.
//  - Need to hotfix some broken or stuck Workflow Executions.
//
// In steady state, redirect rules are beneficial when dealing with old
// Executions ran on now-decommissioned Build IDs:
//  - To redirecting the Workflow Queries to the current (compatible) Build ID.
//  - To be able to Reset an old Execution so it can run on the current
//    (compatible) Build ID.
//
// Redirect rules can be chained.
message CompatibleBuildIdRedirectRule {
    string source_build_id = 1;
    // Target Build ID must be compatible with the Source Build ID; that is it
    // must be able to process event histories made by the Source Build ID by
    // using [Patching](https://docs.temporal.io/workflows#patching) or other
    // means.
    string target_build_id = 2;
}

message TimestampedBuildIdAssignmentRule {
    BuildIdAssignmentRule rule = 1;
    google.protobuf.Timestamp create_time = 2;
}

message TimestampedCompatibleBuildIdRedirectRule {
    CompatibleBuildIdRedirectRule rule = 1;
    google.protobuf.Timestamp create_time = 2;
}

// Attached to task responses to give hints to the SDK about how it may adjust its number of
// pollers.
message PollerScalingDecision {
  // How many poll requests to suggest should be added or removed, if any. As of now, server only
  // scales up or down by 1. However, SDKs should allow for other values (while staying within
  // defined min/max).
  //
  // The SDK is free to ignore this suggestion, EX: making more polls would not make sense because
  // all slots are already occupied.
  int32 poll_request_delta_suggestion = 1;
}

message RateLimit { 
    // Zero is a valid rate limit.
    float requests_per_second = 1;
}

message ConfigMetadata {
    // Reason for why the config was set.
    string reason = 1;

    // Identity of the last updater.
    // Set by the request's identity field.
    string update_identity = 2;
    
    // Time of the last update.
    google.protobuf.Timestamp update_time = 3;
}

message RateLimitConfig { 
    RateLimit rate_limit = 1;    
    ConfigMetadata metadata = 2;
}

message TaskQueueConfig {
    // Unless modified, this is the system-defined rate limit.
    RateLimitConfig queue_rate_limit = 1;
    // If set, each individual fairness key will be limited to this rate, scaled by the weight of the fairness key.
    RateLimitConfig fairness_keys_rate_limit_default = 2;
}
