OTEP-0035: OpenTelemetryプロトコル仕様

著者: Tigran Najaryan, Omnition Inc.

OpenTelemetry Protocol(OTLP)仕様は、テレメトリーソース、Collectorなどの中間ノード、テレメトリーバックエンドの間でテレメトリーデータをやり取りするためのエンコーディング、トランスポート、配信メカニズムを記述します。

目次

動機

OTLPは、OpenTelemetryプロジェクトの範囲内で設計された汎用のテレメトリーデータ配信プロトコルです。 これはOpenCensusプロトコルの漸進的な改善です。 OpenCensusプロトコルと比較して、OTLPには次のような改善点があります。

  • データ配信の高い信頼性と、データを配信できない場合の明確な可視性を確保します。 OTLPは信頼性の高い配信を実現するために確認応答(acknowledgement)を使用します。
  • レイヤー7ロードバランサーにフレンドリーであり、不均衡な受信トラフィックを均衡の取れた送信トラフィックに正しくマッピングすることを可能にします。 これにより、テレメトリーデータの生成レートが時間とともに変化するような、大規模なノードネットワークを効率的に運用することができます。
  • テレメトリーデータの宛先から送信元へのバックプレッシャーのシグナリングを可能にします。 これは、送信元から宛先まで、それぞれ異なる処理能力を持ち、したがって異なるデータ転送レートを必要とする中間ノードを経由する、信頼性の高いマルチホップのテレメトリーデータ配信を実装するために重要です。

プロトコルの詳細

OTLPは、テレメトリーデータのエンコーディングと、クライアントとサーバーの間でデータを交換するために使用されるプロトコルを定義します。

この仕様は、OTLPがgRPC上でどのように実装されるかを定義し、対応するProtocol Buffersのスキーマを規定します。 OTLPの将来的な拡張では、他のトランスポート上での実装が定義される可能性があります。 gRPCサービス定義の詳細については、gRPCトランスポートのセクションを参照してください。

OTLPはリクエスト/レスポンス形式のプロトコルです。 クライアントがリクエストを送信し、サーバーが対応するレスポンスで応答します。 このドキュメントでは、Export という1つのリクエストとレスポンスのタイプを定義します。

エクスポートリクエストとレスポンス

基盤となるトランスポートを確立した後、クライアントは Export リクエストを使ってテレメトリーデータの送信を開始します。 クライアントは一連の Export リクエストをサーバーに継続的に送信し、各リクエストに対するレスポンスを受け取ることを期待します。

Request-Response

注: このプロトコルは、クライアント/サーバーノードの1つのペアの間の配信の信頼性に関するものであり、クライアントとサーバーの間の転送中にデータが失われないことを保証することを目的としています。 多くのテレメトリー収集システムには、データが最終的な宛先に到達するまでに経由しなければならない中間ノードが存在します(例:アプリケーション → エージェント → Collector → バックエンド)。 このようなシステムにおけるエンドツーエンドの配信保証は、OTLPの範囲外です。 このプロトコルで説明されている確認応答は、単一のクライアント/サーバーのペアの間で発生するものであり、マルチホップの配信パスにおける中間ノードにはまたがりません。

gRPCを使ったOTLP

gRPCトランスポートでは、OTLPはUnary RPCを使ってエクスポートリクエストを送信し、レスポンスを受信します。

クライアントはリクエストを送信した後、サーバーからレスポンスを受信するまで待機してもよい(MAY)です。 その場合、サーバーによってまだ確認応答されていないリクエストは、最大でも1つだけ存在することになります。

Unary

逐次的な動作は、実装のシンプルさが望まれる場合や、クライアントとサーバーが非常に低遅延のネットワークで接続されている場合、たとえばクライアントが計装されたアプリケーションであり、サーバーがローカルデーモンとして動作するOpenTelemetry Serviceである場合に推奨されます。

高いスループットを達成する必要がある実装は、より高いスループットを達成するために並行したUnary呼び出しをサポートするべき(SHOULD)です。 クライアントは、以前に送信したリクエストへのレスポンスを待たずに新しいリクエストを送信するべき(SHOULD)であり、これによって基本的に、確認応答されていない転送中のリクエストのパイプラインが作られます。

Streaming

並行リクエストの数は設定可能であるべき(SHOULD)です。

達成可能な最大スループットは max_concurrent_requests * max_request_size / (network_latency + server_response_time) です。 たとえば、リクエストが最大100個のスパンを含むことができ、ネットワークの往復遅延が200ms、サーバーのレスポンス時間が300msである場合、並行リクエスト数が1の場合の達成可能な最大スループットは 100 spans / (200ms+300ms) 、つまり1秒あたり200スパンになります。 高遅延のネットワークや、サーバーのレスポンス時間が長い場合に良好なスループットを達成するには、リクエストを非常に大きくするか、多くの並行リクエストを行う必要があることは容易にわかります。

