データ接続と統合パイプラインの構築インクリメンタルパイプライン高いパフォーマンスの維持

注: 以下の翻訳の正確性は検証されていません。AIPを利用して英語版の原文から機械的に翻訳されたものです。

高いパフォーマンスを維持する

インクリメンタル(差分処理)トランスフォームは、パイプラインが更新されるたびに新しい行のみを処理することで、リソース使用量の最小化に役立ちます。Foundry では、インクリメンタルトランスフォームはデータセットに新しい行を追加することしかできず、行の置換や削除はできません。データ取り込みにも同じことが当てはまる場合があります。通常、1回の実行で処理するデータ量は少ないため、追加されるデータも少量ですが、アクティブなビューを表示するときは、追加されたデータをすべてまとめて考慮する必要があります。

通常は問題ありませんが、小さな更新が数万回、数十万回と蓄積すると(頻度によっては、数週間または数か月にわたって中断なくビルドを続けると発生します)、読み取りと書き込みのパフォーマンスが低下し始める場合があります。これには、以下で説明する、相互に関連するいくつかの要因が考えられます。

考えられる原因

ファイル数の多さ

データセットに含まれるファイルが多すぎると、現在のビューのデータにアクセスするバックエンドリクエストが遅くなります。これにより、プレビューが利用できなくなる問題や Contour での失敗が発生するほか、Transforms、特に Python Transforms で著しい遅延が発生する場合があります。これは、その言語環境でのファイルの読み込みに固有のオーバーヘッドが伴うためです。

時間の経過とともに、アクティブなデータセットビューに関わるあらゆる操作で、非効率なデータ読み取りが処理時間の大半を占めるようになります。その結果、当初は数分で実行できていたトランスフォームが、最終的には数時間または数日かかるようになります。極端な場合には、バックエンドリクエストがタイムアウトし、ビルドに失敗することもあります。

また、APPEND または UPDATE トランザクションで Spark DataFrame を出力に書き込む際、インクリメンタルトランスフォームは、出力の現在のビューにある既存のファイルすべてのリストをメモリー内に作成する必要があります。ファイル数が多い出力では、この操作に長い時間がかかり、メモリー不足エラーを避けるために、ドライバーメモリーの容量が大きい Spark プロファイルを使用する必要がある場合があります。

トランザクション数の多さ

ファイルが多すぎる場合と同様に、ビュー内のトランザクションが多すぎると、処理が遅くなる場合があります。ただし、この遅延はファイルがリクエストされたときだけでなく、ビューの範囲が解決されるたびに発生するため、統計の計算や履歴の読み込みなど、データセットの機能に影響する可能性があります。トランスフォームでは、環境のセットアップ中で、Spark の詳細がまだ利用できない計算前の段階でこの遅延が現れるため、問題のデバッグが難しくなります。

通常、トランザクションはファイルとともに蓄積しますが、ファイル数が比較的少なくても、空のトランザクションが問題を引き起こす場合があります。逆に、トランザクション数が比較的少なくても、各トランザクションに多くのファイルが含まれていれば、ファイル数は多くなる可能性があります。

考えられる解決策

定期的なスナップショット作成

インクリメンタルパイプラインのパフォーマンスを維持するための最も簡単な解決策は、定期的にスナップショットを実行することです。状況によっては、これが最も効果的な解決策でもあります。スナップショットはデータ全体を再処理し、出力を新しいビューに置き換えます。このビューは効率的にパーティション分割できます。スナップショット作成は計算リソースを大量に消費する操作であり、通常の処理よりもインクリメンタル処理が好まれることが多いのはそのためです。ただし、時折スナップショットを作成すると、同じビュー内にファイルやトランザクションが過剰に蓄積するのを防ぐのに役立ちます。管理者は、データの読み取りと書き込みのパフォーマンスのバランスを考慮して、スナップショット作成の頻度を決める必要があります。

