🏔️

4RAPで動いているバッチが300種を超えたお話 - 前編 -

2022/12/20に公開

この記事はFOLIO Advent Calendar 2022の20日目です。

はじめに

FOLIOでは4RAPというプロダクトを2021年1月から提供しています。

プロダクトのローンチからはおよそ2年ですが、ベースは自社プロダクト向けに開発したものでしたので、大体3年とちょっと開発を続けてきました。

今もとある案件に向けて絶賛開発中なんですが、メンバーとのお茶会(という名の雑談会)でだいぶ大きくなってきたよねーという話になり、とある案件向けに動かすバッチの種類(not インスタンス数)を数えてみたところ300種を超えていました。

いやぁー遠くへ来たもんですね(しみじみ)

世の中にはもっともっと大規模なバッチシステムは存在しますが、代々受け継がれて巨大化してきたシステムがほとんどかと思われ、スクラッチから現在まで途切れることなく関わっている人(含む私)が残っているシステムとしてはそこそこの規模じゃないかなと。

なにかしら世の中の役に立つ知見があるかもしれないということで、この機会に4RAP-IAという4RAPの中でも資産管理や取引の責務を持つバッチシステムの設計思想について、このタイミングで記録を残してみたいと思います。(以下では4RAP-IAのことを単に4RAPと呼びます)

なお本記事の内容は、FOLIOでの4RAP開発で得られた、私個人の知見を元にした個人的見解です。
異論、間違い、おかしなところがあっても、ご笑納いただけると幸いです。(質問、指摘、感想などはDMで頂けると嬉しいです)

4RAP=バッチシステム

バッチシステムとは、相互に依存するバッチ処理の組み合わせを構成要素として持つようなシステム、バッチ処理は特定の条件(e.g. 毎日xx時, 毎時xx分)に従って、定期的に実行される処理を指すと定義します。

4RAPはバッチシステムの色合いの濃いシステムです。 (もちろんバッチ以外も持っていますが一番大きな割合を占めている。)
以下ではなぜバッチシステムなのか、どんな設計思想でバッチシステムを採用したのかを紹介していきたいと思います。

なぜリアクティブシステムじゃないの?

バッチシステムとは異なり、イベントをトリガーとしてリアルタイムの処理を行なうシステムのことはリアクティブシステムと呼ばれます。
インプットキューにデータが投入されたら、そのイベントを受けて処理が走り、アウトプットキューやストレージに処理後のデータが投入され、そのアウトプットを受けて次の処理が動く、というような仕組みのシステムですね。

証券の取引は 受注 -> 発注 -> 約定/失効 というイベントで構成され、取引後の残高反映などで即応性を求められるため、実は証券ドメインのシステムはリアクティブシステム(に近いもの[1])が多いです。
例えば、2022年現在も業界標準であるFIXプロトコルは、1992年(30年前!!)に取引に関わるイベントの交換のために作られた、まさにリアクティブシステムのためのプロトコルと言えます。

4RAPはそんなリアクティブシステムが多い証券ドメインのシステムですが、4RAPはあえてリアクティブシステムを採用していません。(FIXプロトコルで接続して取引を行なうコンポーネントなどの一部例外はありますが)
理由は二つです。

即応性が必須ではない

これはファンドラップやロボアドバイザーという商品の性質から来るものです。
そもそもなぜ証券ドメインで即応性が求められるのでしょうか?
例えばオンライン証券だと、

  • 不特定多数の投資家が任意のタイミングで即時に投資判断を行なえること(e.g. 利確、損切り)
  • 法的な制約を適切に課すこと(e.g. 差金決済取引の禁止)

という業務要件が存在します。
即応性が求められるのは、こういった業務要件の前提としてリアルタイムの情報(上の業務要件ならリアルタイムの残高情報)が必要になるからです。

一方でファンドラップやロボアドバイザーは投資一任契約に基づいて、エンドユーザーが投資運用業者と呼ばれるプロに資産運用をおまかせする商品です。
そのため、前者の要件である投資家が直接投資判断を行なうこと自体が発生しません。
また後者についても、4RAPでは業務をほとんど自動化して、その設計の中でそもそも法的制約を受けないスキームとなるような考慮を行なっています。
以上のような感じで、即応性が(あってもいいんだけど)必須ではないように工夫しています。

運用容易性

即応性は必須ではなくなりましたが、それだけだと別にリアクティブシステムでもよくない?ってなりますよね。(そっちの方が難易度高くてかっこいいし)
4RAPでは運用容易性を考慮してバッチシステムを採用しています。