クライアントがシャットダウンしている場合(たとえば、それを含むプロセスが終了しようとしている場合)、クライアントは任意で、保留中のすべての確認応答を受信するまで、または実装依存のタイムアウトが切れるまで待機します。 これにより、テレメトリーデータの信頼性の高い配信が保証されます。 クライアントの実装は、シャットダウン中の待機をオンオフするオプションを公開するべき(SHOULD)です。

クライアントが特定のリクエストを配信できない場合(たとえば、確認応答を待っている間にタイマーが切れた場合)、クライアントはデータが配信されなかったという事実を記録するべき(SHOULD)です。

エクスポートレスポンス

サーバーは、エクスポートリクエストに対して成功またはエラーのいずれかで応答することがあります。

成功レスポンスは、テレメトリーデータがサーバーによって正常に処理されたことを示します。 サーバーが空のリクエスト(テレメトリーデータを一切含まないリクエスト)を受信した場合、サーバーは成功で応答するべき(SHOULD)です。

gRPCトランスポートを使用する場合、成功レスポンスは ExportResponse メッセージを介して返されます。

サーバーによってエラーが返される場合、それは再試行可能(retryable)と再試行不可能(not-retryable)の2つの大きなカテゴリーに分類されます。

  • 再試行可能なエラーは、テレメトリーデータの処理が失敗したことを示し、クライアントはそのエラーを記録するべき(SHOULD)であり、同じデータのエクスポートを再試行してもよいです。 これは、サーバーが一時的にデータを処理できない場合に発生することがあります。
  • 再試行不可能なエラーは、テレメトリーデータの処理が失敗したことを示し、クライアントは同じテレメトリーデータの送信を再試行してはならない(MUST NOT)です。 テレメトリーデータは破棄されなければなりません(MUST)。 これは、たとえば、リクエストに不正なデータが含まれており、サーバーによってデシリアライズやその他の処理ができない場合などに発生することがあります。 クライアントは、このように破棄されたデータのカウンターを保持するべき(SHOULD)です。

gRPCトランスポートを使用する場合、サーバーは再試行可能なエラーを示すためにUnavailableコードを使用するべき(SHOULD)であり、RetryInfoを使ってステータス経由の追加の詳細を、RetryDelayの値を0にして提供してもよい(MAY)です。 以下は、それを示すサンプルのGoコードです。

  // Do this on server side.
  st, err := status.New(codes.Unavailable, "Server is unavailable").
    WithDetails(&errdetails.RetryInfo{RetryDelay: &duration.Duration{Seconds: 0}})
  if err != nil {
    log.Fatal(err)
  }

  return st.Err()

再試行不可能なエラーを示すために、サーバーはInvalidArgumentコードを使用することが推奨されており、BadRequestを使ってステータス経由の追加の詳細を提供してもよい(MAY)です。 より適切な場合は、他のgRPCステータスコードを使用してもよいです。 以下は、それを示すサンプルのGoコードです。

  // Do this on server side.
  st, err := status.New(codes.InvalidArgument, "Invalid Argument").
    WithDetails(&errdetails.BadRequest{})
  if err != nil {
    log.Fatal(err)
  }

  return st.Err()

特定のエラー状況において他のgRPCコードがより適切である場合、サーバーは再試行可能・再試行不可能を示すために他のgRPCコードを使用してもよい(MAY)です。 クライアントは、次の表に従ってgRPCステータスコードを再試行可能または再試行不可能と解釈するべき(SHOULD)です。

gRPCコード再試行可能か
CANCELLEDはい
UNKNOWNいいえ
INVALID_ARGUMENTいいえ
DEADLINE_EXCEEDEDはい
NOT_FOUNDいいえ
ALREADY_EXISTSいいえ
PERMISSION_DENIEDいいえ
UNAUTHENTICATEDいいえ
RESOURCE_EXHAUSTEDはい
FAILED_PRECONDITIONいいえ
ABORTEDはい
OUT_OF_RANGEはい
UNIMPLEMENTEDいいえ
INTERNALいいえ
UNAVAILABLEはい
DATA_LOSSはい

再試行する際、クライアントはバックオフ戦略を実装するべき(SHOULD)です。 この例外は、以下で説明するスロットリングのケースであり、再試行間隔について明示的な指示を提供します。

スロットリング

OTLPはバックプレッシャーのシグナリングを可能にします。

