スキーマの進化
イベントのスキーマを変更すると、それを使う送信コード、クエリ、ダッシュボード、アラート、エクスポートが動かなくなることがあります。Telemetry は事前のマイグレーションなしで新しい JSON フィールドを受け入れます。ただし、既存フィールドの型や意味を変える場合は、古い定義を使うコードと保存済みの行への対応を決める必要があります。
既存のフィールドの意味と型を変えないでください。変更が必要な場合は、新しいフィールドかバージョンを追加します。
互換性マトリックス
| 変更案 | インジェストの互換性 | クエリの互換性 | 推奨されるアプローチ |
|---|---|---|---|
| オプションのフィールドを追加する | 通常互換性があります | 古い行は値を返しません | 追加し、導入を測定し、消費者を更新します |
| ネストされたオブジェクトを追加する | 通常互換性があります | 古い行にはネストされたパスがありません | すべてのネストされたパスを型付けして安定した状態に保つ |
| 制御されたステータス値を追加する | データ型に互換性がある | 徹底的なフィルターでは見逃される可能性があります | 排出前に消費者を更新およびテストする |
| オプションフィールドの送信を停止する | 行では省略可能 | 消費者には欠損値が見える | まず非推奨にし、残りのリーダーを測定する |
| フィールドの名前変更または移動 | 別のフィールドを作成します | 古い消費者は古い名前を読み続けます | 二重書き込み、移行、その後のリタイア |
| 数値を文字列に変更する | 確立されたタイプと互換性がありません | 計算の種類は 1 つではなくなりました | 正しく入力された新しいフィールドを作成する |
| 名前を変更せずに単位を変更する | タイプはまだ一致する可能性があります | 結果は静かに間違ったものになる | 次のようなユニット固有のフィールドを追加します。 _ms |
| イベントグレインを変更する | 行は引き続き取り込まれます | カウントと結合が無効になる | 新しいイベント名またはメジャー バージョンを公開する |
データの追加は技術的には簡単です。互換性は、すべての下流定義、特に制御された値、単位、行粒度、ID、および時間セマンティクスにも依存します。
コンシューマを壊さずにフィールドを追加する
仮定してください api_request_completed すでに記録しています:
{
"event_id": "evt_api_01",
"route": "/v1/query/:id",
"status_code": 200,
"latency_ms": 184,
"release": "2026.07.2"
}
限界のある失敗カテゴリを追加したいとします。
{
"event_id": "evt_api_02",
"route": "/v1/query/:id",
"status_code": 503,
"latency_ms": 921,
"release": "2026.07.3",
"error_type": "upstream_unavailable"
}
段階的に展開します。
- 許可される値、プライバシー クラス、所有者、およびフィールドを設定するブランチを文書化します。
- なしで成功するためのフィクスチャを追加します
error_typeおよび予想される各障害カテゴリ。 - 既存のクエリがフィールドを無視している間にプロデューサーを解放します。
- リリースとステータスによってフィールド カバレッジを測定します。
- ダッシュボードとアラートは、関連する十分な行にそれが含まれている場合にのみ更新してください。
- 少なくとも保持されている移行期間中は、クエリを古い行に耐えられるようにしてください。
ログ API は、NULL 値、空のオブジェクト、および空の配列を削除します。したがって、「存在しない」は、値のないオプションのフィールドの予想される格納状態です。
分野に依存する前に導入を測る
部分的に移行されたコードを見つけるには、リリースまたは明示的なプロデューサー バージョンを使用します。
SELECT
release,
COUNT(*) AS failed_requests,
SUM(CASE WHEN error_type IS NULL THEN 1 ELSE 0 END) AS missing_error_type,
100.0 * SUM(CASE WHEN error_type IS NULL THEN 1 ELSE 0 END)
/ NULLIF(COUNT(*), 0) AS missing_rate_pct
FROM api_request_completed
WHERE status_code >= 500
AND timestamp_utc >= now() - INTERVAL '24 hours'
GROUP BY release
ORDER BY release;
使用しないでください COALESCE(error_type, 'none') ただし、「なし」がすべての古い値や欠損値のカテゴリとして意図されている場合は除きます。壊れたプロデューサーのロールアウトを隠すことができます。
フィールドの名前変更、移動、または再定義
名前の変更 latency_ms に duration_ms 保存されたイベント データのインプレース名前変更ではありません。二重書き込み移行を使用します。
{
"latency_ms": 184,
"duration_ms": 184,
"schema_version": 2
}
移行期間中に、優先順位を明示的にします。
SELECT
route,
approx_percentile_cont(
COALESCE(duration_ms, latency_ms),
0.95
) AS p95_duration_ms
FROM api_request_completed
WHERE timestamp_utc >= now() - INTERVAL '7 days'
GROUP BY route;
次に:
- 保存されているすべてのクエリ、ダッシュボード、アラート、エクスポート、およびコンシューマーを更新します。
- 現在のプロデューサーが古いフィールドのみを送信していないことを確認します。
- 合意された互換性期間が終わるまで待ちます。
- 古いフィールドを書き込むのをやめてください。
- 保持された古い行について、履歴クエリの動作を文書化して保持します。
使用する schema_version 安定したフィールド名の代わりとしてではなく、クエリで定義を区別する必要がある場合。バージョンは、行粒度、結果の意味、または入れ子構造が同時に変更される場合に特に役立ちます。
フィールドのタイプをその場で変更しないでください
この変更は安全ではありません:
{ "account_id": 8421 }
{ "account_id": "acct_8421" }
2 番目のプロデューサーが数値と競合しています。 account_id 初代によって確立されました。ストレージ層が両方の値を個別に表現できる場合でも、結合とフィルターは 1 つの信頼できるタイプを共有できなくなります。
次のような新しい文字列フィールドを追加します。 account_key、レビュー済みで決定的なマッピングがある場合にのみバックフィルし、コンシューマーを移行します。同じルールが以下にも適用されます。
- フォーマットされた文字列として送信される数値の期間。
- ブール値は次のように置き換えられます
"yes"そして"no"; - タイムスタンプはロケール固有の文字列に置き換えられます。
- 1 つのネストされたパスがオブジェクトからスカラーに変化します。
- あるエンティティ タイプから別のエンティティ タイプに変化する識別子。
ステータス値をスキーマとして扱う
追加 cancelled 以前に文書化されたフィールドに success または failed 文字列型は変更されませんが、ロジックが壊れる可能性があります。
SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END)
クエリはサイレントに処理します cancelled 失敗していないので。新しい値が失敗、除外、または別の結果に属するかどうかを、発行前に決定します。クエリ レジストリで徹底的なステータス フィルタを検索し、最初にフィクスチャを更新します。
ロールアウトとロールバックを検証する
実稼働前に代表的なイベントをテストします。
| 治具 | それが証明するもの |
|---|---|
| 旧バージョンの成功 | 既存の行とクエリは引き続き機能します |
| 新しいバージョンの成功 | 追加されたフィールドには予期されたタイプが含まれています |
| 新しいバージョンの失敗 | エラーのみのフィールドが存在し、制限されています |
| オプションのフィールドがありません | Null 処理は意図的なままです |
| 再試行または複製 | カウントは文書化された粒子を保存します |
| ロールバックプロデューサー | 古いデプロイメントは安全に共存できます |
導入後、受け入れられたイベントの量、必須フィールドの範囲、制御された値の分布、および主要なクエリの結果をリリースごとに比較します。ロールバックが安全なのは、古いプロデューサーが確立されたスキーマを引き続き書き込むことができ、新しいコンシューマーがフィールドの欠落を許容できる場合に限られます。
履歴データとバックフィル
スキーマの進化は将来の出来事を変える。保持されている履歴は自動的に書き換えられません。埋め戻しの前:
- 真実の正確な情報源と決定論的な変換を定義する。
- 元のイベント時間と安定した識別子を保持します。
- イベントまたは移行識別子の重複を防止します。
- 行数をテストし、制限された間隔で合計を集計します。
- どの日付とバージョンが書き換えられたかを記録します。
- ダッシュボードに混合履歴を表示するか、バックフィルされた履歴を表示するかを決定します。
古いデータが新しい意味をサポートできない場合は、データを省略したままにし、カバレッジの境界を表示します。値を作成すると、よりきれいなグラフが生成されますが、分析の信頼性は低くなります。
スキーマ変更チェックリスト
- 現在および提案されている行粒度、タイプ、単位、および意味を述べます。
- インベントリ プロデューサーとすべての下流のクエリ、ダッシュボード、アラート、エクスポート。
- 加算フィールドを好みます。グレイン変更には新しいイベントまたはバージョンを使用します。
- 成功、失敗、欠落、再試行、ロールバックのフィクスチャを追加します。
- 両方を変更する必要がある場合は、新しいライターよりも先に互換性のあるリーダーをデプロイします。
- ロールアウトが完了したと仮定するのではなく、リリースごとに導入を測定します。
- デュアル読み取りまたはデュアル書き込みウィンドウを文書化しておいてください。
- 古いパスは、使用法と保持される履歴の動作を理解した後でのみ削除してください。
続けて イベントのデータ型とnull許容性, ネストされた JSON のクエリ、 イベントスキーマカタログ、そして 必須フィールドのヌルレートレシピ.