SNAPSHOT トランザクションの影響は連鎖し、スナップショットが作成された入力を使用するインクリメンタルトランスフォームは、それ自体もスナップショットを作成します。インクリメンタルトランスフォームにスナップショットの実行を直接強制するのではなく、トランスフォームの入力の1つでスナップショットを作成するだけで、Transforms アプリケーションが、そのインクリメンタルトランスフォームにもスナップショット作成が必要であると判断します。したがって、インクリメンタルパイプラインで定期的にスナップショットを作成するための有効な設定方法は、意図したスナップショットのスケジュールに従ってビルドされるダミー入力を用意し、そのダミー入力を常にスナップショットモードで実行するようにすることです。

定期的なスナップショット作成には、以下のような欠点があります。

  • ダミー入力が検索結果やフォルダー構造に表示されるため、スナップショット作成によってリネージが複雑になり、混乱を招く場合があります。
  • スナップショット作成は、頻度が低くても非常に高コストになる場合があります。
  • スナップショットの実行中は更新をパイプライン全体に伝播できないため、SLA(サービスレベル契約)の達成に支障をきたす場合があります。
  • Data Connection の同期を元にしたデータセットなど、生データセットではスナップショット作成は機能しません。
  • すべてのトランスフォームがスナップショット作成をサポートするように記述されているわけではありません(ただし、常にサポートするように記述することを推奨します)。

データセットプロジェクション

インクリメンタル処理による蓄積への対処として最も推奨される方法は、対象のデータセットにデータセットプロジェクションを追加することです。データセットプロジェクションは、データセット内のデータをクエリするための代替の仕組みを提供し、正規のデータセットとは独立して保存および更新されます。こうした特性により、データセットプロジェクションはインクリメンタル計算の追加専用モデルの制約を受けず、データ量の増加に応じて内部のデータ表現を自動的に再編成できます。これはコンパクションと呼ばれます。コンパクションにより、正規のデータセットに含まれるファイル数やトランザクション数にかかわらず、プロジェクションからの読み取りで常に高いパフォーマンスが維持されます。

これは、インクリメンタルパイプラインで特に有用です。読み取り側がパフォーマンス向上の恩恵を受けるために、データセットプロジェクションが正規のデータセットと完全に同期している必要はないためです。Foundry のすべての製品は、プロジェクションと正規のデータセットから取得したデータを組み合わせ、プロジェクションがない場合と同じビューを再構成できます。たとえば、データセットに100個のインクリメンタルトランザクションがあり、最初の99個を使ってビルドされたプロジェクションがある場合、99個はプロジェクションから読み取られ、データセットから読み取られるのは1個だけです。このため、通常はデータセットプロジェクションを毎日または毎週更新すれば十分であり、維持に必要な計算コストは非常に低くなります。

データセットプロジェクションは正規のデータセットとは別のリソースであるため、正規のデータセット自体をビルド中であっても、いつでもビルドできます(その逆も同様です)。読み取り側は、有効なトランザクションに基づいて、現在の状態を使用するだけです。たとえば、データセットがトランザクション10をビルド中で、同時にプロジェクションのビルドが開始された場合、プロジェクションはトランザクション9から読み取ります。この状況でデータをクエリする読み取り側は、トランザクション1~8をプロジェクションから、9をデータセットから読み取るため、実質的にデータセットから直接読み取る場合と同じデータを参照します。

データセットプロジェクションには、以下の欠点があります。

  • データセットプロジェクションは、データセット単体よりも多くのストレージを使用します。
  • データセットプロジェクションは、そのままでは Foundry Retention と連携して機能しません。
  • データセットプロジェクションは、正規のデータセットを一切変更しません(そのため、プロジェクションを削除すると、読み取りは再び非効率になります)。

保持ポリシー