サーバーがクライアントから受信するデータのペースに追いつくことができない場合、サーバーはその事実をクライアントに知らせるべき(SHOULD)です。 クライアントは、サーバーを圧倒しないように、自身をスロットルしなければなりません(MUST)。

gRPCトランスポートを使用する際にバックプレッシャーを知らせるには、サーバーはUnavailableコードでエラーを返すべき(SHOULD)であり、RetryInfoを使ってステータス経由の追加の詳細を提供してもよい(MAY)です。 以下は、それを示すサンプルのGoコードです。

  // Do this on server side.
  st, err := status.New(codes.Unavailable, "Server is unavailable").
    WithDetails(&errdetails.RetryInfo{RetryDelay: &duration.Duration{Seconds: 30}})
  if err != nil {
    log.Fatal(err)
  }

  return st.Err()
  
  ...

  // Do this on client side.
  st := status.Convert(err)
  for _, detail := range st.Details() {
    switch t := detail.(type) {
    case *errdetails.RetryInfo:
      if t.RetryDelay.Seconds > 0 || t.RetryDelay.Nanos > 0 {
        // Wait before retrying.
      }
    }
  }

クライアントがこのシグナルを受信した場合、RetryInfo のドキュメントに記載されている推奨事項に従うべき(SHOULD)です。

// Describes when the clients can retry a failed request. Clients could ignore
// the recommendation here or retry when this information is missing from error
// responses.
//
// It's always recommended that clients should use exponential backoff when
// retrying.
//
// Clients should wait until `retry_delay` amount of time has passed since
// receiving the error response before retrying.  If retrying requests also
// fail, clients should use an exponential backoff scheme to gradually increase
// the delay between retries based on `retry_delay`, until either a maximum
// number of retires have been reached or a maximum retry delay cap has been
// reached.

retry_delay の値はサーバーによって決定され、実装依存です。 サーバーは、サーバーが回復するための十分な時間を与えつつ、クライアントがスロットルされている間にデータを破棄してしまうほど大きすぎない retry_delay の値を選択するべき(SHOULD)です。

gRPCサービス定義

Export リクエストとレスポンスは、unaryのgRPC呼び出しを使って配信されます。

これがOTLP over gRPCのサービス定義です。

service UnaryExporter {
  rpc ExportTraces(TraceExportRequest) returns (ExportResponse) {}
  rpc ExportMetrics(MetricExportRequest) returns (ExportResponse) {}
}

付録Aには、TraceExportRequestMetricExportRequestExportResponse のProtocol Buffer定義が含まれています。

その他のトランスポート

OTLPは、メッセージのリクエスト/レスポンス機能をサポートする他の任意のトランスポート上で動作できます。 OTLPがサポートする追加のトランスポートは、OTLPを拡張する将来のRFCで規定される可能性があります。

実装の推奨事項

複数の宛先へのエクスポート

1つのクライアントからのテレメトリーデータを複数の宛先サーバーに送信しなければならない場合、考慮しなければならない追加の複雑さが生じます。 サーバーの一方がデータを確認応答し、もう一方が(まだ)確認応答していない場合、クライアントはどのように進めるべきかを判断する必要があります。

このような状況では、クライアントは宛先ごとにキューイング、確認応答の処理、再試行のロジックを実装するべき(SHOULD)です。 これにより、サーバー同士が互いをブロックしないことが保証されます。 キューは、送信対象の共有された不変のデータを参照するべき(SHOULD)であり、これによって複数のキューを持つことによるメモリオーバーヘッドを最小限に抑えます。

Multi-Destination Exporting

これにより、すべての宛先サーバーが、その受信速度にかかわらず(クライアント側のキューのサイズによって課される利用可能な制限の範囲内で)データを受信することが保証されます。

トレードオフと緩和策

リクエストの確認応答

重複データ

エッジケース(再接続時、ネットワークの中断時など)では、確認応答がまだ受信されていない場合、クライアントは最近送信したデータが配信されたかどうかを知る方法がありません。 クライアントは通常、配信を保証するためにそのようなデータを再送信することを選択し、その結果、サーバー側で重複データが生じる可能性があります。 これは意図的な選択であり、テレメトリーデータにとって正しいトレードオフであると考えられています。

部分的な成功

このプロトコルは、サーバーからクライアントへの部分的な受信成功(すなわち、データの一部はサーバーが受信できたが、一部はできなかった場合)を伝えようとはしません。 そうしようとすると、プロトコルと実装が大幅に複雑になるため、将来の作業領域の可能性として除外されています。

将来のバージョンと相互運用性

