TimeFuzeアーキテクチャ構想 - 処理とデータとタイマーを一体化したデータパイプライン

この記事は第1回ウェブシステムアーキテクチャ(WSA)研究会の予稿です。

cronのようなタイムスケジューラーにより、定期的に実行されるバッチ処理の課題を解決するアーキテクチャを最近考えている。 この記事では、単一のタイムスケジューラによるcronベースの手法に代えて、データに対してタイマーと処理を仕込むことでスケールさせやすい構造にできないか、という提案を試みる。

はじめに

Webサービスにおいて、リクエストに対してHTMLのレスポンスを返却する以外のワークロードの多様化が進んでいる。 最近であれば、機械学習による時間周期による大規模なデータ処理が求められることも多い。 その他、月次の課金バッチ処理や、ランキングの定期更新など、一定の時間間隔で任意の処理を実行したいケースは多い。

このような定期的なデータ処理パターンは、SRE本[Bet17]の25.1節「パイプラインのデザインパターンの起源」にて、データパイプラインと定義されるデザインパターンに分類できる。

データ処理に対する旧来のアプローチは、データを読み取り、希望する何らかの方法でそのデー タを変換し、新しいデータを出力するプログラムを書くというものでした。通常こういったプログラムは、cron のような定期スケジューリングを行うプログラムの制御の下でスケジュール実行されました。このデザインパターンはデータパイプラインと呼ばれます。 「SRE サイトリライアビリティエンジニアリング ――Googleの信頼性を支えるエンジニアリングチーム」

データパイプライン処理を実現する一般的な方式は、特定サーバ上のcronなどのタイムスケジューラーにより、バッチ処理を実行させる方式である。

一般のデータパイプラインの欠点として、25.3節「定期的なパイプラインパターンでの課題」にて、期限内に実行が終わらないジョブ、リソースの枯渇、処理の進まないチャンクとそれに伴う運用負荷が挙げられている。 この記事では、一般のデータパイプラインの課題は以下の4つであると仮定する。

  • a) データ量増大にあわせたスケーリング運用の難しさ
  • b) バッチ処理途中のエラー処理の難しさ
  • c) タイムスケジューラそのものの運用の煩雑さ
  • d) 実行頻度設定の柔軟性の低さ

一般のデータパイプラインの課題を、データと処理とタイマーを一体化するアーキテクチャにより解決できると考えている。 提案するアーキテクチャでは、データに紐付いたタイマーがジョブを起動する。 これにより、一般手法と比較し、スケーラブルかつ細かい粒度で制御しやすいデータパイプライン処理ができる。 起爆装置と爆薬とタイマーを一体化した時限信管(Time Fuze)に似ていることから、このアーキテクチャを「TimeFuzeアーキテクチャ」と名付けてみた。

課題aについては、最初から並行処理を前提としたアーキテクチャにより解決できる。 課題bについては、バッチ処理ではなくレコード単位でジョブ定義することにより解決できる。 課題cについては、データストアのTTLによりスケジューラホストが不要となる。 課題dについては、タイマー設定の粒度をレコード単位で設定することで解決できる。

以前開発した時系列データベースアーキテクチャ[yuu17]では、特性の異なる3種類の分散データストアを組み合わせ、古いデータを遅いディスクへ徐々に逃していくことにより、性能とコストを最適化した。データストア実装として、インメモリDB(Redis Cluster)、オンディスクDB(Amazon DynamoDB)、オブジェクトストレージ(Amazon S3)を採用している。 このアーキテクチャを考える上で重要なのは、どのようにデータを移動させるかということだった。 前述の一般的な手法により、データレコードを走査し、一定以上古いタイムスタンプをもつレコードを読み出し、次のデータストアへ書き込むという素朴な手法をまず考えた。 時系列データベースのアーキテクチャでは、これをデータストアのレコード単位のTTLを利用して解決した。 具体的には、データストアに書き込むときに、レコードに対してTTL(Time To Live)を設定し、TTLが期限切れになったタイミングで、トリガーを起動し、期限切れレコードを別のデータストアへ書き込む。 実装として、DynamoDB StreamsとLambda Triggersの組み合わせ[dyn01]とDynamoDBのTTL[dyn02]を利用し、DynamoDBからS3へデータを移動させた。 これにより、パイプライン処理を、バッチ処理ではなく、レコード単位のイベント駆動型処理に置き換えられた。

