Andrew Tanによる
スキーマドリフトと上流の破壊的変更は、サイレントデータ障害の主な原因ですが、ほとんどのパイプラインコンテンツはインフラストラクチャに焦点を当てており、ソースシステムの動作には注目していません
関連: この記事は、Uber、Netflix、Stripe、その他の50のデータパイプラインポストモーテムからの分析のパターン1をカバーしています。スキーマドリフトは38%のインシデントを占め、私たちが見つけた最も一般的な根本原因です。
火曜日に型が変わったフィールド
私が知っているチームは、中規模のeコマース会社の支払い調整を行っています。彼らのパイプラインは、サードパーティの支払い処理業者からトランザクションデータを取得し、変換してデータウェアハウスにロードします。2年半の間、問題なく稼働していました。
11月の火曜日の午後、支払い処理業者が静かにAPIを更新しました。1つのフィールド — transaction_amount — は、文字列(いくつかのレガシーシステムが金額を"47.50"として表現するため)からネイティブな浮動小数点数(47.50)に変更されました。バージョン管理も廃止通知もなく、メールもありませんでした。ドキュメントはその後の週に更新されました。
パイプラインはクラッシュしませんでした。稼働を続け、トランザクションを処理し、成功を報告し続けました。
しかし、正しくキャストすることをやめました。下流の変換は文字列入力を想定し、通貨記号を削除するために正規表現を適用していました。浮動小数点数が入力されると、正規表現は何も一致せず、変換はnullを生成し、次の6時間のすべてのトランザクションは金額がゼロになりました。
6時間のゼロドルのトランザクションがすべて処理済みとして表示されました。誰も気づきませんでしたが、翌朝の毎日の調整レポートが出ると、数字がビジネスを飲み込んだ四捨五入誤差のように見えました。
この話をするのは、ほとんどのパイプラインアーキテクチャの文書が見逃していることを示しているからです。最も恐ろしい障害はインフラストラクチャからではなく、制御できないシステムから来るのです。
上流の変更の3つのタイプ
すべての上流の変更が同じではありません。私はそれぞれの変更によって焼かれるチームを見てきましたが、それらは異なる防御を必要とします。
追加的な変更は、ベンダーが「後方互換性あり」として発表するものです。新しいフィールドがレスポンスに現れます。既存のフィールドはそのままです。理論的には、パイプラインは問題ないはずです — 新しいフィールドを使用していないからです。実際には、追加的な変更は、暗黙のサイズの仮定に当たったとき(JSONレスポンスがバッファ制限を超える)、ワイルドカードスキーマキャプチャが予期しないフィールドを拾い始めたとき、または新しいフィールドが既に宛先テーブルにあるフィールドと衝突する名前を持っているときにパイプラインを壊します。
破壊的な変更は、少なくとも正直なものです。フィールドがリネームされます。型が変わります。エンドポイントが廃止されます。これらは発表されるべきであり、通常は評判の良いベンダーによって発表されます。しかし、「発表された」とは「行動された」という意味ではありません。発表は誰も読まないメールダイジェストに座っており、パイプラインを所有するチームではないチームがそれを受け取り、廃止日が来るまでに元のエンジニアは別の会社に移動しています。
サイレントな変更は、支払い処理業者の状況です。ベンダーの視点からは何も変わっていないため、誰も教えてくれない種類のものです。セマンティクスは同じです。データも同じです。ただし、型が変わっただけです。エンコーディングが変わっただけです。null処理の動作が変わっただけです。サイレントな変更は、誰も気づかないうちに6時間のデータ破損イベントに変わるものです。
各タイプの割合はベンダーの成熟度によって異なります。確立された金融APIは、主に長い廃止ウィンドウを持つ破壊的な変更です。リリースサイクルが速いSaaS製品は、主にサイレントで追加的なものです。パートナー提供のデータフィード — B2B統合を実行する地味で重要な種類のもの — は本当に予測不可能です。