OTLPは時間とともに進化し、変化していきます。 OTLPの将来のバージョンは、異なるバージョンのOTLPを実装するクライアントとサーバーが相互運用し、テレメトリーデータを交換できることを保証する方法で設計・実装されなければなりません(MUST)。 古いクライアントは新しいサーバーと通信できなければならず、その逆も同様です(MUST)。 OTLPの新しいバージョンが、OTLPの古いバージョンを実装するノードでは理解・サポートできない新しい機能を導入する場合、プロトコルは機能的な観点から見て最小公倍数(lowest common denominator)に後退しなければなりません(MUST)。

可能な場合、廃止が宣言されていないOTLPのすべてのバージョンの間で相互運用性が保証されるべき(SHOULD)です。

OTLPは明示的なプロトコルバージョン番号を使用しません。 異なるバージョンのOTLPのクライアントとサーバーの相互運用性は、次の概念に基づいています。

  1. OTLP(現在および将来のバージョン)は、一連の機能(capability)を定義し、そのうちのいくつかは必須であり、他は任意です。 クライアントとサーバーは必須の機能を実装しなければならず、任意の機能についてはそのサブセットのみを実装することを選択できます。
  2. プロトコルへの小さな変更については、OTLPの将来のバージョンおよび拡張は、後方互換性のある方法でメッセージスキーマを進化させるProtocol Buffersの機能を使用することが推奨されます。 OTLPの新しいバージョンは、これらの新しいフィールドを理解しないクライアントとサーバーによって無視される新しいフィールドをメッセージに追加する場合があります。 多くの場合、そのようなスキーマ変更を慎重に設計し、新しいフィールドのデフォルト値を正しく選択することで、ノードが相手のノードが異なる機能を持っていることを明示的に検出しなくても、異なるバージョン間の相互運用性を保証するのに十分です。
  3. より重要な変更は、将来のRFCで新しい任意の機能として明示的に定義されなければなりません(MUST)。 このような機能は、基盤となるトランスポートを確立した後、クライアントとサーバーの実装によって発見されるべき(SHOULD)です。 正確な発見メカニズムは、新しい機能を定義する将来のRFCで説明されるべき(SHOULD)であり、通常はクライアントからサーバーへの発見リクエスト/レスポンスのメッセージ交換によって実装できます。 この仕様で定義されている必須の機能は暗黙的であり、発見を必要としません。 新しい任意の機能をサポートする実装は、その機能をサポートしていない相手の期待に合わせて動作を調整しなければなりません(MUST)。

OTLPの現在のバージョンは、必須の機能のみを記述する初期バージョンです。 この仕様の実装は、相手の機能を検出しようとするべきではなく(SHOULD NOT)、このドキュメントで定義されているとおりに動作するべきです。

先行技術、代替案、将来の可能性

私たちは、Unary RPC呼び出しの代わりにgRPCストリーミングを使用することを検討しました。 これには、L7ロードバランサーにフレンドリーであるために、実装が定期的に手動でストリームのクローズとオープンを行う必要があります。 gRPCストリーミングを使用したリファレンス実装では、大きな利点がないまま、コードが大幅に複雑でエラーが発生しやすくなることが示されました。 このため、Unary RPCが選択されました。

OTLPは、Omnitionにおける本番環境での修正の調査とテストに基づく、OpenCensusプロトコルの進化形です。 修正には、データフォーマットの変更(RFC0059を参照)、Unary RPCの使用、バックプレッシャーのシグナリング機能が含まれます。

OTLPはデータのエンコーディングにProtocol Buffersを使用しています。 代替アプローチとして、FlatBuffersとCapnprotoという2つの他のエンコーディングが検討されました。 どちらの代替案も却下されました。 FlatBuffersは、C++以外のすべての言語で必要な機能を欠いていること、特にデコードされたデータの検証ができないことと、インメモリデータをミューテートできないことから却下されました。 Capnprotoは、まだ本番環境で使用できる準備が整っていないと考えられていること、APIがまだ安定していないこと、そしてFlatBuffersと同様にインメモリデータをミューテートする能力を欠いていることから却下されました。

FlatBuffersとCapnprotoの両方は、現在知られている制限を克服した場合、OpenTelemetryプロトコルの将来のバージョンのために再評価する価値があります。

gRPC以外のトランスポートを調査することも価値があります。 時間的な制約のため、他のトランスポートはこのRFCには含まれていません。

WebSocket上でのOTLPの実験的な実装が存在し、代替案として調査されました。 WebSocketは、特定の機能の欠如または未成熟(たとえば、RFC 7692のメッセージ圧縮拡張に対する普遍的なサポートの欠如など)のため、OTLPの主要なトランスポートとしては選択されませんでした。 制限はあるものの、この実験的な実装は良好なパフォーマンスを示しており、WebSocketトランスポートは将来のOTLP Extensions RFCへの組み込みが検討される予定です。