のちに、TTLを用いたデータパイプラインアーキテクチャを時系列データベース以外のデータパイプラインに適用できないかを考えた。 例えば、機械学習の学習モデルの定期更新や、マルチテナント環境における大量のSSL/TLS証明書[mat17]の定期更新などがある。 以降では、TimeFuzeアーキテクチャの詳細と実装手段、アプリケーション適用について議論する。

提案手法

TimeFuzeアーキテクチャ

TimeFuzeアーキテクチャの動作フローを以下の図に示す。

DataSourceはデータパイプラインの読み出し側を指し、DataDestinationは書き出し側を指す。 TriggerはDataSourceレコード単位のパイプライン処理を実行する。

まず、DataSourceに対して、Triggerを実行したい時刻を表すTTLと共にデータレコードを書き込む。 次に、TTLの期限切れ(expire)を検知し、Triggerに期限切れしたレコードを渡す。 さらに、Triggerが渡されたレコードをもとに所定の処理を実行し、DataDestinationに対して、結果を書き込む。 Triggerでは、その他のAPIやデータストアからデータを取得することもある。 DataDestinationは、DataSourceと同一のデータストアでもよい。 前述の学習モデルの更新や証明書更新の例では、DataSourceとDataDestinationは同一のデータストアを利用することを想定している。

TimeFuzeのメリット

TimeFuzeアーキテクチャには以下のメリットがある。

  • a) 並行処理を前提としたアーキテクチャであり、スケールさせやすいこと
  • b) エラーからの回復処理が容易であること
  • c) タイムスケジューラとバッチ処理のためのホストもしくはクラスタの構築・運用が不要であること
  • d) レコード単位でタイマーをセットするため、ジョブの実行頻度をレコード単位で調整可能

a) について、バッチ処理の場合、マルチコア・マルチホストスケールさせるまたはI/O多重化するための並行処理実装を開発者に要求する。 TimeFuzeでは、レコード単位でトリガー処理を記述するため、スケールさせやすい。 b) について、TimeFuzeでは、1レコード分の処理を書けばよいため、エラー発生時にリトライする場合、どこまで処理を終えたかを記録するといった回復処理のための実装が必要がない。

TimeFuzeの実装

実装として、Amazon DynamoDBとAWS Lambdaを利用する例と、Redisを利用する例をあげる。

Amazon DynamoDBおよびAWS Lambda

Amazon DynamoDBは、フルマネージド型のNoSQLデータベースサービスである。 AWS Lambdaは、任意のイベントを入力として任意の処理をFunctionとして登録しておくと、イベント発火を契機にFunctionを呼び出せる。 これらを組み合わせ、DynamoDB上のレコードに対する登録、更新、削除のイベントを契機に、Lambda Functionをトリガーとして実行できる。 DynamoDBはTTLをサポートしており、TTL expiredイベントを契機にLambda Functionを実行できる。

DynamoDBをDataSourceとして、トリガー処理にLambdaを利用することで、TimeFuzeアーキテクチャを素直に実装できる。

Redis

Redisは多彩なデータ構造をもつインメモリDBであり、昨今のWebアプリケーションのデータストアの一つとして、広く利用されている。 RedisはKeyspace通知[rednot]機能をもち、キーに対するイベントをPub/Subにより、購読者にメッセージ通知できる。 Keyspace通知を利用し、DynamoDB同様にトリガー処理を実現できる。

ただし、実運用のためのトリガー処理を実現するには、イベント通知以外に、通知するイベントの永続化とイベントを受信し処理するトリガーの実装が必要となる。 Redis自体は、後者の2つをサポートしないため、アプリケーション開発者が汎用的に利用できる実装を提示したい。

