代表的な面接トピック

データエンジニアリング面接:レガシーログをOpenTelemetry Logs Data Modelへ移行するにはどうするか?

データ難しい
Offer.cc 編集チーム公開日 更新日

質問

ある企業がテキスト形式およびJSON形式のレガシーログを保持しており、単一のOpenTelemetry Logs Data Modelへの統合を求めています。フィールドマッピング、時間セマンティクス、テナント分離、リダクション(墨消し)、重複排除、リプレイ、および品質ゲートについて説明してください。

課題とスコープ

ある企業が、異なるフィールド名、時間精度、重要度(severity)規則を持つアプリケーションのテキストログ、コンテナのstdout、レガシーJSONイベントを出力しています。履歴検索を維持し、誤解を招くTraceIdリンクを回避しつつ、OpenTelemetry Logs Data Modelを採用したいと考えています。オフラインのバックフィルとリアルタイムのデュアルライト(二重書き込み)計画を設計してください。

OpenTelemetryは、Timestamp、ObservedTimestamp、TraceId、SpanId、SeverityNumber、Body、Resource、Attributesを明確に分離します。この移行は追跡可能なデータコントラクトの構築であり、すべての行を単にBodyに詰め込むことではありません。優れた回答では、パーサーの失敗、タイムゾーン、重複、機密フィールド、リプレイのウォーターマークに対処します。

面接官が評価するポイント

  • イベント時間、観測時間、リソース属性、イベント属性の区別。
  • スキーママッピング、バージョニング、未知のフィールドの保持、パース失敗時の処理経路の設計。
  • TraceId、SpanId、リクエスト識別子の真実性とオプショナル性の維持。
  • テナント分離、リダクション、重複、順序の乱れ(reordering)、バックフィルコストの処理。
  • 単なるETLツール名を挙げるのではなく、リプレイ可能で照合可能な品質ゲートの提示。

確認すべき質問

  1. レガシーのタイムスタンプはローカル時刻、UTC、または混在のいずれですか?また、ミリ秒精度ですか、ナノ秒精度ですか?
  2. どのソースが安定したスキーマを持ち、どのソースが正規表現やサンプル駆動型のパースを必要としますか?
  3. TraceId、SpanId、リクエストIDはアプリケーションによって生成されていますか?それともコレクターによって推測されていますか?
  4. 生のペイロード(raw payloads)を保持する必要がありますか?その場合の期間と閲覧権限者は誰ですか?
  5. バックフィルとデュアルライトでストレージやインデックスを共有しますか?また、どの程度のクエリ差異が許容されますか?

30秒での回答

バージョニングされたマッピングコントラクトを作成し、生のペイロードをパースステータスとともに保持します。Timestampをイベント時間、ObservedTimestampを観測時間とし、Resourceにはサービス、ホスト、テナントなどの安定したソース情報を、Attributesにはイベントフィールドを、Bodyには構造化されたビジネスコンテンツを保持します。TraceIdとSpanIdには信頼できるコンテキストからのみ値を設定します。バックフィルはデュアルライトと並行して実行し、照合にはソースおよび時間パーティションを使用し、失敗したレコードはリプレイ可能なデッドレターに入れます。品質ゲートでは、パース成功率、フィールドの完全性、時間スキュー、重複、リダクションの一致率、クエリの等価性をカバーします。

ステップごとのソリューション

1. 最初にデータコントラクトを確定する

ソースごとに、パーサーバージョン、必須フィールド、デフォルト値、未知のフィールドのポリシーを定義します。安定したイベントIDを生成し、ソース、ファイルオフセット、またはメッセージ位置を記録します。未知のフィールドはAttributesまたは生ペイロードに残しても構いませんが、暗黙的に消失させてはなりません。セマンティクスの変更にはマッピングバージョンの引き上げが必要です。

json
{
  "timestamp": "2026-08-02T02:00:00.123Z",
  "observedTimestamp": "2026-08-02T02:00:00.800Z",
  "severityNumber": 17,
  "severityText": "ERROR",
  "body": {"message": "payment declined", "code": "CARD_DECLINED"},
  "resource": {"service.name": "checkout", "tenant.id": "t-7"},
  "attributes": {"region": "us-east-1"}
}

2. 時間セマンティクスを維持する

タイムゾーンを考慮したタイムスタンプに正規化し、元の文字列とパースステータスを保持します。Timestampはイベントが発生した時刻であり、ObservedTimestampはコレクターがそれを観測した時刻です。イベント時間が欠落している場合は、観測時間を明示的なフォールバックとしてのみ使用し、その旨をマークして、収集の遅延がビジネスのレイテンシと誤認されないようにします。未来の時刻、過度な古さ、精度の切り捨てを検証します。

3. Resource、Attributes、Bodyのマッピング

Resourceは、サービス、バージョン、ホスト、クラスター、テナントなど、ログを生成するエンティティを記述します。Attributesは、リージョン、リクエストタイプ、実験グループなど、イベントのインスタンスを記述します。Bodyには構造化コンテンツまたは未パースのメッセージを含めます。フィールドの配置を誤ると、集計、インデックス作成、コストが変化するため、マッピングには理由と下流のコンシューマーを記録する必要があります。

4. 重要度とコンテキストのマッピング