未解決の問題

テレメトリープロトコルの目標の1つは、ガベージコレクションを行う言語におけるCPU使用率とメモリの圧迫を削減することです。 これらの目標は、このRFCの一部としては取り組まれておらず、未解決のままです。 これに取り組むための将来の有望な方法の1つは、よりCPUとメモリの効率が良いエンコーディングメカニズムを見つけることです。

テレメトリープロトコルのもう1つの目標は、CPU消費を低く保ちながら、テレメトリーデータの高い圧縮率を達成することです。 OTLPはgRPCトランスポートが提供する圧縮を使用します。 圧縮に対するさらなる改善は、このRFCの一部としては検討されておらず、将来の作業領域です。

付録A - Protocol Buffer定義

これは、Export リクエストとレスポンスのためのProtocol Buffersスキーマです。

// A request from client to server containing trace data to export.
message TraceExportRequest {
    // Telemetry data. An array of ResourceSpans.
    repeated ResourceSpans resourceSpans = 2;
}

// A request from client to server containing metric data to export.
message MetricExportRequest {
    // Telemetry data. An array of ResourceMetrics.
    repeated ResourceMetrics resourceMetrics = 2;
}

// A response to ExportRequest.
message ExportResponse {
    // Response in an empty message.
}

// A list of spans from a Resource.
message ResourceSpans {
  Resource resource = 1;
  repeated Span spans = 2;
}

// A list of metrics from a Resource.
message ResourceMetrics {
  Resource resource = 1;
  repeated Metric metrics = 2;
}

SpanMetricResource のスキーマ定義はRFCNNNN(RFC番号は未定で、ここからリンクされる予定)で定義されています。

付録B - パフォーマンスベンチマーク

OTLPと他のテレメトリープロトコルとのベンチマークは、Goによるリファレンス実装を使って行われました。

スループット - 逐次実行 vs 並行実行

20の並行リクエストを使用すると、さまざまなネットワーク往復遅延の値に対して、逐次実行と比較して次のようなスループットの優位性がベンチマークで示されています。

+-----------+-----------------------+
+ Latency   | Concurrent/Sequential |
+           |   Throughput Factor   |
+-----------+-----------------------+
+   0.02 ms |          1.7          |
+   2 ms    |          2.1          |
+  20 ms    |          4.9          |
+ 200 ms    |          6.9          |
+-----------+-----------------------+

ベンチマークは、それぞれ10個の小さな属性を含む500個のスパンを運ぶExportリクエストを使って行われています。

CPU使用率 - gRPC vs WebSocket/実験的

WebSocketトランスポートを使用した実験的な実装では、小さなバッチでgRPCトランスポートと比較して約30%少ないCPU使用率を、大きなバッチで約7%少ないCPU使用率を示しました。

これは、オーバーヘッドの少ない異なるトランスポートを調査することが将来有望な方向性であることを示しています。

ベンチマークの生データ

以下は、i7 7500Uプロセッサー、16GB RAMのシステムで実行したベンチマーク結果です(ベンチマークスクリプトは、実行中により一貫した結果を得るために、CPUガバナーを「パフォーマンス」に設定し、プロセスのnice値を設定することに注意してください)。

====================================================================================
Legend:
GRPC/Stream/LBTimed/Sync    - GRPC, streaming, load balancer friendly, close stream every 30 sec, with ack
GRPC/Stream/LBTimed/Async/N - OTLP Streaming. GRPC, N streams, load balancer friendly, close stream every 30 sec, with async ack
GRPC/Unary                  - OTLP Unary. One request per batch, load balancer friendly, with ack
GRPC/Unary/Async            - GRPC, unary async request per batch, load balancer friendly, with ack
GRPC/OpenCensus             - OpenCensus protocol, streaming, not load balancer friendly, without ack
GRPC/OpenCensusWithAck      - OpenCensus-like protocol, streaming, not load balancer friendly, with ack
GRPC/Stream/NoLB            - GRPC, streaming, not load balancer friendly, with ack
GRPC/Stream/LBAlways/Sync   - GRPC, streaming, load balancer friendly, close stream after every batch, with ack
GRPC/Stream/LBSrv/Async     - OTLP Streaming. Load balancer friendly, server closes stream every 30 sec or 1000 batches, with async ack
WebSocket/Stream/Sync       - WebSocket, streaming, unknown load balancer friendliness, with sync ack
WebSocket/Stream/Async      - WebSocket, streaming, unknown load balancer friendliness, with async ack
WebSocket/Stream/Async/zlib - WebSocket, streaming, unknown load balancer friendliness, with async ack, zlib compression