そこで、以前筆者が開発したジョブキューシステム[yuu14]Fireworq[gitfir]に着目する。 Fireworqでは、ジョブの永続化をサポートし、所定のインタフェースを満たしていれば任意の言語で開発したWebアプリケーションサーバに対してジョブ実行を依頼できる。 RedisのKeyspace通知とFireworqを組み合わせることにより、メッセージの永続化をサポートしつつ、任意の言語によるトリガー処理を実現できる。 ただし、RedisにKeyspace通知を受信し、Fireworqへ投稿するコンポーネントを新たに実装する必要がある。

TimeFuzeの具体的なアプリケーションへの適用

自分の最近の業務経験から、時系列データの異常検知と大規模SSL/TLS証明書管理アプリケーションへのTimeFuzeアーキテクチャの適用を考えた。

時系列データの異常検知

機械学習をWebサービスに導入すると、最新の投稿データに追従するために、定期的に学習モデルを更新することがある。 TimeFuzeにより、DataSource上のモデルの更新処理をTriggerとして登録し、モデル更新時にTTLをセットして再書き込みすることにより、データパイプラインとして機能する。

時系列データによる異常検知では、収集したメトリック系列もしくはメトリック系列の集合に対して、学習モデルを構築する。 学習モデル構築後に収集されるメトリックに対応するため、学習モデルを定期的に更新する必要がある。

このアプリケーションにTimeFuzeアーキテクチャを適応することを考える。 学習モデルを更新するのみで、学習モデルを他のデータストアに移動させる必要はないため、DataSourceとDataDestinationは同一のデータストアとなる。 各学習モデルのレコードにTTLを設定し、Triggerが外部データストアからメトリックを取得し、学習処理を実行したのちにDataDestinationに対して上書き更新する。

大規模SSL/TLS証明書管理

ブログサービスやレンタルサーバーサービスのようなマルチテナント環境[pep17]にて、大量のSSL/TLS証明書を管理し、数ヶ月ごとに更新するケースがある。Let's Encrypt[letenc]の場合、証明書の期限は3ヶ月となる。

[mat17]では、サーバ証明書と秘密鍵をWebサーバ上のファイルシステムに格納するのではなく、データベースに格納し動的取得するアーキテクチャが提案されている。 データベース上の証明書と秘密鍵を定期的に更新するために、TimeFuzeアーキテクチャを適用することを考える。 各ドメインに対する証明書および秘密鍵のレコードにTTLを設定し、Triggerが証明書を再取得し、DataDestinationへ上書きする。 Let's Encryptの場合、ACMEプロトコルにより証明書を自動発行できる。

考察

TimeFuzeの適用条件

現在のところ、TimeFuzeの適用条件は、y_uukiの経験に頼っており、明らかでない。*1 既存のデータパイプラインアプリケーションの課題をサーベイし、適用条件を整備することは今後の課題である。 現段階では、以下のデメリットにあてはまるケースに加えて、少なくともデータレコード数が一定以上大きくなければメリットを享受しづらいことがわかっている。

TimeFuzeのデメリット

TimeFuzeアーキテクチャはあらゆるデータパイプラインに適用できるわけではない。

アーキテクチャレベルでは、Triggerがレコード単位でジョブを実行するため、DataSource上の複数のレコードをマージする必要がある処理をしづらいというデメリットがある。 従来手法であれば、DataSource上のレコードをグループ化して読み出し、マージ処理することは容易である。 一方、TimeFuzeアーキテクチャでは、マージする必要がないように最初から1つのレコードにまとめて書き込む必要がある。

さらに、実装レベルでは、TTLを利用することのデメリットとして、データの一貫性と、ジョブ実行タイミング制御の困難性がある。 前者では、レコードが削除されてから更新されるまでレコードを参照できない期間があり、アプリケーションに工夫を要求する。 後者は、TTLの期限切れを過ぎてから実際に削除されるまでのディレイが大きいと、開発者が意図したタイミングよりも遅れてジョブが実行されることがありえる。 DynamoDBのTTL実装の場合、ベストエフォートベースでTTL期限切れ以降2日以内に削除することを目指している[dyn03]