ほとんどのパイプラインが間違ったレイヤーで失敗する理由
スキーマバリデーションについてのことですが、ほぼすべての最新のパイプラインツールがそれをサポートしています。スキーマを定義できます。取り込み時にバリデートできます。不正なレコードを拒否できます。
ほとんどのチームは理解できる理由でそれを行いません。
パイプラインの初期段階では、スキーマは絶えず変化します。ソースシステムはまだ開発中です。厳格なバリデーションは、通常の反復中にフィールドが追加またはリネームされるたびにパイプラインを失敗させます。そのため、バリデーションはオフにされるか、「ベストエフォート」に緩められ、パイプラインが本番に到達する頃には、誰もそれを厳しくすることを覚えていません。
また、チームがスキーマエンフォースメントについて考える方法には哲学的な分裂があります。厳格なスキーマバリデーションは防御的に感じられます。それは、ソースシステムが息をするたびにパイプラインを壊す壁を作っているように感じられます。許容的な処理は実用的に感じられます。処理できるものを処理し、できないものを通過させ、宛先がそれを解決するようにします。
許容的な処理の問題は、それが失敗の表面を下流に移し、それを見えなくすることです。パイプラインは失敗しません。下流の分析やアプリケーションが静かに不正なデータを処理します。そして、気づく頃には — 数日後、レポートが間違って見えるときや、ユーザーが不一致を報告するとき — 汚染されたレコードは正当なものと混ざり合い、下流の変換によって複雑化され、おそらく行動に移されています。
パイプラインレイヤーでのスキーマバリデーションは、それ自体のために厳格であることではありません。それは、失敗を静かで遅いものではなく、早くて大きなものにすることです。
3つの防御クラス
これらのインシデントを十分に見てきた後、上流の変更をうまく処理するチームは、一貫して3つのことを行っています。
型だけでなく形状のバリデーション。型のバリデーションは支払い処理業者の状況をキャッチします。しかし、形状のバリデーションはより微妙なケースをキャッチします: 必須フィールドがオプションになり(したがって時々欠落する)、常に1つの要素を持っていた配列が今では時々ゼロを持ち、平坦だったオブジェクトが1レベル深くネストするようになった場合。
この区別は重要です。なぜなら、型エラーは大きな失敗を引き起こしますが、形状の不一致は静かな失敗を引き起こします。99.9%の時間存在し、0.1%の時間欠落するフィールドは、まれなトランザクションタイプや特定の地理的地域、またはエッジケースの支払い方法でのみトリガーされるため、数週間表面化しないnull処理バグを引き起こします。
スキーマドリフトモニタリング、ジョブステータスだけではありません。ジョブステータスはパイプラインが実行されたかどうかを教えてくれます。スキーマドリフトモニタリングは、パイプラインが今日処理したものが昨日処理したものと同じ形状かどうかを教えてくれます。
これは高度な可観測性プラットフォームを必要としません。最も簡単なバージョンは、各ソースからのレコードのサンプルの推測されたスキーマをハッシュし、ハッシュが変わった場合にアラートを出す毎日のチェックです。これは粗いですが効果的です。ほとんどのスキーマドリフトイベントは、この方法で24時間以内に検出可能です。
より高度なバージョンは、フィールドレベルの統計を追跡します: フィールドごとのnull率、フィールドごとのカーディナリティ、フィールドごとの型分布。transaction_amountのnull率が0.0%から0.1%に変わると、上流で何かが変わりました。それが意図的かもしれません。それがバグかもしれません。いずれにせよ、それが問題になる前に知りたいのです。
取り込みと処理の分離。これは、上流の変更が発生したときに最も時間を稼ぐアーキテクチャパターンです。パイプラインが生データをランディングゾーンに取り込んでから処理する場合、スキーマの問題を修正した後に履歴の生データに対して再生するオプションがあります。取り込みと処理が結合されている場合、そのオプションは失われます。
生のランディングゾーンは高価でも複雑でもある必要はありません。多くのユースケースでは、パーティション化された生のJSONを持つ追加専用のオブジェクトストア(S3、GCS、Azure Blob)が十分です。変換レイヤーはランディングゾーンから読み取り、直接ソースからは読み取りません。上流で何かがうまくいかない場合、変換を修正して再生します。データはまだそこにあります。