8000 small batches, 100 spans per batch, 4 attrs per span
GRPC/Stream/LBTimed/Async/1   800000 spans, CPU time  12.4 sec, wall time   5.3 sec, 645.7 batches/cpusec, 1510.0 batches/wallsec
GRPC/Stream/LBTimed/Async/10  800000 spans, CPU time  12.3 sec, wall time   3.9 sec, 650.9 batches/cpusec, 2058.4 batches/wallsec
GRPC/Unary                    800000 spans, CPU time  15.3 sec, wall time   9.5 sec, 523.2 batches/cpusec, 840.0 batches/wallsec
GRPC/Unary/Async              800000 spans, CPU time  14.1 sec, wall time   4.0 sec, 565.8 batches/cpusec, 1986.3 batches/wallsec
GRPC/OpenCensus               800000 spans, CPU time  21.7 sec, wall time  10.6 sec, 368.7 batches/cpusec, 751.5 batches/wallsec
GRPC/OpenCensusWithAck        800000 spans, CPU time  23.4 sec, wall time  19.0 sec, 342.3 batches/cpusec, 420.8 batches/wallsec
GRPC/Stream/NoLB              800000 spans, CPU time  13.6 sec, wall time   9.4 sec, 588.2 batches/cpusec, 848.7 batches/wallsec
GRPC/Stream/LBAlways/Sync     800000 spans, CPU time  16.1 sec, wall time  10.0 sec, 495.7 batches/cpusec, 798.8 batches/wallsec
GRPC/Stream/LBTimed/Sync      800000 spans, CPU time  13.7 sec, wall time   9.5 sec, 585.7 batches/cpusec, 845.1 batches/wallsec
GRPC/Stream/LBSrv/Async       800000 spans, CPU time  12.7 sec, wall time  12.5 sec, 628.9 batches/cpusec, 639.8 batches/wallsec
WebSocket/Stream/Sync         800000 spans, CPU time   8.4 sec, wall time   8.3 sec, 949.0 batches/cpusec, 965.3 batches/wallsec
WebSocket/Stream/Async        800000 spans, CPU time   9.4 sec, wall time   5.4 sec, 852.0 batches/cpusec, 1492.0 batches/wallsec
WebSocket/Stream/Async/zlib   800000 spans, CPU time  23.3 sec, wall time  16.5 sec, 343.8 batches/cpusec, 484.0 batches/wallsec

800 large batches, 500 spans per batch, 10 attrs per span
GRPC/Stream/LBTimed/Async/1   400000 spans, CPU time  11.4 sec, wall time   7.1 sec, 70.2 batches/cpusec, 113.1 batches/wallsec
GRPC/Stream/LBTimed/Async/10  400000 spans, CPU time  12.2 sec, wall time   5.8 sec, 65.8 batches/cpusec, 138.4 batches/wallsec
GRPC/Unary                    400000 spans, CPU time  10.7 sec, wall time   9.6 sec, 74.7 batches/cpusec, 83.2 batches/wallsec
GRPC/Unary/Async              400000 spans, CPU time  11.9 sec, wall time   5.6 sec, 67.0 batches/cpusec, 141.8 batches/wallsec
GRPC/OpenCensus               400000 spans, CPU time  23.9 sec, wall time  14.1 sec, 33.5 batches/cpusec, 56.8 batches/wallsec
GRPC/OpenCensusWithAck        400000 spans, CPU time  22.0 sec, wall time  21.1 sec, 36.4 batches/cpusec, 38.0 batches/wallsec
GRPC/Stream/NoLB              400000 spans, CPU time  10.7 sec, wall time   9.8 sec, 74.9 batches/cpusec, 81.8 batches/wallsec
GRPC/Stream/LBAlways/Sync     400000 spans, CPU time  11.5 sec, wall time  10.2 sec, 69.9 batches/cpusec, 78.2 batches/wallsec
GRPC/Stream/LBTimed/Sync      400000 spans, CPU time  11.1 sec, wall time  10.2 sec, 71.9 batches/cpusec, 78.4 batches/wallsec
GRPC/Stream/LBSrv/Async       400000 spans, CPU time  11.3 sec, wall time   7.0 sec, 70.5 batches/cpusec, 113.6 batches/wallsec
WebSocket/Stream/Sync         400000 spans, CPU time  10.3 sec, wall time  10.1 sec, 78.0 batches/cpusec, 79.4 batches/wallsec
WebSocket/Stream/Async        400000 spans, CPU time  10.5 sec, wall time   7.2 sec, 76.2 batches/cpusec, 111.2 batches/wallsec
WebSocket/Stream/Async/zlib   400000 spans, CPU time  29.0 sec, wall time  22.1 sec, 27.6 batches/cpusec, 36.1 batches/wallsec

