スクレイピングでデータ収集 + 処理しつつ本番DBの負荷軽減しながらリアルタイム感を損なわない0円運用を考える【Supabase利用時】
![masa5714](https://res.cloudinary.com/zenn/image/fetch/s--qV-pD3R_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/3e30cf0778.jpeg)
ソースコードを公開しました
render.comでホスティングする想定でNode.jsによる擬似的なレプリケーションを簡単に実行できるソースコードを公開しました。よろしければご利用ください。
![masa5714](https://res.cloudinary.com/zenn/image/fetch/s--qV-pD3R_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/3e30cf0778.jpeg)
前提
金銭的なコストを下げる。可能な限り 0円で 済ませる。そのためVPSを使わない予定。
なおかつ複雑化しないように注意する。
実現したいこと
- スクレイピングで収集したデータをリアルタイム感を損なわずに配信したい。
- 独自ダッシュボードでコンテンツ管理をしたい。
- ローカルDBと本番DBは一対一にしたい。(マイグレーションの複雑化を避ける。標準的なSupabaseのレールから外れないようにする。)
- スクレイピング&データ処理の用DBを別途設けたい。(スクレイピング処理でInputが多発する。データ処理ではInputとOutputが多発する。これらのI/Oは本番DBには無駄である。本番DBへの負荷は処理結果を丸々コピーする際にだけ発生する程度に抑える。)
DBとしては下記の3つ出てくる。
- 本番DB(ユーザー公開向けのDB)
- スクレイピング&データ処理DB(データ収集と処理結果を格納する。結果はレプリケーションで本番DBに丸々コピーする。)
- ローカルDB(開発者向けのローカル環境のDB)
![masa5714](https://res.cloudinary.com/zenn/image/fetch/s--qV-pD3R_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/3e30cf0778.jpeg)
Supabase利用時の問題点
- Supabase CLIでターゲットの切り替えが面倒なのと不具合の温床になりそうなので、ローカルDBと本番DBの関係は崩したくない。
- Supabaseでは
CREATE SUBSCRIPTION
という標準的なレプリケーション機能が使えない。(superuser
権限が与えられていないため。 )
これらを解決するために擬似的なレプリケーションを実装する。(後述します)
![masa5714](https://res.cloudinary.com/zenn/image/fetch/s--qV-pD3R_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/3e30cf0778.jpeg)
処理の全体図
マイグレーションを一対一に保ちつつも負荷軽減をさせつつ、レプリケーションも無料で実現できる。また、リアルタイム感も損なわれない運用ができそうだ。
ポイント
1. Render.comで擬似的なレプリケーションを行う(AとB箇所)
先述の通り、Supabaseで無料ではレプリケーションできない。苦肉の策としてNode.jsのコードでLISTENしておき、本番DBとデータ処理用DBを双方向でデータ同期をする。
- ダッシュボードから追加したデータは本番DBに直接入れる。スクレイピングに必要になりそうなデータだけをスクレイピング用DBに同期させる。
- スクレイピング後データ処理が終わったら本番DBにデータ同期させる。
- A方向のNOTIFYの頻度を減らすように心がける。同期すべきデータが蓄積されたタイミングで行うべき。
▼擬似的なレプリケーションの実現方法は下記記事を参照。(Node.jsでLISTENしておき、DBに NOTIFYクエリ が飛んだら疑似レプリケーションを実行する形)
2. ダッシュボードの運用には注意が必要
本番DBへのマイグレーションファイルを作るためにローカルDBで開発する必要がある。しかし、運用データ自体は本番DBに入れなければならない。そのため、ダッシュボード開発時にはローカルDBに向ける必要があるし、実際にデータ挿入する運用フェーズには本番DBに通信を向けなければならない。
かと言って今回はグローバル環境にデプロイする訳ではないので、ビルド時に振り分けるということはしない。
独自のSupabaseクライアント関数を作り引数で dev
または production
を渡すことで簡単に切り替えられるようにしておくべき。
3. 本番DBとデータ処理DBのテーブル構造の違い
ほとんど同じテーブル構造を利用するが、コピー元のテーブルのみ replicate_status
カラムを追加しておく。 pending(処理待ち)
delete(削除待ち)
running(実行中)
done(完了)
の4種類のステータスで管理する。
4. ダッシュボードのデータが開発に必要になったらどうする?
ダッシュボードのデータは本番DBに直接行ってしまうので、開発用のローカルDBにはデータが残ってくれない。そうなると開発が難しくなるので、必要なデータは DBeaver
などのソフトウェアを使って本番DBのデータをローカルDBへと必要なデータだけコピーすればOK。
![masa5714](https://res.cloudinary.com/zenn/image/fetch/s--qV-pD3R_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/3e30cf0778.jpeg)
Render.comにホストすべきなのか?
擬似的なレプリケーション処理はNode.jsが動くマシンならどこにホストしても良い。Webhookとは異なりPostgreSQLのnotification機能を活用して動いているため、自宅PCでもいいし、本当にどこにでもホストできる。わざわざグローバル環境に晒す必要もない。
個人的には意識せずにレプリケーションしててほしいので、
外部マシンで勝手に動いている状態にしたい上に無料で動いてほしいので Render.com にホストしてもらっている。
総括
無料縛りで実現しようとすると複雑化してしまいがちだが、データ同期を除けばマイグレーションも一対になっておりシンプルな構造にできているように思う。
実際に運用してみて改めて感想を書きたい。