syntax="proto3";

package tempopb;

import "trace/v1/trace.proto";
import "common/v1/common.proto";
import "github.com/gogo/protobuf/gogoproto/gogo.proto";

service Pusher {
  // different versions of PushBytes expect the trace data to be pushed in different formats
  rpc PushBytes(PushBytesRequest) returns (PushResponse) {};   // ./pkg/model/v1
  rpc PushBytesV2(PushBytesRequest) returns (PushResponse) {}; // ./pkg/model/v2
}

service MetricsGenerator {
  rpc PushSpans(PushSpansRequest) returns (PushResponse) {};
  rpc GetMetrics(SpanMetricsRequest) returns (SpanMetricsResponse) {};
}

service Querier {
  rpc FindTraceByID(TraceByIDRequest) returns (TraceByIDResponse) {};
  rpc SearchRecent(SearchRequest) returns (SearchResponse) {};
  rpc SearchBlock(SearchBlockRequest) returns (SearchResponse) {};
  rpc SearchTags(SearchTagsRequest) returns (SearchTagsResponse) {};
  rpc SearchTagsV2(SearchTagsRequest) returns (SearchTagsV2Response) {};
  rpc SearchTagValues(SearchTagValuesRequest) returns (SearchTagValuesResponse) {};
  rpc SearchTagValuesV2(SearchTagValuesRequest) returns (SearchTagValuesV2Response) {};
  // rpc SpanMetricsSummary(SpanMetricsSummaryRequest) returns (SpanMetricsSummaryResponse) {};
}

service StreamingQuerier {
  rpc Search(SearchRequest) returns (stream SearchResponse);
}

service Metrics {
  rpc SpanMetricsSummary(SpanMetricsSummaryRequest) returns (SpanMetricsSummaryResponse) {}
}

// Read
message TraceByIDRequest {
  bytes traceID = 1;
  string blockStart = 2;
  string blockEnd = 3;
  string queryMode = 5;
}

message TraceByIDResponse {
  Trace trace = 1;
  TraceByIDMetrics metrics = 2;
}

message TraceByIDMetrics {
}

// SearchRequest takes no block parameters and implies a "recent traces" search
message SearchRequest {
  // case insensitive partial match
  map<string, string> Tags = 1 [(gogoproto.nullable) = false];
  uint32 MinDurationMs = 2;
  uint32 MaxDurationMs = 3;
  uint32 Limit = 4;
  uint32 start = 5;
  uint32 end = 6;
  // TraceQL query
  string Query = 8;
  uint32 SpansPerSpanSet = 9;
}

// SearchBlockRequest takes SearchRequest parameters as well as all information necessary
// to search a block in the backend.
message SearchBlockRequest {
  SearchRequest searchReq = 1;
  string blockID = 2;
  uint32 startPage = 3;
  uint32 pagesToSearch = 4;
  string encoding = 5;
  uint32 indexPageSize = 6;
  uint32 totalRecords = 7;
  string dataEncoding = 8;
  string version = 9;
  uint64 size = 10; // total size of data file
  uint32 footerSize = 11; // size of file footer (parquet)
}

message SearchResponse {
  repeated TraceSearchMetadata traces = 1;
  SearchMetrics metrics = 2;
}

message TraceSearchMetadata {
  string traceID = 1;
  string rootServiceName = 2;
  string rootTraceName = 3;
  uint64 startTimeUnixNano = 4;
  uint32 durationMs = 5;
  SpanSet spanSet = 6; // deprecated. use SpanSets field below
  repeated SpanSet spanSets = 7;
}

message SpanSet {
  repeated Span spans = 1;
  uint32 matched = 2;
  repeated tempopb.common.v1.KeyValue attributes = 3;
}

message Span {
  string spanID = 1;
  string name = 2;
  uint64 startTimeUnixNano = 3;
  uint64 durationNanos = 4;
  repeated tempopb.common.v1.KeyValue attributes = 5;
}

message SearchMetrics {
  uint32 inspectedTraces = 1;
  uint64 inspectedBytes = 2;
  uint32 totalBlocks = 3;
  uint32 completedJobs = 4;
  uint32 totalJobs = 5;
  uint64 totalBlockBytes = 6;
}

message SearchTagsRequest {
  string scope = 1;
}

message SearchTagsResponse {
  repeated string tagNames = 1;
}

message SearchTagsV2Response {
  repeated SearchTagsV2Scope scopes = 1;
}

message SearchTagsV2Scope {
  string name = 1;
  repeated string tags = 2;
}

message SearchTagValuesRequest {
  string tagName = 1;
  string query = 2; // TraceQL query
}

message SearchTagValuesResponse {
  repeated string tagValues = 1;
}

message TagValue {
  string type = 1;
  string value = 2;
}

message SearchTagValuesV2Response {
  repeated TagValue tagValues = 1;
}

message Trace {
  repeated tempopb.trace.v1.ResourceSpans batches = 1;
}

// Write
message PushResponse {
}

// PushBytesRequest pushes slices of traces, ids and searchdata. Traces are encoded using the
//  current BatchDecoder in ./pkg/model
message PushBytesRequest {
  // pre-marshalled Traces. length must match ids
  repeated bytes traces = 2 [(gogoproto.nullable) = false, (gogoproto.customtype) = "PreallocBytes"];
  // trace ids. length must match traces
  repeated bytes ids = 3 [(gogoproto.nullable) = false, (gogoproto.customtype) = "PreallocBytes"];
  // search data, length must match traces
  repeated bytes searchData = 4 [(gogoproto.nullable) = false, (gogoproto.customtype) = "PreallocBytes"];
}

message PushSpansRequest {
  // just send entire OTel spans for now
  repeated tempopb.trace.v1.ResourceSpans batches = 1;
}

message TraceBytes {
  // pre-marshalled Traces
  repeated bytes traces = 1;
}

// this message exists for marshalling/unmarshalling convenience to/from parquet. in parquet we proto encode
// links to a column. unfortunately you can't encode a slice directly so we use this wrapper to generate 
// the required marshalling/unmarshalling functions.
message LinkSlice {
  repeated tempopb.trace.v1.Span.Link links = 1;
}

message SpanMetricsRequest {
  string query = 1;
  string groupBy = 2;
  uint64 limit = 3;
}

message SpanMetricsSummaryRequest {
  string query = 1;
  string groupBy = 2;
  uint64 limit = 3;
}

message SpanMetricsResponse {
  bool estimated = 1;
  uint64 spanCount = 2;
  uint64 errorSpanCount = 3;
  repeated SpanMetrics metrics = 4;
}

message RawHistogram {
  uint64 bucket = 1;
  uint64 count = 2;
}

message SpanMetrics {
  repeated RawHistogram latency_histogram = 1;
  TraceQLStatic static = 2;
  uint64 errors = 3;
}

message SpanMetricsSummary {
  uint64 spanCount = 1;
  uint64 errorSpanCount = 2;
  TraceQLStatic static = 3;
  uint64 p99 = 4;
  uint64 p95 = 5;
  uint64 p90 = 6;
  uint64 p50 = 7;
}

message SpanMetricsSummaryResponse {
  repeated SpanMetricsSummary summaries = 1;
}

message TraceQLStatic {
  int32 type = 1;
  int64 n = 2;
  double f = 3;
  string s = 4;
  bool b = 5;
  uint64 d = 6;
  int32 status = 7;
  int32 kind = 8;
}
