syntax = "proto3";

package twitch.video.usher;
option go_package = "code.justin.tv/video/usherapi/rpc/usher";

import "google/protobuf/timestamp.proto";

// The cluster service provides cluster state information from usher. A Cluster
// represents a PoP on our network, or an external CDN which can serve video.
// The clusters service provides operational data like total available bandwidth,
// current bandwidth, throttles, etc.
message ClusterListRequest {
}

message ListOf_Cluster {
  repeated Cluster clusters = 1;
}

message Cluster {
  // id is the UsherDB ID of this cluster
  int64 id = 1 [json_name = "id"];

  // name is the cluster, aka POP
  //
  //   Example:
  //     "sfo01"
  //     "cdg01"
  string name = 2 [json_name = "name"];

  // enabled indicates this cluster should receive traffic
  bool enabled = 3 [json_name = "enabled"];

  // cdn indicates this cluster represents an external CDN and not a Twitch PoP
  bool cdn = 4 [json_name = "cdn"];

  // local indicates that this cluster is a twitch cluster and should have its
  // stats aggregated from the nodes which belong to it
  bool local = 5 [json_name = "local"];

  // super is a legacy field which no longer holds meaning, but has some queries
  // which depend on it. do not use this field for new work.
  bool super = 6 [json_name = "super", deprecated = true];

  // zone indicates the region this cluster is in
  //
  //   Example:
  //     "europe"
  //     "usa"
  string zone = 7 [json_name = "zone"];

  // threshold indicates the minimum number of viewers a channel should have to
  // become available for viewing at this pop.
  int32 threshold = 8 [json_name = "threshold"];

  // rep_version indicates the version of the replication system this cluster is
  // running on. as of 7/18/18 it is nearly always 4
  int32 rep_version = 9 [json_name = "rep_version"];

  // flowsd_host is no longer used and should not be used for any new work. it
  // has been included here due to pre-existing dependencies
  string flowsd_host = 10 [json_name = "flowsd_host", deprecated = true];

  // update_time is time this entry was last updated
  google.protobuf.Timestamp update_time = 20 [json_name = "updated_on_rfc3339"];

  // link_info_update_time_seconds should not be used and link_info_update_time
  // should be used instead
  int64 link_info_update_time_seconds = 21 [json_name = "link_info_updated_on"];

  // link_info_update_time is the last time the network interface data for this
  // cluster was aggregated and updated
  google.protobuf.Timestamp link_info_update_time = 22 [json_name = "link_info_update_time_rfc3339"];

  // max_disabled specifies the maximum number of nodes that may be disabled
  // concurrently in this cluster
  int64 max_disabled = 30 [json_name = "max_disabled"];

  // max_pr_disabled specifies the maximum number of pr nodes that may be
  // disabled concurrently in this cluster
  int64 max_pr_disabled = 31 [json_name = "max_pr_disabled"];

  // max_ingest_disabled specifies the maximum number of ingest nodes that may
  // be disabled concurrently in this cluster
  int64 max_ingest_disabled = 32 [json_name = "max_ingest_disabled"];

  // max_edge_disabled specifies the maximum  number of edge nodes that may be
  // disabled concurrently in this cluster
  int64 max_edge_disabled = 33 [json_name = "max_edge_disabled"];

  // mbps_max is the maximum outbound bandwidth in megabits/s for this cluster.
  // once this value is reached, no more viewers will be assigned
  double mbps_max = 40 [json_name = "mbps_max"];

  // mbps_out is the current outbound bandwidth in megabits/s for this cluster
  double mbps_out = 41 [json_name = "mbps_out"];

  // max_transit_load represents the maximum ratio of mbps_out/mbps_max at which
  // no more transit traffic will be assigned to this cluster. only peering
  // traffic will be allowed after that point
  double max_transit_load = 50 [json_name = "max_transit_load"];

  // offnet indicates this cluster is an offnet cluster, meaning servers we own
  // or manage running in an external dataceter we do not own or manage
  bool offnet = 74 [json_name = "offnet"];

  // identifying_asn is the ASN tag to look for when determining which routes
  // from routesd belong to this cluster.
  int32 identifying_asn = 75 [json_name = "identifying_asn"];
}

//
// Example
//
// {
//   "current_transit": 0,
//   "max_peering_ix": 0,
//   "max_transit_load": 0.99,
//   "precedence": 2,
//   "max_pr_disabled": 1,
//   "cdn": false,
//   "use_constraint_hoses": false,
//   "current_peering_ix": 0,
//   "boost_chance": 0,
//   "link_info_updated_on": 1479596166,
//   "boost_max_mbps": 0,
//   "threshold": 300,
//   "rep_version": 4,
//   "id": 928,
//   "max_edge_disabled": 2,
//   "zone": "europe",
//   "flowsd_host": "localhost:12347",
//   "offnet": false,
//   "mbps_max": 500000,
//   "nodes": "/cluster/nodes/mad01.json",
//   "local": true,
//   "max_disabled": 2,
//   "max_ingest_disabled": 1,
//   "identifying_asn": 65316,
//   "manual_max_transit_and_peering": false,
//   "max_peering_pni": 0,
//   "super": false,
//   "max_transit": 0,
//   "boost_route_preference": 0,
//   "name": "mad01",
//   "type": "cluster",
//   "current_peering_pni": 0,
//   "enabled": true,
//   "mbps_out": 99935.88,
//   "updated_on": "Mon Jul  2 14:52:36 2018"
// }
//

message ListOf_Threshold {
  repeated Threshold thresholds = 1;
}

// Threshold defines the minimum number of viewers a channel needs before it is replicated
// to the named cluster/POP.
//
// created_on and updated_on have been skipped because they are not currently being used and
// I suspect they are not well defined. Looking at the Usher code, it looks like it blindly returns
// whatever the database internally stores as the "insert" and "update" times for the item. The
// type of these values could change if the database depended on changes. We should wait until Usher
// has a well defined type for these values (e.g. unix epoch seconds, RFC3339, etc).
message Threshold {
  // origin is the name of the replication tree origin where this threshold is defined.
  // Currently it is always "sjc02", but once disaster recovery/resilience has been implemented
  // and duplicated sjc02, there may be multiple possible origins.
  string origin = 1;
  // name is the cluster, aka POP, which will be used if the stream exceeds the threshold
  //
  // Example:
  //   "sfo01"
  //   "cdg01"
  string name = 2;
  // threshold indicates the minimum number of viewers a channel should have to
  // become available for viewing at this pop.
  int64 threshold = 3;
}

message ClusterThresholdsRequest {
  // origin is the replication tree origin to get the cluster/POP threshold definitions from i.e "sjc02"
  string origin = 1;
}

//
// Example
//
// curl -s -H "Accept: application/json" http://video-api.internal.justin.tv/cluster/thresholds/sjc02
//
// [
//     {
//         "created_on": "2018-04-02 14:27:02.657280",
//         "id": 7,
//         "name": "slc01",
//         "origin": "sjc02",
//         "threshold": 0,
//         "updated_on": null
//     },
//     {
//         "created_on": "2018-04-02 14:27:02.657280",
//         "id": 8,
//         "name": "cloudfront_korea",
//         "origin": "sjc02",
//         "threshold": 15,
//         "updated_on": null
//     },
//     {
//         "created_on": "2018-04-18 10:50:55.958387",
//         "id": 135,
//         "name": "jfk04",
//         "origin": "sjc02",
//         "threshold": 0,
//         "updated_on": "2018-04-26 16:51:50.830615"
//     }
// ]