パイプラインレイヤーでの契約テスト: それは価値があるか?
消費者駆動の契約テストがこの問題の「正しい」解決策として聞かれるでしょう。このアイデアは、パイプラインが契約を公開し — これが私が依存するフィールドであり、これが私が期待する型であり、これが私が破壊的変更と見なすものである — ソースシステムが変更をデプロイする前にその契約に対してバリデートすることが期待されるというものです。
これは、統合の両側を制御する場合によく機能します。内部のマイクロサービスを統合している場合や、統合の安定性を真剣に考えているベンダーと協力している場合、契約テストは本当に価値があります。Pactのようなツールはそれを実現可能にします。
私が実際に見る統合の大多数 — サードパーティのSaaS、パートナーAPI、あなたが影響を及ぼせないシステムからのデータフィード — では、契約テストは良い理論です。支払い処理業者にあなたのPactテストを実行するよう強制することはできません。消費者駆動の契約を聞いたことがないベンダーの法務チームと契約公開権を交渉することはできません。
より実用的なフレームは次のとおりです: 変更を検出し、迅速に回復するために境界のあなたの側で何ができるか?
それはスキーマモニタリング、ランディングゾーン、パイプラインレベルのバリデーションに戻ります。派手ではありません。技術的に興味深い解決策ではありません。しかし、あなたが遭遇するすべての上流のシナリオにわたって実際に機能するものです。
すべての統合キックオフで尋ねるべき質問
私はすべての統合設計レビューで1つの質問をし始めました: これが警告なしに変更されたときのプロセスは何ですか?
もしではなく、いつ。
それは悲観的に聞こえます。パートナー統合チームは時々それを個人的に受け取ります。しかし、その質問は、誰も明示的にしていなかった仮定をほぼ必ず表面化させる会話を強制します: ソースシステムのチームが破壊的変更を伝えるという仮定、統合チームの誰かが変更ログを読むという仮定、パイプラインがX日間の不正確なデータを許容できるという仮定。
それらの仮定は通常間違っています。それらを明示的にすることで、それを回避する設計の機会を得ることができます。
「これが警告なしに変更されたときに何が起こるか」の答えには、少なくとも次のことが含まれているべきです: アラートが発生する場所、誰がそれを受け取るか、チームがどのフィールドが変更されたかを特定するのにどれくらいの時間がかかるか、影響を受けたデータを生のランディングゾーンから再生するのにどれくらいの時間がかかるか。もし答えが「調査し、たぶんベンダーに電話する必要がある」なら、そのパイプラインは本番の準備ができていません。
layline.ioがどのように適合するか
スキーマの進化は、layline.ioがデータ処理をどのように扱うかを考えるときに多く考慮することの1つです。バッチとストリーミングの両方のパイプラインを扱うとき — そして現実にはほとんどのチームが両方を無期限に実行しています — 上流の変更問題は複雑になります。ストリーミングソースでのスキーマ変更はリアルタイムであなたに影響を与えます。バッチソースでの同じ変更は24時間表面化しないかもしれません。
layline.ioの処理モデルは、明示的なバージョンルーティングを通じてスキーマの進化をサポートします: 新しいスキーマバージョンが導入されたとき、別のロジックとバリデーションを適用するか、またはそれらのレコードを検証と処理のために完全に別のフローにルーティングすることができます。これにより、メインの処理パスを汚染することなく処理できます。
それは魔法ではありません。上流のものが変わるという仮定で統合を設計する必要があります。しかし、それが変わったときには、失敗の表面が小さくなり、回復の道が速くなります。
上流の変更をうまく処理するチームは、最も洗練されたインフラストラクチャを持っているチームではありません。ソースシステムが決して驚かせないと仮定することをやめたチームです。
あなたのチームがスキーマドリフトや上流の統合の信頼性に取り組んでいる場合、layline.ioの処理エンジンは、バッチとリアルタイムのワークロードの両方でスキーマ進化とパイプラインの回復力を処理します。Community Editionは無料で探索できます。
Andrew Tanは、layline.ioの創設者であり、バッチとリアルタイムのワークロードの両方をスケールで処理するエンタープライズデータ処理インフラストラクチャを構築するシリアルアントレプレナーです。



