HubSpotは、コンシューマーグループの遅延の蓄積を回避し、リアルタイムのトラフィックの処理を優先するために、同じプロデューサーの複数のKafkaトピック(スイムレーンと呼ばれる)上でメッセージをルーティングすることを採用した。トラフィック急増の自動検知と手動検知を組み合わせて使用することで、同社は顧客の大半のワークフローが遅延なく実行されるようにしている。
HubSpotは、ビジネスプロセス自動化プラットフォームを提供しており、その中核には、アクションの実行を強力に後押しするワークフローエンジンが採用されている。このプラットフォームは、数百万のアクティブなワークフローを処理し、毎日数億、毎秒数万のアクションを実行する。
ワークフローエンジンの概要(出典:HubSpot Engineering Blog)
ほとんどの処理は非同期でトリガーされ、メッセージングにはApache Kafkaが使用されるため、アクションのソース/トリガーと、それを実行するコンポーネント間の分離が可能になる。このプラットフォームは、様々なソースからアクションデータを配信するために、多くのKafkaトピックを使用している。メッセージ・ブローカーを使用することの潜在的な欠点は、メッセージがあまりにも早く公開され、コンシューマーがそれを時間内に処理できない場合、コンシューマー・ラグと呼ばれる処理待ちのメッセージが蓄積されることだ。
HubSpotのエンジニアリング・リードであるAngus Gibbs氏は、メッセージ処理がほぼリアルタイムで行われるようにするための課題について次のように述べている。
トピックに突然大量のメッセージが書き込まれると、処理しなければならないメッセージのバックログが発生します。コンシューマー・インスタンスの数を増やせますが、そうするとインフラ・コストが増大します。オートスケールを追加できますが、新しいインスタンスを追加するには時間がかかり、通常、顧客はワークフローがほぼリアルタイムで登録を処理することを期待する。
チームは、同じタイプまたは同じソースからのすべてのメッセージに対して同じトピックを使用することが、解決すべき問題であると認識した。このプラットフォームが多くの顧客に利用されていることを考えると、1人または少数の顧客が大量のメッセージを作成し始めると、すべてのトラフィックが遅延し、すべての顧客のユーザーエクスペリエンスが損なわれてしまう。
この懸念に対処するため、開発者はスイムレーンと呼ぶ複数のトピックを使用し、それぞれに専用のコンシューマ・プールを用意することを選択した。このパターンを適用する最も簡単な方法は、2つのトピックを使用することだ。1つはリアルタイムのトラフィックを担当し、もう1つはオーバーフローのトラフィックを担当する。2つのスイムレーンは全く同じ方法でトラフィックを処理するが、それぞれのトピックは独立したコンシューマーのラグを持つことになり、両者の間で適切なメッセージのルーティングを行うことで、リアルタイムのスイムレーンが遅延を回避できる。
Kafkaスイムレーン(出典:HubSpot Engineering Blog)
可能な限り、公開されたメッセージから抽出されたメタデータに基づいて、スイムレーン間のメッセージの自動ルーティングが実装された。例えば、リフィル操作のバルクインポートのいくつかは、メッセージスキーマ内でそのように明確にマークされており、ルーティングロジックはそれらをオーバーフロースイムレーンに簡単に発行できた。さらに開発者は、トラフィックをレート制限するために顧客ごとのコンフィギュレーションを導入し、メッセージ・コンシューマーの最大スループット・メトリクスに基づいて適切な閾値を設定した。
スイムレーン間でどのようにメッセージをルーティングするかを決めるもうひとつの切り口は、アクションの実行時間を見ることだった。ファクトなアクションは一方のスイムレーンに、遅いアクションは他方のスイムレーンにルーティングされる。これは、顧客が任意の Node または Python コードを実行するカスタムアクションを作成できる HubSpot プラットフォームに特に関連する。
最後に、チームは、ある顧客からのトラフィックが予期せずプライマリ(リアルタイムまたは高速)スイムレーンに遅延を発生させ始め、自動ルーティングメカニズムが作動しなかった場合に備えて、特定の顧客のすべてのトラフィックを専用のスイムレーンに手動でルーティングする手段を開発した。そうすれば、チームが遅延の原因のトラブルシューティングに取り組む間、トラフィックを隔離できる。