// SPDX-License-Identifier: AGPL-3.0-only
// Provenance-includes-location: https://github.com/cortexproject/cortex/blob/master/pkg/scheduler/schedulerpb/scheduler.proto
// Provenance-includes-license: Apache-2.0
// Provenance-includes-copyright: The Cortex Authors.

syntax = "proto3";

package schedulerpb;

import "github.com/grafana/dskit/httpgrpc/httpgrpc.proto";
import "gogoproto/gogo.proto";
import "google/protobuf/any.proto";

option go_package = "schedulerpb";
option (gogoproto.marshaler_all) = true;
option (gogoproto.unmarshaler_all) = true;

// Scheduler interface exposed to Queriers.
service SchedulerForQuerier {
  // After calling this method, both Querier and Scheduler enter a loop, in which querier waits for
  // "SchedulerToQuerier" messages containing HTTP requests and processes them. After processing the request,
  // querier signals that it is ready to accept another one by sending empty QuerierToScheduler message.
  //
  // Long-running loop is used to detect broken connection between scheduler and querier. This is important
  // for scheduler to keep a list of connected queriers up-to-date.
  rpc QuerierLoop(stream QuerierToScheduler) returns (stream SchedulerToQuerier) {}

  // The querier notifies the query-scheduler that it started a graceful shutdown.
  rpc NotifyQuerierShutdown(NotifyQuerierShutdownRequest) returns (NotifyQuerierShutdownResponse);
}

// Querier reports its own clientID when it connects, so that scheduler knows how many *different* queriers are connected.
// To signal that querier is ready to accept another request, querier sends empty message.
message QuerierToScheduler {
  string querierID = 1;
}

message SchedulerToQuerier {
  // Query ID as reported by frontend. When querier sends the response back to frontend (using frontendAddress),
  // it identifies the query by using this ID.
  uint64 queryID = 1;

  oneof payload {
    httpgrpc.HTTPRequest httpRequest = 2;
    ProtobufRequest protobufRequest = 7;
  }

  // Where should querier send HTTP Response to (using FrontendForQuerier interface).
  string frontendAddress = 3;

  // User who initiated the request. Needed to send reply back to frontend.
  string userID = 4;

  // Whether query statistics tracking should be enabled. The response will include
  // statistics only when this option is enabled.
  bool statsEnabled = 5;

  // How much time did query spend in the queue.
  int64 queueTimeNanos = 6;
}

message ProtobufRequest {
  google.protobuf.Any payload = 1;

  // Metadata is metadata orthogonal to the request itself, analogous to HTTP headers or gRPC metadata.
  // We can't use gRPC metadata in this context because this message is sent over long-lived streams, rather
  // than as an individual request.
  repeated MetadataItem metadata = 2 [(gogoproto.nullable) = false];
}

message MetadataItem {
  string key = 1;
  repeated string value = 2;
}

// Scheduler interface exposed to Frontend. Frontend can enqueue and cancel requests.
service SchedulerForFrontend {
  // After calling this method, both Frontend and Scheduler enter a loop. Frontend will keep sending ENQUEUE and
  // CANCEL requests, and scheduler is expected to process them. Scheduler returns one response for each request.
  //
  // Long-running loop is used to detect broken connection between frontend and scheduler. This is important for both
  // parties... if connection breaks, frontend can cancel (and possibly retry on different scheduler) all pending
  // requests sent to this scheduler, while scheduler can cancel queued requests from given frontend.
  rpc FrontendLoop(stream FrontendToScheduler) returns (stream SchedulerToFrontend) {}
}

enum FrontendToSchedulerType {
  INIT = 0;
  ENQUEUE = 1;
  CANCEL = 2;
}

message FrontendToScheduler {
  FrontendToSchedulerType type = 1;

  // Used by INIT message. Will be put into all requests passed to querier.
  string frontendAddress = 2;

  // Used by ENQUEUE and CANCEL.
  // Each frontend manages its own queryIDs. Different frontends may use same set of query IDs.
  uint64 queryID = 3;

  // Following are used by ENQUEUE only.
  string userID = 4;
  oneof payload {
    httpgrpc.HTTPRequest httpRequest = 5;
    ProtobufRequest protobufRequest = 8;
  }
  bool statsEnabled = 6;
  repeated string additionalQueueDimensions = 7;
}

enum SchedulerToFrontendStatus {
  OK = 0;
  TOO_MANY_REQUESTS_PER_TENANT = 1;
  ERROR = 2;
  SHUTTING_DOWN = 3;
}

message SchedulerToFrontend {
  SchedulerToFrontendStatus status = 1;
  string error = 2;
}

message NotifyQuerierShutdownRequest {
  string querierID = 1;
}

message NotifyQuerierShutdownResponse {}