2ms network roundtrip latency
800 large batches, 500 spans per batch, 10 attrs per span
GRPC/Stream/LBTimed/Async/1   400000 spans, CPU time  11.1 sec, wall time   7.0 sec, 71.9 batches/cpusec, 114.9 batches/wallsec
GRPC/Stream/LBTimed/Async/10  400000 spans, CPU time  11.4 sec, wall time   5.4 sec, 70.5 batches/cpusec, 148.0 batches/wallsec
GRPC/Unary                    400000 spans, CPU time  11.5 sec, wall time  11.8 sec, 69.5 batches/cpusec, 68.1 batches/wallsec
GRPC/Unary/Async              400000 spans, CPU time  11.3 sec, wall time   5.3 sec, 70.5 batches/cpusec, 150.4 batches/wallsec
GRPC/OpenCensus               400000 spans, CPU time  23.1 sec, wall time  13.6 sec, 34.6 batches/cpusec, 58.7 batches/wallsec
GRPC/OpenCensusWithAck        400000 spans, CPU time  21.9 sec, wall time  22.6 sec, 36.6 batches/cpusec, 35.4 batches/wallsec
GRPC/Stream/NoLB              400000 spans, CPU time  11.1 sec, wall time  11.6 sec, 72.3 batches/cpusec, 69.2 batches/wallsec
GRPC/Stream/LBAlways/Sync     400000 spans, CPU time  11.5 sec, wall time  11.6 sec, 69.8 batches/cpusec, 68.9 batches/wallsec
GRPC/Stream/LBTimed/Sync      400000 spans, CPU time  11.3 sec, wall time  11.7 sec, 71.0 batches/cpusec, 68.2 batches/wallsec
GRPC/Stream/LBSrv/Async       400000 spans, CPU time  11.1 sec, wall time   6.9 sec, 72.0 batches/cpusec, 115.1 batches/wallsec
WebSocket/Stream/Sync         400000 spans, CPU time  10.8 sec, wall time  12.0 sec, 74.1 batches/cpusec, 66.5 batches/wallsec
WebSocket/Stream/Async        400000 spans, CPU time  10.6 sec, wall time   7.2 sec, 75.5 batches/cpusec, 111.8 batches/wallsec
WebSocket/Stream/Async/zlib   400000 spans, CPU time  28.6 sec, wall time  21.9 sec, 27.9 batches/cpusec, 36.6 batches/wallsec

20ms network roundtrip latency
400 large batches, 500 spans per batch, 10 attrs per span
GRPC/Stream/LBTimed/Async/1   200000 spans, CPU time   6.2 sec, wall time   4.1 sec, 64.9 batches/cpusec, 96.7 batches/wallsec
GRPC/Stream/LBTimed/Async/10  200000 spans, CPU time   6.2 sec, wall time   3.0 sec, 64.0 batches/cpusec, 132.9 batches/wallsec
GRPC/Unary                    200000 spans, CPU time   6.2 sec, wall time  13.5 sec, 64.3 batches/cpusec, 29.6 batches/wallsec
GRPC/Unary/Async              200000 spans, CPU time   5.9 sec, wall time   3.0 sec, 68.0 batches/cpusec, 132.9 batches/wallsec
GRPC/OpenCensus               200000 spans, CPU time  12.6 sec, wall time   7.5 sec, 31.8 batches/cpusec, 53.3 batches/wallsec
GRPC/OpenCensusWithAck        200000 spans, CPU time  12.0 sec, wall time  19.5 sec, 33.4 batches/cpusec, 20.5 batches/wallsec
GRPC/Stream/NoLB              200000 spans, CPU time   5.9 sec, wall time  13.3 sec, 68.3 batches/cpusec, 30.0 batches/wallsec
GRPC/Stream/LBAlways/Sync     200000 spans, CPU time   5.9 sec, wall time  13.3 sec, 68.0 batches/cpusec, 30.2 batches/wallsec
GRPC/Stream/LBTimed/Sync      200000 spans, CPU time   5.8 sec, wall time  13.3 sec, 69.3 batches/cpusec, 30.1 batches/wallsec
GRPC/Stream/LBSrv/Async       200000 spans, CPU time   5.5 sec, wall time   3.7 sec, 73.4 batches/cpusec, 107.3 batches/wallsec
WebSocket/Stream/Sync         200000 spans, CPU time   5.8 sec, wall time  14.6 sec, 69.4 batches/cpusec, 27.4 batches/wallsec
WebSocket/Stream/Async        200000 spans, CPU time   5.5 sec, wall time   3.9 sec, 72.3 batches/cpusec, 102.1 batches/wallsec
WebSocket/Stream/Async/zlib   200000 spans, CPU time  14.7 sec, wall time  11.2 sec, 27.3 batches/cpusec, 35.7 batches/wallsec