レガシーのWARNERR、および数値レベルをSeverityNumberにマッピングし、元のSeverityTextも保持します。フォーマットと注入ポイントが信頼できる場合にのみ、TraceId、SpanId、TraceFlagsを受け入れます。欠落しているコンテキストは理由を付記して空のままにし、決してトレースIDを捏造してはなりません。リクエストIDは通常のAttributeとして保持できますが、TraceIdとして提示してはなりません。

5. デュアルライトとバックフィルの設計

リアルタイムパスは、同じイベントIDを使用して新旧両方のストアに書き込みます。オフラインパスは、ファイル、パーティション、またはメッセージ位置をスライスし、チェックポイントを記録します。両方のパスでパーサーとリダクションルールを共有しますが、異なるバッチサイズを使用しても構いません。バックフィル後、ソース、時間枠、イベントIDによって照合を行い、その後クエリを徐々に新しいモデルへと移行します。

6. 障害、重複、順序の乱れの処理

パース失敗は、生データ、エラーコード、パーサーバージョンを含むデッドレターに書き込み、修正後にチェックポイントからリプレイします。イベントIDにソース位置とコンテンツハッシュを加えて重複排除を行います。レコードが順不同で到着してもイベント時間を書き換えてはなりません。インデックスがイベント時間と観測時間を別々にサポートできるようにします。同一性が不確かな場合は、暗黙的に上書きするのではなくマークを付与します。

7. 分離、リダクション、コスト

テナントIDは任意のクライアント入力からではなく、信頼できるResource属性から取得します。永続化の前にシークレット、トークン、個人データをリダクションし、リダクションバージョンとヒット数を記録します。生のペイロードはアクセスを制限し、保持期間を短くして個別に暗号化します。正規化されたモデルがコストの暴走を引き起こさないよう、カーディナリティの高いAttributesに対するインデックスの予算を管理します。

8. 品質ゲートとロールバック

新旧のクエリをサンプリングし、イベント数、重要度分布、時間スキュー、重要フィールドを比較します。パース成功率、必須フィールドの完全性、重複、時間スキュー、リダクション漏れ、クエリの等価性をゲートとします。カナリア期間中は旧環境への書き込みを継続します。フィールドのドリフトやテナント間の漏洩が発生した場合は、リプレイ可能な生データを削除することなく、新しい書き込みを停止し、チェックポイントを使用してクエリを以前の状態にルーティングし直します。

優れた回答例

私はこの移行を、コントラクト策定、パース、デュアルライト、バックフィル、照合、カットオーバーに分割します。すべてのソースにバージョニングされたパーサーと安定したイベントIDを付与します。Timestampをイベント時間、ObservedTimestampを収集時間とします。Resourceには安定したサービス、ホスト、テナントの情報を保持し、Attributesにはイベントフィールドを、Bodyには構造化コンテンツを保持し、SeverityNumberで古いレベルをマッピングし、TraceIdは信頼できるコンテキストからのみ受け入れます。

ライブフェーズでは、新旧両方のストアが同じイベントを受信します。バックフィルは位置情報に基づいて実行され、失敗したものは生データとエラーコードとともにデッドレターに送られます。順序の乱れや重複率を追跡しながら、イベントIDと位置情報によって照合を行います。永続化の前にリダクションを行い、生のペイロードを分離します。イベント数、完全性、時間スキュー、クエリ結果に基づいてカナリア検証を行い、ゲートに引っかかった場合は新しいライターを停止して古いクエリを復元します。

よくある間違い

  • すべてをBodyに入れる → 下流でリソースと属性のセマンティクスが失われる → データモデルに従ってフィールドをマッピングし、未知のものは保持する。
  • イベント時間を収集時間で置き換える → ビジネスレイテンシが測定不能になる → TimestampとObservedTimestampの両方を保持する。
  • コンテキストが欠落している場合にTraceIdを捏造する → 偽のトレースが作成される → 空のままにして理由を記録する。
  • 照合なしのデュアルライト → 履歴とライブ結果が等価であることを証明できない → 位置、イベントID、時間枠によって照合する。
  • パース失敗を破棄する → パーサーを修正しても履歴を修復できない → リプレイ可能なデッドレターを使用する。
  • リダクションの前に永続化する → 生データの露出ウィンドウが広がる → 制御されたインテークポイントでリダクションし、オリジナルを分離する。

フォローアップの質問と回答

イベントタイムスタンプが存在しない場合はどうしますか?

明示的なフォールバックとして、時刻欠落マーカーとともにObservedTimestampを使用します。これをビジネス時間として提示せず、品質メトリクスで個別に報告します。

TraceIdが捏造されたものではないことをどのように証明しますか?

アプリケーションSDKまたは制御されたプロキシのコンテキストのみを信頼し、フォーマットとスコープを検証し、同名のクライアントフィールドは通常のAttributeとして扱います。

デュアルライトによる重複はどのように処理しますか?

安定したイベントIDを生成し、ソース位置とコンテンツハッシュを使用して冪等な書き込みを行います。同一性が依然として不確かな場合は、重複マーカーを保持し、クエリ時に説明できるようにします。

移行中にフィールドの意味が変更された場合はどうしますか?

パーサーとスキーマのバージョンを引き上げ、古いマッピングとバージョンメタデータを保持し、互換期間を設けて、下流のクエリを両方のバージョンに対してテストします。

なぜ生のペイロードを保持するのですか?

パーサーの修正、不整合の調査、リプレイを可能にするためです。暗号化してアクセスを制限し、アクセスを監査し、保持期間を短縮し、正規化されたインデックスとは分離して保持します。

公開情報ソース

関連する質問