パイプラインのユースケースによっては、プラットフォーム内に履歴データを永続的に保持する必要がなく、最新のトランザクションのみを保持すれば十分な場合があります。これは Foundry Retention を使用して自動的に行えます。この場合、トランスフォームのロジックにトランザクションをまたぐ依存関係(集計や差分計算など)が含まれていなければ、特別な考慮をせずにインクリメンタルパイプラインを構築できます。Python Transforms では、インクリメンタルデコレーターに特別な allow_retention フラグを設定する必要があります(設定しない場合、DELETE トランザクションによってスナップショットの実行が開始されます)。

保持ポリシーの変更には、以下の欠点があります。

  • 履歴データが失われます。
  • トランザクションがデータの観点で自己完結した単位でない場合、保持ポリシーによって不整合な状態になる可能性があります(たとえば、対応する開始イベントがない終了イベント)。

検討すべき追加オプション

トランザクションを中止する

状況によっては、ファイルが0個のトランザクションや、空のファイルのみを含むトランザクションがコミットされます。これらのトランザクションはビューに影響しませんが、有効な更新と見なされ、スケジュールと関連するすべての副次的効果をトリガーするため、計算リソースが無駄になる可能性があります。また、空のトランザクションによって、ファイル数とトランザクション数が大幅に増えることもあります。

空のトランザクションは、通常は Data Connection の同期であるソース側で発生を防ぐのが最善です。Data Connection はファイルを含まないトランザクションを常に中止しますが、空のファイルが生成される可能性はあります。空のトランザクションは、カスタムプラグインで特に問題になることがあります。空のトランザクションを回避するようにプラグインを変更できない場合もあります(たとえば、Data Connection のインクリメンタルメタデータを更新するために、空でないトランザクションが必要な場合)。また、トランスフォームやその他の手段によって、ファイルを含まないトランザクションがコミットされる場合もあります。

空のトランザクションやファイルを含まないトランザクションの影響を最小限に抑えるには、パイプラインの最上流のトランスフォームで、トランザクションを明示的に中止できます。受け取った入力が空であること、または出力が空になったことを検出した場合、出力オブジェクトで .abort() を呼び出すことができます。これにより、ジョブとそのトランザクションが中止されます。中止されたトランザクションは実質的にキャンセルされ、スケジュールをトリガーせず、副次的効果も発生しません。トランザクションを中止すると連鎖が途切れ、空のトランザクションがパイプラインの下流に伝播するのを防げます。また、トランザクションを中止しても失敗の統計には計上されません(意図的にビルドを失敗させた場合は、失敗の統計に計上されます)。

中止されたビルドは成功と見なされ、入力トランザクションのポインターが進みます。そのため、空でない入力がある状態で中止すると、データが破棄されます。前提条件が満たされないなど、別の理由でビルドを停止したい場合には、これは望ましくない可能性があります。

変更履歴データセット

変更履歴ロジックを使用すると、APPEND のみのトランザクションに編集セマンティクスを実装でき、インクリメンタル(差分処理)パイプラインで結合や集計を確実に実行できます。ただし、前述のファイル数とトランザクション数の問題に加えて、APPEND のみのトランザクションに編集セマンティクスを実装すると、行数が際限なく増加する可能性があります。その結果、状態を解決する段階に達したとき(または単独のスナップショットが必要になったとき)に、トランスフォームのパフォーマンスがますます低下します。

このようなパイプラインの行数を抑えるのは、やや複雑です。スナップショットの作成は可能であり、部分的な状態が存在する中間トランスフォームでは、大きな効果がある場合があります(完全な再ビルドでは、その大部分がなくなるためです)。ただし、スナップショットの利点を十分に活かすには、行を最新の状態にまとめるロジックが必要ですが、それが常に望ましいとは限りません(場合によっては、最新の状態だけでなく、任意の時点における行の状態を把握したいこともあります)。データセットのプロジェクションで改善できる範囲にも限界があります。また、行が不可分な単位でない場合、保持ポリシーでは期待する効果が得られない可能性があります(たとえば、対応する開始イベントのない終了イベントが残ることがあります)。そのため、このようなパイプラインを設計する際には、特に注意が必要です。