200ms network roundtrip latency
40 large batches, 500 spans per batch, 10 attrs per span
GRPC/Stream/LBTimed/Async/1    20000 spans, CPU time   0.5 sec, wall time   3.1 sec, 74.1 batches/cpusec, 12.7 batches/wallsec
GRPC/Stream/LBTimed/Async/10   20000 spans, CPU time   0.7 sec, wall time   3.1 sec, 61.5 batches/cpusec, 12.8 batches/wallsec
GRPC/Unary                     20000 spans, CPU time   0.6 sec, wall time   9.9 sec, 65.6 batches/cpusec,  4.0 batches/wallsec
GRPC/Unary/Async               20000 spans, CPU time   0.6 sec, wall time   3.6 sec, 65.6 batches/cpusec, 11.1 batches/wallsec
GRPC/OpenCensus                20000 spans, CPU time   1.1 sec, wall time   3.5 sec, 35.1 batches/cpusec, 11.3 batches/wallsec
GRPC/OpenCensusWithAck         20000 spans, CPU time   1.2 sec, wall time  10.2 sec, 32.8 batches/cpusec,  3.9 batches/wallsec
GRPC/Stream/NoLB               20000 spans, CPU time   0.6 sec, wall time   9.5 sec, 67.8 batches/cpusec,  4.2 batches/wallsec
GRPC/Stream/LBAlways/Sync      20000 spans, CPU time   0.6 sec, wall time   9.5 sec, 63.5 batches/cpusec,  4.2 batches/wallsec
GRPC/Stream/LBTimed/Sync       20000 spans, CPU time   0.6 sec, wall time   9.5 sec, 66.7 batches/cpusec,  4.2 batches/wallsec
GRPC/Stream/LBSrv/Async        20000 spans, CPU time   0.5 sec, wall time   3.3 sec, 74.1 batches/cpusec, 12.0 batches/wallsec
WebSocket/Stream/Sync          20000 spans, CPU time   0.6 sec, wall time  13.5 sec, 69.0 batches/cpusec,  3.0 batches/wallsec
WebSocket/Stream/Async         20000 spans, CPU time   0.5 sec, wall time   6.1 sec, 74.1 batches/cpusec,  6.5 batches/wallsec
WebSocket/Stream/Async/zlib    20000 spans, CPU time   1.5 sec, wall time   2.0 sec, 26.3 batches/cpusec, 19.8 batches/wallsec


400 large batches, 500 spans per batch, 10 attrs per span
200ms network roundtrip latency
GRPC/OpenCensus               200000 spans, CPU time  11.9 sec, wall time  10.1 sec, 33.6 batches/cpusec, 39.6 batches/wallsec
GRPC/Stream/LBTimed/Async/1   200000 spans, CPU time   5.3 sec, wall time   9.5 sec, 76.0 batches/cpusec, 41.9 batches/wallsec
GRPC/Stream/LBTimed/Async/10  200000 spans, CPU time   6.4 sec, wall time   8.9 sec, 62.3 batches/cpusec, 44.7 batches/wallsec
GRPC/Unary/Async              200000 spans, CPU time   5.8 sec, wall time  12.0 sec, 68.6 batches/cpusec, 33.3 batches/wallsec
WebSocket/Stream/Async        200000 spans, CPU time   5.3 sec, wall time  11.2 sec, 75.3 batches/cpusec, 35.7 batches/wallsec
WebSocket/Stream/Async/zlib   200000 spans, CPU time  15.1 sec, wall time  12.0 sec, 26.5 batches/cpusec, 33.4 batches/wallsec
====================================================================================

用語集

テレメトリーデータの交換には2つの関係者が関わります。 このドキュメントでは、テレメトリーデータの送信元となる関係者を Client と呼び、テレメトリーデータの宛先となる関係者を Server と呼びます。

Client-Server

Clientの例としては、計装されたアプリケーションやテレメトリーコレクターの送信側が挙げられ、Serverの例としては、テレメトリーバックエンドやテレメトリーコレクターの受信側が挙げられます(したがって、Collectorは、どちらの側から見るかによって、通常ClientとServerの両方になります)。

ClientとServerはどちらも Node でもあります。 この用語は、ドキュメント内でどちらか一方を指す場合に使用されます。

謝辞

ロードバランサーに関する実験の実施を手伝ってくれたOwais Lone氏、そしてプロトコルに関する思慮深い議論をしてくれたPaulo Janotti氏、Bogdan Drutu氏、Yuri Shkuro氏に特に感謝します。