例えば、約定などのインプットデータに誤りがあって、訂正したデータでもう一度処理を行なう障害対応などのケースを考えましょう。
リアクティブシステムの場合、約定イベント(イベント#1)に正しいイベントを再投入することになるかと思います。

ただ、約定イベントは投入し直しせばいいと言うものでもなく、結構気にするポイントがあります。

例えば、失効した注文の約定訂正だったとしたら、、、訂正された約定イベントと共に失効のイベントも再投入する必要があるかもしれません。
もしEffectively Onceの実現のために処理済みのフラグをキャッシュしていたりすると、、、プロセスの再起動が必要になってきます。
さらに、もし失効イベントして作成されたイベント#2をトリガーとしてもう後続の処理が動いてしまっていたら、、、後続のイベントに対しての訂正処理も必要になってくるかもしれません。
もちろん常にそんなことになるとは限りませんが、リアクティブシステムは障害対応の難易度(≒対応時間)の振れ幅が結構大きくなる可能性をなかなか潰せません。

一方4RAPのバッチシステムの場合、図のように各イベントは全てデータベースに永続化して、バッチはそれをトリガーとして動くような構成となっています。

約定訂正であれば、元となる約定=イベント#1のデータの修正 → バッチ再実行で事足ります。
またバッチはキャッシュを持たないので、Effectively Onceはアウトプットされたイベント#2で実現するしかありません。
そのため、アウトプットであるイベント#2を削除すれば、訂正が発生した対象のみ再実行することが可能です。
後続処理への波及についても、リアクティブシステムだとイベント#2の生成 → 即時後続処理が稼働してしまいますが、バッチシステムなら障害の検知を出来るだけ早い段階(イベント#2を使う処理が稼働する前)に行なえれば最低限の対応で済ませられます。
このようにバッチシステムは(早めの検知が出来れば)障害対応のコストの振れ幅を最小限にする(i.e. 障害が起こっても半日がかりという事態にはなりにくい)ことが出来ます。

今4RAPは、スクラッチから(少なくとも日本では)誰も知見のないサービスを作っているところです。
そのため、当面は実装ミス、設計ミスによる不具合、データ修正作業は発生する前提として設計を進める必要があります。
そして、4RAPはマルチテナントで複数の導入金融機関のシステムや業務と連携するサービスです。
もし障害対応が遅れれば、入金/出金が翌日に回ってしまう、今日購入/売却予定だったものが翌日に回ってしまうなど、エンドユーザーに大きな影響を与えてしまう可能性があります。
そのため、障害が起こったとしても復旧作業の完了見込みを精度高く予測出来るというのは、実はとても大事な機能だったりします。

こういった観点から、あえて運用の難易度の高いリアクティブシステムではなく、バッチシステムを採用しました。

将来リアクティブシステムへの移行はあるのか?

現在の業務要件からバッチシステムを採用した経緯を述べてきました。
じゃあずっとバッチシステムで行くのかというと、そうとも限らないと考えています。

4RAPも例えば他の商品を扱うなどで業務要件が変わって、リアルタイム残高を見たいということになると、リアクティブシステムへの移行を検討することになります。
また、ファンドラップやゴールベース運用が主流になって爆発的に処理件数が増えてくると、バッチシステムではタイムラインを守れなくなって、リアクティブシステムへの移行を検討するかもしれません。
業務要件はとても移ろいやすいものですので、出来るだけ移行のしやすい形(バッチのインプットをイベントとするetc)を採用して、必要に応じて(部分的な)リアクティブシステムへの移行が可能な設計を心がけたりしています。

この辺りは、システムの成熟とともにモノリシックなアーキテクチャからマイクロサービスアーキテクチャへの移行するが良い、みたいな話にも近い部分がある気がします。

次回予告

本稿では4RAPはなぜバッチシステムを採用したのかをご紹介しました。
12/25公開予定の後半では、バッチシステム内で動く個別のバッチ処理についての知見をご紹介出来ればと思います。

脚注
  1. 近いものという表現を使っているのは、弾力性の観点だけは リアクティブ宣言に準じているとはいえないケースが多いからです。
    というのも、証券ドメインのイベントは順序関係や状態の保持が重要なため、シンプルな並列処理では問題が発生することがあります。
    例えば100円の指値を持つ注文の訂正で、訂正#1=110円への訂正、訂正#2=90円への訂正を考えましょう。
    投資家は訂正#1 → 訂正#2の順序で送ったとすると、最終的に90円の指値になって欲しいと期待していることになります。
    もし処理順序が入れ替わって訂正#2 → 訂正#1の順に処理されてしまうと、最終的に110円の指値となってしまいますよね。
    これは投資家からの期待=受注に従っていないことになり大問題となります。
    他にもイベントを発生させる主体が複数(投資家や取引所など)いることも、並列処理を難しくする一因です。(同一の状態を発生ソースの異なる2つのイベントで参照することになり、なんらかの同期の仕組みが必要)
    こういったドメインの制約を考慮した上で高レベルの弾力性を実現するのは、相当難易度が高い、要件によってはそもそも不可能な場合もありえます。
    なのでリアクティブ宣言通りとは言えないかもしれませんが、ちょうど良い単語も他にないので、理解の助けを優先して、本稿ではバッチシステムの対比としてリアクティブシステムの呼称を使うこととしています。 ↩︎

Discussion