TimeFuzeのデメリットの解決

TTLのデータの一貫性の問題は、TTLをタイマーとして利用するのではなく、設定した時刻を経過したイベントのみを発行し、レコードを削除しない機能をデータストアに組み込むことで解決できる。

ジョブ実行タイミング制御の問題は、RedisやCassandraなどTTLをサポートしたデータベース実装を調査し、ディレイ時間を計測する必要がある。

アーキテクチャレベルの課題について、リレーショナルモデルのようなデータレコード間の関係の概念を導入することにより、複数のレコードを前提としたアーキテクチャへ昇華できるかもしれない。 *2

むすび

データパイプライン処理は、Webサービス開発ではよく採用されるデザインパターンである。 単一のタイムスケジューラによる従来手法では、レコード数に対するスケーラビリティ・エラー回復処理・サーバ運用効率・ジョブ実行頻度の細かい粒度での制御に課題があった。 そこで、この記事では、処理とデータとタイマーを一体化したTimeFuzeアーキテクチャを提案し、従来手法の課題の解決を構想した。 さらに、時系列データベースアーキテクチャ、時系列データの異常検知、大規模SSL/TLS証明書管理の各アプリケーションへの適用可能性を示した。

今後の取り組みとして、既存のデータパイプラインの課題をサーベイし整理し、既存データストア実装のTTLディレイ時間の調査、TTLではなくTime to Eventを既存のデータストアへ組み込むことを考えている。

参考文献

発表スライド

発表時のフィードバック

  • TimeFuzeアーキテクチャの適用条件について (matsumotoryさん)
  • 関係性の概念の導入について (monochromeganeさん)
  • モバイルエージェントとの関連について (61503891さん)
  • 適応的なパラメータ決定ポイントについて (matsumotoryさん、syu_creamさん、suma90hさん)

あとがき

提案するアーキテクチャ名がTimeFuze(時限信管)であることにはそれなりの意味があります。 表題を考えていたときに、まつもとりーさんのFastContainerアーキテクチャ構想を眺めていました。 FastContainerってかっこいいしずるいみたいな感じです。 自分もかっこいい名前にするぞ、とあれこれ考えました。 最初はDataCronやData-Driven Cronといった名前を考えていました。 このような名前であれば、この分野の人であればなんとなく想像はつきそうであるものの、多義的な用語になりそうだったため、あまりピンときていませんでした。 このアーキテクチャに対して、なんとなく時限爆弾っぽいイメージとARMORED COREの近接信管のイメージをもっていたため、ググってみると、TimeFuze(時限信管)という用語があることを知りました。 Wikipediaによると信管は、「起爆時期を感知する機能」「所望の時期以外では絶対に起爆させないための安全装置」「安全装置の解除機構」「弾薬の起爆装置」の4つの機能が統合された装置だそうです。 ここで、このアーキテクチャのアイデアって、「データ」「処理」「タイマー」の一体化ではないかということに思いあたりました。 記事中では、しれっとでてくるこの表現ですが、この表現のおかげで、最終的にはデータが意思を持って動いて欲しい(by takumakumeさん)という話まで発展させることができました。

そうすると自分の中でのFastContainerの見方がかわってきました。 FastContainerは現在のところプロセスに着目されていますが、データ処理に着目する発想もあるんじゃないかと考えました。 議論では、さらに関係性の概念やモバイルエージェントの関連など、さらにTimeFuzeを深めるための着想をいただき、ひさびさにおもしろい、楽しいと思う発表でした。

言葉にこだわった結果、抽象化が進み、他の技術や研究と結び付け、新たな着想を得ることができる、というのがここで言いたかったことでした。

あとがきは以上です。wsa研自体の振り返りは別記事として書きます。

*1:発表後の質疑にて、matsumotoryさんに指摘されたところ

*2:発表後の質疑にて、monochromeganeさんに指摘されたところ