go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/analysis/proto/v1/clusters.proto (about)

     1  // Copyright 2022 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  syntax = "proto3";
    16  
    17  package luci.analysis.v1;
    18  
    19  option go_package = "go.chromium.org/luci/analysis/proto/v1;analysispb";
    20  
    21  import "google/protobuf/timestamp.proto";
    22  import "go.chromium.org/luci/analysis/proto/v1/common.proto";
    23  import "go.chromium.org/luci/analysis/proto/v1/sources.proto";
    24  import "go.chromium.org/luci/analysis/proto/v1/failure_reason.proto";
    25  
    26  // Provides methods to cluster test results, and obtain the impact of those
    27  // clusters.
    28  //
    29  // A cluster is a group of test failures with a common characteristic.
    30  // For example, test results may form a cluster with other failures that share
    31  // a common test name, or failure reason. Test results may also be in a cluster
    32  // defined by a user-modifiable failure association rule (which associates
    33  // failures with a bug). In this case, the failures have the property defined
    34  // by the failure association rule in common.
    35  //
    36  // A test result may be in many clusters, and each cluster may contain many
    37  // test results.
    38  //
    39  // Each cluster has an identity, consisting of three components:
    40  // - The LUCI Project name, e.g. "chromium" or "fuchsia".
    41  // - The Clustering Algorithm that identified the cluster. As at writing
    42  //   (April 2022), the algorithms are 'testname-v3' for version 3 of the
    43  //   test-name clustering algorithm, 'reason-v3' for version 3 of the failure
    44  //   reason clustering algorithm, and 'rules-v2' for the rules-based clustering
    45  //   algorithm.
    46  //   (Although internally versioned, the rules algorithm version is hidden
    47  //   for clients, so that {luci_project}/rules/{rule_id} always represents
    48  //   the cluster defined by the given rule_id.)
    49  //   We make no guarantees about the structure of algorithm names, they should
    50  //   be treated as opaque strings by clients.
    51  // - An algorithm-defined cluster identifier. This is algorithm-dependent and
    52  //   although (as at April 2022) a lowercase hexadecimal string, should be
    53  //   treated as an opaque value by clients.
    54  //   For the 'rules' algorithm, the cluster identifier will always correspond
    55  //   to the Rule ID of the rule that defines the cluster.
    56  service Clusters {
    57      // Identifies the cluster(s) for one or more test failure(s).
    58      //
    59      // This RPC returns the clusters of each test result, using
    60      // current suggested cluster algorithms, configured failure
    61      // association rules, and ingested project configuration with
    62      // a bounded staleness of up to one minute. (Returned clusters
    63      // may be based on project configuration and configured failure
    64      // association rules that is up to one minute out-of-date).
    65      //
    66      // As at April 2022, the implementation does not use stale
    67      // rules, but you are instructed NOT to rely on this property to
    68      // allow reversion to the faster implementation that is tolerant
    69      // to higher QPS in future. If your use case require strong reads
    70      // (e.g. you want to call cluster immediately after updating a rule),
    71      // please contact LUCI Analysis owners. We may be able to provide a
    72      // request flag to select this processing behaviour.
    73      //
    74      // This RPC is a pure query API and does not lead to the ingestion of the
    75      // test failures by LUCI Analysis (e.g. for cluster impact calculations).
    76      rpc Cluster(ClusterRequest) returns (ClusterResponse) {};
    77  
    78      // Reads information about the given cluster.
    79      //
    80      // Please consult LUCI Analysis owners before adding additional calls to
    81      // this RPC, as the implementation currently calls back to BigQuery and as
    82      // such, is not cost-optimised if many queries are to be made.
    83      //
    84      // As of writing (April 13, 2022) this query reads ~1 GB per call for
    85      // the largest LUCI Project, which translates to a cost of 0.5 US cents
    86      // per query at published pricing (US$5/TB analyzed for BigQuery).
    87      //
    88      // Changes to this RPC should comply with https://google.aip.dev/131.
    89      rpc Get(GetClusterRequest) returns (luci.analysis.v1.Cluster) {};
    90  
    91      // Reads current progress re-clustering the given project. Re-clustering
    92      // means updating the clusters each failure is in to reflect the latest
    93      // failure association rules, suggested clustering algorithms and
    94      // clustering configuration.
    95      rpc GetReclusteringProgress(GetReclusteringProgressRequest)
    96          returns (ReclusteringProgress) {};
    97  
    98      // Queries summary information about top clusters.
    99      //
   100      // The set of test failures used as input to the clustering can be
   101      // specified using the failure_filter field on the request.
   102      // The returned clusters include only the impact derived from the
   103      // filtered failures.
   104      //
   105      // This allows investigation of the highest impact clusters for some
   106      // subset of the failure data in a project. For example, a filter string
   107      // of "failure_reason:ssh" would find all of the clusters where any test
   108      // results mention "ssh" in their failure reason, and show how big the
   109      // impact from these ssh failures is in each cluster. This is useful when
   110      // investigating specific problems, or ownership areas of the tests.
   111      //
   112      // Please consult LUCI Analysis owners before adding additional calls
   113      // to this RPC, as the implementation currently calls back to BigQuery and as
   114      // such, is not cost-optimised if many queries are to be made.
   115      //
   116      // As of writing (April 13, 2022) this query reads up to 10 GB per call for
   117      // 7 days of data for the largest LUCI Project, which translates to a cost
   118      // of up to 5 US cents per query at published pricing
   119      // (US$5/TB analyzed for BigQuery).
   120      rpc QueryClusterSummaries(QueryClusterSummariesRequest)
   121          returns (QueryClusterSummariesResponse) {};
   122  
   123      // Queries examples of failures in the given cluster.
   124      //
   125      // Please consult LUCI Analysis owners before adding additional calls to
   126      // this RPC, as the implementation currently calls back to BigQuery and as
   127      // such, is not cost-optimised if many queries are to be made.
   128      rpc QueryClusterFailures(QueryClusterFailuresRequest)
   129          returns (QueryClusterFailuresResponse) {};
   130  
   131      // Queries test variants in the cluster which have recently had an
   132      // exoneration recorded against them. Only exonerations on failures
   133      // which are part of the cluster are considered.
   134      //
   135      // Consider solving this use case in future by a standard AIP-132 List
   136      // method with filter and order_by support.
   137      //
   138      // This RPC is useful for projects using the legacy QueryFailureRate
   139      // API for exoneration.
   140      rpc QueryExoneratedTestVariants(QueryClusterExoneratedTestVariantsRequest)
   141          returns (QueryClusterExoneratedTestVariantsResponse) {};
   142  
   143      // Queries test variant branches in the cluster which have recently had
   144      // an exoneration recorded against them. Only exonerations on failures
   145      // which are part of the cluster are considered.
   146      //
   147      // Use for projects performing branch-scoped exoneration using
   148      // QueryStability.
   149      rpc QueryExoneratedTestVariantBranches(QueryClusterExoneratedTestVariantBranchesRequest)
   150          returns (QueryClusterExoneratedTestVariantBranchesResponse) {};
   151  
   152      // Queries the history of metrics for clustered failures satisying given criteria.
   153      // For example the number of test runs failed on each day for the last 7 days.
   154      //
   155      // Please consult LUCI Analysis owners before adding additional calls to
   156      // this RPC, as the implementation currently calls back to BigQuery and as
   157      // such, is not cost-optimised if many queries are to be made.
   158      rpc QueryHistory(QueryClusterHistoryRequest)
   159          returns (QueryClusterHistoryResponse) {};
   160  }
   161  
   162  message ClusterRequest {
   163      // TestResult captures information about a test result, sufficient to
   164      // cluster it. The fields requested here may be expanded over time.
   165      // For example, variant information may be requested in future.
   166      message TestResult {
   167          // Opaque tag supplied by the caller, to be returned in the
   168          // response. Provided to assist correlating responses with requests.
   169          // Does not need to be unique. Optional.
   170          string request_tag = 1;
   171  
   172          // Identifier of the test (as reported to ResultDB).
   173          // For chromium projects, this starts with ninja://.
   174          string test_id = 2;
   175  
   176          // The failure reason of the test (if any).
   177          luci.analysis.v1.FailureReason failure_reason = 3;
   178      }
   179      // The LUCI Project for which the test result should be clustered.
   180      string project = 1;
   181  
   182      // The test results to cluster. At most 1000 test results may be
   183      // clustered in one request.
   184      repeated TestResult test_results = 2;
   185  }
   186  
   187  message ClusterResponse {
   188      // The cluster(s) a test result is contained in.
   189      message ClusteredTestResult {
   190          // An individual cluster a test result is contained in.
   191          message ClusterEntry {
   192              // The unique identifier of the cluster.
   193              // If the algorithm is "rules", the cluster ID is also a rule ID.
   194              luci.analysis.v1.ClusterId cluster_id = 1;
   195  
   196              // The bug associated with the cluster, if any. This is only
   197              // populated for clusters defined by a failure association rule,
   198              // which associates specified failures to a bug.
   199              luci.analysis.v1.AssociatedBug bug = 2;
   200          }
   201          // Opaque tag supplied by the caller in the request. Provided to assist
   202          // the caller correlate responses with requests.
   203          string request_tag = 1;
   204  
   205          // The clusters the test result is contained within.
   206          repeated ClusterEntry clusters = 2;
   207      }
   208  
   209     // The clusters each test result is in.
   210     // Contains one result for each test result specified in the request.
   211     // Results are provided in the same order as the request, so
   212     // the i-th ClusteredTestResult corresponds to the i-th
   213     // TestResult in the request.
   214     repeated ClusteredTestResult clustered_test_results = 1;
   215  
   216     // The versions of clustering algorithms, rules and project configuration
   217     // used to service this request. For debugging purposes only.
   218     ClusteringVersion clustering_version = 2;
   219  }
   220  
   221  // The versions of algorithms, rules and configuration used by LUCI Analysis
   222  // to cluster test results. For a given test result and ClusteringVersion,
   223  // the set of returned clusters should always be the same.
   224  message ClusteringVersion {
   225      // The version of clustering algorithms used.
   226      int32 algorithms_version = 1;
   227  
   228      // The version of failure association rules used. This is the Spanner
   229      // commit timestamp of the last rule modification incorporated in the
   230      // set of rules used to cluster the results.
   231      google.protobuf.Timestamp rules_version = 2;
   232  
   233      // The version of project configuration used. This is the timestamp
   234      // the project configuration was ingested by LUCI Analysis.
   235      google.protobuf.Timestamp config_version = 3;
   236  }
   237  
   238  message GetClusterRequest {
   239      // The resource name of the cluster to retrieve.
   240      // Format: projects/{project}/clusters/{cluster_algorithm}/{cluster_id}.
   241      // Designed to conform to aip.dev/131.
   242      string name = 1;
   243  }
   244  
   245  message Cluster {
   246      // The resource name of the cluster.
   247      // Format: projects/{project}/clusters/{cluster_algorithm}/{cluster_id}.
   248      string name = 1;
   249  
   250      // Whether there is a recent example in the cluster.
   251      bool has_example = 2;
   252  
   253      // A human-readable name for the cluster.
   254      // Only populated for suggested clusters where has_example = true.
   255      // Not populated for rule-based clusters.
   256      string title = 3;
   257  
   258      message Counts {
   259          // The value of the metric (summed over all failures).
   260          int64 nominal = 1;
   261      }
   262  
   263      message TimewiseCounts {
   264          // The impact value for the last day.
   265          Counts one_day = 2;
   266          // The impact value for the last three days.
   267          Counts three_day = 3;
   268          // The impact value for the last week.
   269          Counts seven_day = 4;
   270      }
   271  
   272      // The values of metrics associated with the cluster. The map key is the ID
   273      // of the metric (e.g. "human-cls-failed-presubmit").
   274      //
   275      // The following metrics are currently defined:
   276      // - "human-cls-failed-presubmit":
   277      //   The number of distinct developer changelists that failed at least one
   278      //   presubmit (CQ) run because of failure(s) in this cluster. Excludes
   279      //   changelists authored by automation.
   280      // - "critical-failures-exonerated":
   281      //   The number of failures on test variants which were configured to be
   282      //   presubmit-blocking, which were exonerated (i.e. did not actually block
   283      //   presubmit) because infrastructure determined the test variant to be
   284      //   failing or too flaky at tip-of-tree. If this number is non-zero, it
   285      //   means a test variant which was configured to be presubmit-blocking is
   286      //   not stable enough to do so, and should be fixed or made non-blocking.
   287      // - "failures":
   288      //   The total number of test results in this cluster. LUCI Analysis only
   289      //   clusters test results which are unexpected and have a status of crash,
   290      //   abort or fail, so by definition the only test results counted here
   291      //   will be an unexpected fail/crash/abort.
   292      map<string, TimewiseCounts> metrics = 10;
   293  
   294      // The failure association rule equivalent to the cluster. Populated only
   295      // for suggested clusters where has_example = true.
   296      // Not populated for rule-based clusters. If you need the failure
   297      // association rule for a rule-based cluster, use
   298      // luci.analysis.v1.Rules/Get to retrieve the rule with ID matching the
   299      // cluster ID.
   300      // Used to facilitate creating a new rule based on a suggested cluster.
   301      string equivalent_failure_association_rule = 7;
   302  
   303      // Next ID: 11.
   304  }
   305  
   306  // Designed to conform with aip.dev/131.
   307  message GetReclusteringProgressRequest {
   308      // The name of the reclustering progress resource to retrieve.
   309      // Format: projects/{project}/reclusteringProgress.
   310      string name = 1;
   311  }
   312  
   313  // ReclusteringProgress captures the progress re-clustering a
   314  // given LUCI project's test results using specific rules
   315  // versions or algorithms versions.
   316  message ReclusteringProgress {
   317      // The name of the reclustering progress resource.
   318      // Format: projects/{project}/reclusteringProgress.
   319      string name = 1;
   320  
   321      // ProgressPerMille is the progress of the current re-clustering run,
   322      // measured in thousandths (per mille). As such, this value ranges
   323      // from 0 (0% complete) to 1000 (100% complete).
   324      int32 progress_per_mille = 2;
   325  
   326      // The goal of the last completed re-clustering run.
   327      ClusteringVersion last = 5;
   328  
   329      // The goal of the current re-clustering run. (For which
   330      // ProgressPerMille is specified.) This may be the same as the
   331      // last completed re-clustering run the available algorithm versions,
   332      // rules and configuration is unchanged.
   333      ClusteringVersion next = 6;
   334  }
   335  
   336  enum ClusterSummaryView {
   337      // The default / unset value.
   338      // The API will default to the BASIC view.
   339      CLUSTER_SUMMARY_VIEW_UNSPECIFIED = 0;
   340  
   341      // Include most fields in the cluster summary, EXCLUDING
   342      // daily breakdowns of the cluster's impact metrics.
   343      BASIC = 1;
   344  
   345      // Include everything in the cluster summary.
   346      FULL = 2;
   347  }
   348  
   349  message QueryClusterSummariesRequest {
   350      // The LUCI Project.
   351      string project = 1;
   352  
   353      // An AIP-160 style filter to select test failures in the project
   354      // to cluster and calculate metrics for.
   355      //
   356      // Filtering supports a subset of [AIP-160 filtering](https://google.aip.dev/160).
   357      //
   358      // All values are case-sensitive.
   359      //
   360      // A bare value is searched for in the columns test_id and
   361      // failure_reason. E.g. ninja or "test failed".
   362      //
   363      // You can use AND, OR and NOT (case sensitive) logical operators, along
   364      // with grouping. '-' is equivalent to NOT. Multiple bare values are
   365      // considered to be AND separated.  E.g. These are equivalent:
   366      // hello world
   367      // and:
   368      // hello AND world
   369      //
   370      // More examples:
   371      // a OR b
   372      // a AND NOT(b or -c)
   373      //
   374      // You can filter particular columns with '=', '!=' and ':' (has) operators.
   375      // The right hand side of the operator must be a simple value. E.g:
   376      // test_id:telemetry
   377      // -failure_reason:Timeout
   378      // ingested_invocation_id="build-8822963500388678513"
   379      //
   380      // Supported columns to search on:
   381      // - test_id
   382      // - failure_reason
   383      // - realm
   384      // - ingested_invocation_id
   385      // - cluster_algorithm
   386      // - cluster_id
   387      // - variant_hash
   388      // - test_run_id
   389      // - tags
   390      //
   391      // Note that cost is greatly reduced (more than 90%) if exact matches for the
   392      // cluster_algorithm and cluster_id field are both provided in the filter string.
   393      string failure_filter = 2;
   394  
   395      // A comma-separated list of fields to order the response by.
   396      //
   397      // The default sorting order is ascending; to specify descending order
   398      // for a field append a " desc" suffix. The dot syntax can be used
   399      // to navigate fields and map keys, and the backtick character (``) used
   400      // to escape field names that do not match `[a-zA-Z_][a-zA-Z0-9_]`.
   401      //
   402      // The only sortable columns that are supported currently are metric
   403      // fields.
   404      //
   405      // For example, to sort by human CLs failed presubmit descending, use:
   406      // "metrics.`human-cls-failed-presubmit`.value desc".
   407      // To sort by human CLs failed presubmit followed by failures, use:
   408      // "metrics.`human-cls-failed-presubmit`.value desc, metrics.`failures`.value desc"
   409      //
   410      // For more details, see aip.dev/132 for ordering syntax, and
   411      // aip.dev/161#map-fields for navigating map fields.
   412      string order_by = 3;
   413  
   414      // The resource name(s) of the metrics to include in the cluster summaries.
   415      // Format: projects/{project}/metrics/{metric_id}.
   416      // See the metrics field on the luci.analysis.v1.Cluster message for details
   417      // about valid metric identifiers.
   418      repeated string metrics = 4;
   419  
   420      // The time range over which to get the cluster summaries.
   421      // Note: the response will include only data for the portion of the
   422      // time range that is within the data retention period of 90 days.
   423      TimeRange time_range = 5;
   424  
   425      // The level of detail that the returned cluster summaries should have. See
   426      // luci.analysis.v1.ClusterSummaryView.
   427      ClusterSummaryView view = 6;
   428  }
   429  
   430  message QueryClusterSummariesResponse {
   431      // The clusters and impact metrics from the filtered failures.
   432      repeated ClusterSummary cluster_summaries = 1;
   433  }
   434  
   435  message ClusterSummary {
   436      // The cluster ID of this cluster.
   437      luci.analysis.v1.ClusterId cluster_id = 1;
   438  
   439      // Title is a one-line description of the cluster.
   440      string title = 2;
   441  
   442      // The bug associated with the cluster. This will only be present for
   443      // rules algorithm clusters.
   444      luci.analysis.v1.AssociatedBug bug = 3;
   445  
   446      message MetricValue {
   447          // The residual value of the cluster metric.
   448          // For bug clusters, the residual metric value is the metric value
   449          // calculated using all of the failures in the cluster.
   450          // For suggested clusters, the residual metric value is calculated
   451          // using the failures in the cluster which are not also part of a
   452          // bug cluster. In this way, measures attributed to bug clusters
   453          // are not counted again against suggested clusters.
   454          int64 value = 1;
   455  
   456          // The value of the cluster metric over time, grouped by 24-hour periods
   457          // in the queried time range, in reverse chronological order
   458          // i.e. the first entry is the metric value for the 24-hour period
   459          // immediately preceding the time range's latest time.
   460          repeated int64 daily_breakdown = 2;
   461      }
   462  
   463      // The values of cluster metrics. The key of the map is the identifier
   464      // of the metric (e.g. "human-cls-failed-presubmit").
   465      // See the metrics field on the luci.analysis.v1.Cluster message for details
   466      // about valid metric identifiers.
   467      map<string, MetricValue> metrics = 7;
   468  
   469      // Next ID: 8.
   470  }
   471  
   472  message QueryClusterFailuresRequest {
   473      // The resource name of the cluster failures to retrieve.
   474      // Format: projects/{project}/clusters/{cluster_algorithm}/{cluster_id}/failures.
   475      string parent = 1;
   476  
   477      // Optional. The resource name of the metric for which failures should
   478      // be displayed.
   479      // Format: projects/{project}/metrics/{metric_id}.
   480      //
   481      // If no metrics is specified here, then no filtering is performed
   482      // and all failures are eligible to be returned. Over time, we may wish
   483      // to migrate this to an AIP-160 filter clause, e.g. "in_metric(`metric-id`)"
   484      // where in_metric is a function.
   485      string metric_filter = 2;
   486  }
   487  
   488  message QueryClusterFailuresResponse {
   489      // Example failures in the cluster.
   490      // Limited to the most recent 2000 examples.
   491      repeated DistinctClusterFailure failures = 1;
   492  }
   493  
   494  // DistinctClusterFailure represents a number of failures which have identical
   495  // properties. This provides slightly compressed transfer of examples.
   496  message DistinctClusterFailure {
   497      // Representation of an exoneration. An exoneration means the subject of
   498      // the test (e.g. a CL) is absolved from blame for the unexpected results
   499      // of the test variant.
   500      message Exoneration {
   501          // The machine-readable reason for the exoneration.
   502          luci.analysis.v1.ExonerationReason reason = 1;
   503      }
   504  
   505      // Representation of a presubmit run (e.g. LUCI CV Run).
   506      message PresubmitRun {
   507          // Identity of the presubmit run that contains this test result.
   508          // This should be unique per "CQ+1"/"CQ+2" attempt on gerrit.
   509          //
   510          // One presumbit run MAY have many ingested invocation IDs (e.g. for its
   511          // various tryjobs), but every ingested invocation ID only ever has one
   512          // presubmit run ID (if any).
   513          //
   514          // All test results for the same presubmit run will have one
   515          // partition_time.
   516          //
   517          // If the test result was not collected as part of a presubmit run,
   518          // this is unset.
   519          luci.analysis.v1.PresubmitRunId presubmit_run_id = 1;
   520  
   521          // The owner of the presubmit run (if any).
   522          // This is the owner of the CL on which CQ+1/CQ+2 was clicked
   523          // (even in case of presubmit run with multiple CLs).
   524          // There is scope for this field to become an email address if privacy
   525          // approval is obtained, until then it is "automation" (for automation
   526          // service accounts) and "user" otherwise.
   527          string owner = 2;
   528  
   529          // The mode of the presubmit run. E.g. DRY_RUN, FULL_RUN, QUICK_DRY_RUN.
   530          luci.analysis.v1.PresubmitRunMode mode = 3;
   531  
   532          // The status of the presubmit run. E.g. succeeded, failed or cancelled.
   533          luci.analysis.v1.PresubmitRunStatus status = 4;
   534      }
   535  
   536      // The identity of the test.
   537      string test_id = 1;
   538  
   539      // Description of one specific way of running the test,
   540      // e.g. a specific bucket, builder and a test suite.
   541      luci.analysis.v1.Variant variant = 2;
   542  
   543      // Timestamp representing the start of the data retention period for the
   544      // test results in this group.
   545      // The partition time is usually the presubmit run start time (if any) or
   546      // build start time.
   547      google.protobuf.Timestamp partition_time = 3;
   548  
   549      // Details if the presubmit run associated with these results (if any).
   550      PresubmitRun presubmit_run = 4;
   551  
   552      // Whether the build was critical to a presubmit run succeeding.
   553      // If the build was not part of a presubmit run, this field should
   554      // be ignored.
   555      bool is_build_critical = 5;
   556  
   557      // The exonerations applied to the test variant verdict.
   558      repeated Exoneration exonerations = 6;
   559  
   560      // The status of the build that contained this test result. Can be used
   561      // to filter incomplete results (e.g. where build was cancelled or had
   562      // an infra failure). Can also be used to filter builds with incomplete
   563      // exonerations (e.g. build succeeded but some tests not exonerated).
   564      // This is the build corresponding to ingested_invocation_id.
   565      luci.analysis.v1.BuildStatus build_status = 7;
   566  
   567      // The invocation from which this test result was ingested. This is
   568      // the top-level invocation that was ingested, an "invocation" being
   569      // a container of test results as identified by the source test result
   570      // system.
   571      //
   572      // For ResultDB, LUCI Analysis ingests invocations corresponding to
   573      // buildbucket builds.
   574      string ingested_invocation_id = 8;
   575  
   576      // Is the ingested invocation blocked by this test variant? This is
   577      // only true if all (non-skipped) test results for this test variant
   578      // (in the ingested invocation) are unexpected failures.
   579      //
   580      // Exoneration does not factor into this value; check exonerations
   581      // to see if the impact of this ingested invocation being blocked was
   582      // mitigated by exoneration.
   583      bool is_ingested_invocation_blocked = 9;
   584  
   585      // The unsubmitted changelists that were tested (if any).
   586      // Up to 10 changelists are captured.
   587      repeated luci.analysis.v1.Changelist changelists = 10;
   588  
   589      // The number of test results which have these properties.
   590      int32 count = 11;
   591  }
   592  
   593  message QueryClusterExoneratedTestVariantsRequest {
   594      // The resource name of the cluster exonerated test variants to retrieve.
   595      // Format: projects/{project}/clusters/{cluster_algorithm}/{cluster_id}/exoneratedTestVariants.
   596      string parent = 1;
   597  }
   598  
   599  message QueryClusterExoneratedTestVariantsResponse {
   600      // A list of test variants in the cluster which have exonerated critical
   601      // failures. Ordered by recency of the exoneration (most recent exonerations
   602      // first) and limited to at most 100 test variants.
   603      repeated ClusterExoneratedTestVariant test_variants = 1;
   604  }
   605  
   606  // ClusterExoneratedTestVariant represents a test variant in a cluster
   607  // which has been exonerated. A cluster test variant is the subset
   608  // of a test variant that intersects with the failures of a cluster.
   609  message ClusterExoneratedTestVariant {
   610      // A unique identifier of the test in a LUCI project.
   611      string test_id = 1;
   612  
   613      // Description of one specific way of running the test,
   614      // e.g. a specific bucket, builder and a test suite.
   615      luci.analysis.v1.Variant variant = 2;
   616  
   617      // The number of critical (presubmit-blocking) failures in the
   618      // cluster which have been exonerated on this test variant
   619      // in the last week.
   620      int32 critical_failures_exonerated = 3;
   621  
   622      // The partition time of the most recent exoneration of a
   623      // critical failure.
   624      google.protobuf.Timestamp last_exoneration = 4;
   625  }
   626  
   627  message QueryClusterExoneratedTestVariantBranchesRequest {
   628      // The resource name of the cluster exonerated test variant branches to retrieve.
   629      // Format: projects/{project}/clusters/{cluster_algorithm}/{cluster_id}/exoneratedTestVariantBranches.
   630      string parent = 1;
   631  }
   632  
   633  message QueryClusterExoneratedTestVariantBranchesResponse {
   634      // A list of test variants branches in the cluster which have exonerated
   635      // critical failures. Ordered by recency of the exoneration (most recent
   636      // exonerations first) and limited to at most 100 test variant branches.
   637      //
   638      // Pagination following AIP-158 may be implemented in future if
   639      // more than 100 items is needed.
   640      repeated ClusterExoneratedTestVariantBranch test_variant_branches = 1;
   641  }
   642  
   643  // ClusterExoneratedTestVariantBranch represents a (test, variant, source ref)
   644  // in a cluster which has been exonerated. A cluster test variant branch is
   645  // the subset of a test variant branch that intersects with the failures of a
   646  // cluster.
   647  message ClusterExoneratedTestVariantBranch {
   648      // The LUCI project.
   649      string project = 1;
   650  
   651      // A unique identifier of the test in a LUCI project.
   652      string test_id = 2;
   653  
   654      // Description of one specific way of running the test,
   655      // e.g. a specific bucket, builder and a test suite.
   656      luci.analysis.v1.Variant variant = 3;
   657  
   658      // The branch in source control that was tested, if known.
   659      // For example, the `refs/heads/main` branch in the `chromium/src` repo
   660      // hosted by `chromium.googlesource.com`.
   661      luci.analysis.v1.SourceRef source_ref = 4;
   662  
   663      // The number of critical (presubmit-blocking) failures in the
   664      // cluster which have been exonerated on this test variant
   665      // in the last week.
   666      int32 critical_failures_exonerated = 5;
   667  
   668      // The partition time of the most recent exoneration of a
   669      // critical failure.
   670      google.protobuf.Timestamp last_exoneration = 6;
   671  }
   672  
   673  message QueryClusterHistoryRequest {
   674      // The LUCI Project.
   675      string project = 1;
   676  
   677      // An AIP-160 style filter to select test failures in the project
   678      // to calculate metrics for.
   679      //
   680      // See the description of the QueryClusterSummariesRequest.failure_filter
   681      // above for the format of this field.
   682      //
   683      // Note that cost is greatly reduced (more than 90%) if exact matches for the
   684      // cluster_algorithm and cluster_id field are both provided in the filter string.
   685      string failure_filter = 2;
   686  
   687      // The number of days of history to return.  Maximum of 90 as only 90 days of
   688      // history is kept by LUCI Analysis.  Note that the cost of the query scales
   689      // linearly with the number of days.
   690      int32 days = 3;
   691  
   692      // The resource name(s) of the metrics to include in the cluster histories.
   693      // Format: projects/{project}/metrics/{metric_id}.
   694      // See the metrics field on the luci.analysis.v1.Cluster message for details
   695      // about valid metric identifiers.
   696      repeated string metrics = 4;
   697  }
   698  
   699  message QueryClusterHistoryResponse {
   700      // The metrics for each day.  There will be the same number of days as
   701      // requested in the request.  The entries will be returned in sorted date
   702      // order, earliest day first.
   703      repeated ClusterHistoryDay days = 1;
   704  }
   705  
   706  // Represents metrics about a cluster on a specific day.
   707  message ClusterHistoryDay {
   708      // A map from requested metric name to the value of that metric on this day.
   709      // The key of the map is the metric ID.
   710      map<string, int32> metrics = 1;
   711  
   712      // The date that these metrics are for.
   713      // This is a UTC date in ISO 8601 format, e.g. 2022-11-29
   714      string date = 2;
   715  }