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

syntax = "proto3";

// TODO: Rename to ingesterpb
package cortex;

import "github.com/grafana/mimir/pkg/mimirpb/mimir.proto";
import "gogoproto/gogo.proto";

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

service Ingester {
  rpc Push(cortexpb.WriteRequest) returns (cortexpb.WriteResponse) {}
  rpc QueryStream(QueryRequest) returns (stream QueryStreamResponse) {}
  rpc QueryExemplars(ExemplarQueryRequest) returns (ExemplarQueryResponse) {}

  rpc LabelValues(LabelValuesRequest) returns (LabelValuesResponse) {}
  rpc LabelNames(LabelNamesRequest) returns (LabelNamesResponse) {}
  rpc UserStats(UserStatsRequest) returns (UserStatsResponse) {}
  rpc AllUserStats(UserStatsRequest) returns (UsersStatsResponse) {}
  rpc MetricsForLabelMatchers(MetricsForLabelMatchersRequest) returns (MetricsForLabelMatchersResponse) {}
  rpc MetricsMetadata(MetricsMetadataRequest) returns (MetricsMetadataResponse) {}

  // LabelNamesAndValues provides all values for each label that matches the matchers.
  // The order of the labels and values is not guaranteed.
  rpc LabelNamesAndValues(LabelNamesAndValuesRequest) returns (stream LabelNamesAndValuesResponse) {}

  // LabelValuesCardinality returns all values and series total count for label_names labels
  // that match the matchers.
  // The listing order of the labels is not guaranteed.
  rpc LabelValuesCardinality(LabelValuesCardinalityRequest) returns (stream LabelValuesCardinalityResponse) {}

  rpc ActiveSeries(ActiveSeriesRequest) returns (stream ActiveSeriesResponse) {}

  // When adding more read-path methods here, please update ingester_read_path_routes_regex in operations/mimir-mixin/config.libsonnet as well.
}

message LabelNamesAndValuesRequest {
  repeated LabelMatcher matchers = 1;
  CountMethod count_method = 2;
}

message LabelNamesAndValuesResponse {
  repeated LabelValues items = 1;
}

message LabelValues {
  string label_name = 1;
  repeated string values = 2;
}

enum CountMethod {
  IN_MEMORY = 0;
  ACTIVE = 1;
}

message LabelValuesCardinalityRequest {
  repeated string label_names = 1;
  repeated LabelMatcher matchers = 2;
  CountMethod count_method = 3;
}

message LabelValuesCardinalityResponse {
  repeated LabelValueSeriesCount items = 1;
}

message LabelValueSeriesCount {
  string label_name = 1;
  map<string, uint64> label_value_series = 2;
}

message QueryRequest {
  int64 start_timestamp_ms = 1;
  int64 end_timestamp_ms = 2;
  repeated LabelMatcher matchers = 3;

  // Why 100? This QueryRequest message is also used for remote read requests, so we need to avoid any field numbers added in the future.
  uint64 streaming_chunks_batch_size = 100;
}

message ExemplarQueryRequest {
  int64 start_timestamp_ms = 1;
  int64 end_timestamp_ms = 2;
  repeated LabelMatchers matchers = 3;
}

message ActiveSeriesRequest {
  repeated LabelMatcher matchers = 1;
  enum RequestType {
    SERIES = 0;
    NATIVE_HISTOGRAM_SERIES = 1;
  }
  RequestType type = 2;
}

// QueryStreamResponse contains either:
// - a batch of series labels (sorted in series order), with is_end_of_series_stream=true for the last batch or
// - a batch of chunks (sorted in series order).
//
// Only one of these two options will be populated.
message QueryStreamResponse {
  reserved 1; // "chunkseries" aren't used anymore, only streaming series.
  reserved 2; // "timeseries" aren't used anymore, only streaming series.

  repeated QueryStreamSeries streaming_series = 3 [(gogoproto.nullable) = false];
  bool is_end_of_series_stream = 4;

  repeated QueryStreamSeriesChunks streaming_series_chunks = 5 [(gogoproto.nullable) = false];
}

message QueryStreamSeries {
  repeated cortexpb.LabelPair labels = 1 [
    (gogoproto.nullable) = false,
    (gogoproto.customtype) = "github.com/grafana/mimir/pkg/mimirpb.LabelAdapter"
  ];
  int64 chunk_count = 2;
}

message QueryStreamSeriesChunks {
  uint64 series_index = 1; // Index into list of all series previously sent with QueryStreamSeries messages by this ingester during this query response.
  repeated Chunk chunks = 2 [(gogoproto.nullable) = false];
}

message ExemplarQueryResponse {
  repeated cortexpb.TimeSeries timeseries = 1 [(gogoproto.nullable) = false];
}

message LabelValuesRequest {
  string label_name = 1;
  int64 start_timestamp_ms = 2;
  int64 end_timestamp_ms = 3;
  LabelMatchers matchers = 4;
  int64 limit = 5;
}

message LabelValuesResponse {
  repeated string label_values = 1;
}

message LabelNamesRequest {
  int64 start_timestamp_ms = 1;
  int64 end_timestamp_ms = 2;
  LabelMatchers matchers = 3;
  int64 limit = 4;
}

message LabelNamesResponse {
  repeated string label_names = 1;
}

message UserStatsRequest {
  CountMethod count_method = 1;
}

message UserStatsResponse {
  double ingestion_rate = 1;
  uint64 num_series = 2;
  double api_ingestion_rate = 3;
  double rule_ingestion_rate = 4;
}

message UserIDStatsResponse {
  string user_id = 1;
  UserStatsResponse data = 2;
}

message UsersStatsResponse {
  repeated UserIDStatsResponse stats = 1;
}

message MetricsForLabelMatchersRequest {
  int64 start_timestamp_ms = 1;
  int64 end_timestamp_ms = 2;
  repeated LabelMatchers matchers_set = 3;
  int64 limit = 4;
}

message MetricsForLabelMatchersResponse {
  repeated cortexpb.Metric metric = 1;
}

message MetricsMetadataRequest {
  sint32 limit = 1;
  sint32 limit_per_metric = 2;
  string metric = 3;
}

message MetricsMetadataResponse {
  repeated cortexpb.MetricMetadata metadata = 1;
}

message ActiveSeriesResponse {
  repeated cortexpb.Metric metric = 1;
  // bucket_count is only used when the request type was NATIVE_HISTOGRAM_SERIES.
  // bucket_count contains the native histogram active buckets count for each series in "metric" above.
  repeated uint64 bucket_count = 2;
}

message Chunk {
  int64 start_timestamp_ms = 1;
  int64 end_timestamp_ms = 2;
  int32 encoding = 3;
  bytes data = 4 [
    (gogoproto.nullable) = false,
    (gogoproto.customtype) = "github.com/grafana/mimir/pkg/mimirpb.UnsafeByteSlice"
  ];
}

message LabelMatchers {
  repeated LabelMatcher matchers = 1;
}

enum MatchType {
  EQUAL = 0;
  NOT_EQUAL = 1;
  REGEX_MATCH = 2;
  REGEX_NO_MATCH = 3;
}

message LabelMatcher {
  MatchType type = 1;
  string name = 2;
  string value = 3;
}
