こんにちは、元 Typetalk チームの永江です。(現在は Backlog Git チームに所属)
今回は Typetalk のエクスポート機能開発の裏側について書いていこうと思います。
目次
はじめに
残念なことに Typetalk はサービス終了が決まり、2025年12月1日以降は、これまでに投稿されたメッセージやファイルなどを見ることができなくなります。
そのため、今まで Typetalk をお使いいただいたユーザー様ができるだけ困らないようにサポートする意図でエクスポート機能を開発しました。
Typetalkに投稿した内容の「エクスポート機能」をリリースしました
エクスポート機能には以下の3種類のエクスポート方法があります。
- トピック / DM エクスポート
- ボット一覧エクスポート (管理者のみ)
- トピック一括エクスポート (管理者のみ)
今回はトピック一括エクスポートの開発で検討した3つのポイントを紹介します。
ポイント1 – アーキテクチャ設計
まず1つ目のポイントはシステム全体のアーキテクチャ設計についてです。
検討
アーキテクチャ設計するに当たり、以下の3つの案を考えました。
- Typetalk (EC2 上で動いている Web アプリケーション) のみで完結
- Typetalk 内にエクスポート機能を追加
- Typetalk → SQS + Lambda
- Typetalk から SQS 経由で Lambda にリクエストする
- Lambda でエクスポートプロセスを実行する
- Typetalk → Step Functions + Lambda
- Typetalk から Step Functions にリクエストする
- エクスポートプロセス全体を Step Functions ワークフローとして定義する
- Lambda を Step Functions ステートマシンとして設定する
- Lambda でエクスポートプロセスを実行する
次に、それらの構成案を以下の3つの観点で比較・検討しました。
- エクスポート機能実行時の負荷が、Typetalk のチャット機能の利用に影響を与えないようにする
- 組織単位 / トピック単位で容易にエラーハンドリングできるようにする
- 処理単位でパフォーマンスの調整が容易にできる
観点 1 | 観点 2 | 観点 3 | |
---|---|---|---|
Typetalk | × | × | × |
Typetalk → SQS + Lambda | ○ | △ | △ |
Typetalk → Step Functions + Lambda | ○ | ○ | ○ |
- Typetalk
- × : エクスポート処理は同一リソースを使用して実行されるため、システム負荷が集中し、他の機能が正常に動作しない可能性がある
- ×:エラー処理が難しく、開発コストが高い
- ×:パフォーマンスチューニングが難しい
- Typetalk → SQS + Lambda
- ○:リソースを分離できるため、システム負荷が分散され、他の機能の動作に影響を与えない
- △:Lambdaごとのエラー処理は簡単だが、エクスポート処理全体のエラー処理には工夫が必要で開発コストが高額になる
- △:Lambdaごとのパフォーマンスチューニングは容易ですが、エクスポートプロセス全体のパフォーマンスチューニングは困難
- Typetalk → Step Functions + Lambda
- ○:リソースを分離できるため、システム負荷が分散され、他の機能の動作に影響を与えない
- ○:エラー処理が容易で開発コストが低い
- ○:ワークフロー / Lambdaごとにパフォーマンスチューニングが容易
結論
以上のことから3つの観点を満たしている、Typetalk → Step Functions + Lambda を選択しました。
ポイント2 – Lambda の実行時間制限
2つ目のポイントは、Lambda の実行時間制限についてです。
課題
ポイント1で決定した通り、Step Functions + Lambda で以下のようなワークフローを作成しました。
- Lambda: A
- 組織に紐づくトピックの ID を取得する
- Lambda: B
- トピック ID からトピックに紐づくデータを取得する
- 取得したデータからファイルを作成し、S3 にアップロードする
- Lambda: C
- S3 からファイルをダウンロードする
- ダウンロードしたファイルを圧縮して、再度 S3 にアップロードする
しかし、ワークフローを何度か動かしていると、Lambda: C の処理がエラーになるケースがありました。
原因を調査したところ、メッセージ数の多いトピックを持つ組織の場合ファイル容量が大きくなり、S3 からファイルをダウンロードするのに時間がかかっている事がわかりました。
Lambda には最大 15 分の実行時間の制限があり、処理時間が長すぎてタイムアウトしていたのです。
Lambda は、コードを一定時間実行してからタイムアウトします。タイムアウトとは、Lambda 関数がタイムアウトするまでの最大実行時間です。この設定のデフォルト値は 3 秒ですが、最大値の 900 秒 (15 分) まで 1 秒単位で調整できます。
検討
この課題を解決するために、3つのアーキテクチャ変更案を考えました。
- Glue Job / S3
- Lambda を Glue Job に変更する
- Lambda / Dynamo DB
- S3 を Dynamo DB に変更する
- Lambda / EFS
- S3 を EFS に変更する
次に、それらの構成案を開発コスト、処理性能の観点で比較・検討しました。
開発コスト | 処理性能 | |
---|---|---|
Glue Job / S3 | × | × |
Lambda / DynamoDB | × | ○ |
Lambda / EFS | ○ | ○ |
- Glue Job / S3
- ×:変更コストが高い
- プログラミング言語を変更する必要がある (Typescript → Python または Scala)
- 導入経験がないため、システム構築に時間がかかる
- ×:実行時間制限をなくすことでエラーは回避できるが、プロセス自体のパフォーマンスは向上しない
- ×:変更コストが高い
- Lambda / Dynamo DB
- ×:変更コストが高い
- データ形式を変更する必要があり、修正する部分が多い
- 経験不足のため構築に時間がかかる
- ○:S3 よりも高速なのでパフォーマンス向上が期待できる
- ×:変更コストが高い
- Lambda / EFS
- ○:変更コストが低い
- AWS の機能として Lambda の共有ストレージとして指定できるため、プログラムの変更を最小限に抑えることができる
- システムがすでに実装されているため、構築する必要がない
- ○:EFS を Lambda の共有ストレージとして設定することができ、その結果ダウンロード処理自体が不要となり、全体の実行時間が短縮される
- ○:変更コストが低い
結論
以上のことから、2つの観点を満たしている Lambda + EFS を選択しました。
ポイント3 – データベースのボトルネック
最後のポイントは、データベースのボトルネックについてです。
課題
Step Functions + Lambda を使うことでプログラムレベルでの分離はできたのですが、データベースレベルではまだ既存機能とリソースを共有していました。
そのため、大きなトピックのエクスポートを実行した際に既存機能の動作が重くなる懸念がありました。
検討
そのため、エクスポート機能専用のリーダーインスタンスを追加し、既存機能とリソースを分離することを考えました。
しかし、その場合エクスポートを使っていないときにリソースが無駄になることが懸念事項にありました。
- RI (リザーブドインスタンス) のため、できるだけリソースは活用したい
- 既存機能の DB 負荷が時折高くなることがあったため、リスクヘッジしたい
結論
エクスポート機能の通信は追加リーダーインスタンスのみに流しつつ、既存機能の通信は追加リーダーインスタンスにも流して負荷分散するようにしました。
- エクスポート機能の負荷は既存機能に影響しない
- 既存機能の負荷を分散できる
おわりに
以上、Typetalk のエクスポート機能の開発で考慮した3つのポイントでした。
サービスを終了するための機能提供というところで、Typetalk チームメンバーとしては複雑な心境での開発でしたが、議論を重ね、良い設計・実装ができたと思っています。
また、これまで Typetalk をお使いいただいた皆様にこの場を借りてお礼申し上げます。
ありがとうございました。