見出し画像

RailsでマルチDB(Aurora リードレプリカ & Autoscaling)をやろうとするとすんなり出来ない

こんにちは、taskeyの田代です。

弊社サービス

21年始まってしまいましたが、20年でやったことで書いていないことがあったので、書き残していくチェレンジvol 3です。

弊社ではAWS Auroraを採用しておりまして、それのリードレプリカ & Autoscaling対応をしてDBの冗長性を高めたことを書いていきます。

そもそもタイトルに有るように、Rails x AuroraでマルチDBを行おうとするとすんなり出来ません。まず各所問題点を確認していきます。

【問題点その①】
 「Auroraがフェイルオーバーした時に、connection poolの切り替えがうまく行かない」

Auroraでリードレプリカを追加してフェイルオーバーを起こすとmasterDBとreadDBが切り替わります。その際、Railsからのconnection poolがエンドポイントをキャッシュしてしまい、うまく切り替わらないという問題があります。
詳しくは「Rails Aurora フェイルオーバー」で検索してみてください。
こちらが主な原因として、RailsからのAuroraマルチDBの使用難易度が上がります。connection pool?普段意識しないから知らないなぁ。。。。とほほ、、、という感じです。

【問題点その②】
 「masterDBとreadDBに投げるSQLの割り振りをどうするか」

そもそもの話なのですが、マルチ構成にした場合、単純にマスターのクラスターのmasterエンドポイントのみの参照にすると、readDBがただフェイルオーバー専用という、マルチDB有るまじき勿体ない構成になってしまいます。(※ ダウンタイム低下に繋がるので意味がなくは無い)
本来であれば読み込みはreadDB、書き込みやリアルタイム性重視のSQLはmasterDBに向けて投げたいですよね。。。
また、Rails 6.0.0以降ですと、Defaultの機能でマルチDB化を行えるのですが、他にも選択肢がありますので、それら込みで選定を行いました。

【問題点その③】
「Aurora AutoscalingでScale outしたDBにアクセスが広がってくれない」

こちらもRailsのconnection pool系の問題なのですが、read系が増えたとしても、processがrestartされない限り、connectionがうまく広がってくれません。
こちらに関しては「その①」と同様でconnection poolをオフにする、もしくはRailsをDBが増えた段階でrestartさせる等のアプローチが思いつきます。

問題点まとめ

 - Auroraがフェイルオーバーした時に、connection poolの切り替えがうまく行かない
 - masterDBとreadDBに投げるSQLの割り振りをどうするか
 - Aurora Autoscalingで増えたDBにアクセスが広がってくれない

connection pool問題大きいのですが、単純にオフにしてしまうのは、折角の恩恵が受けれなくなってしまうので出来れば避けたいですね。
また、SQLの割り振りに関してもRails側でModelからのSQLの 呼び出し、または書き出しの度に処理を分けるような事もしたくないです。
上記問題点を踏まえ、技術選定をしていきました。

技術選定

「Auroraがフェイルオーバーした時に、connection poolの切り替えがうまく行かない」

一つの解決策として、connection poolをオフにする という方法もありますが、こちらは接続が都度必要になりAPIを速度低下の懸念が目に見える為、却下です。

他に思いつくアプローチとしては、DBconnectionでerrorが吐かれた場合に再接続を行う、というものです。
こちら、調べたところナイスgemがありました。しかも日本製です👏

検証してみたところ、フェイルオーバー時の切り替わりが問題い事を確認出来た為、本番導入させていただきました。

「masterDBとreadDBに投げるSQLの割り振りをどうするか」

こちらは先人の知恵により数多くの選択肢が存在します。
選定ポイントをしっかりと出してから選びました。

 - アプリケーション側でmaster / read の割り振りを都度書きたくない
 - Rails 6.0.0のDefault機能は使えない ※ 弊社Railsはまだ5.1.2
 - メンテナンスが継続されている物が好ましい ※ Railsへの組み込みによりメンテナンスが止まっている物が多い

これらを踏まえ選定したgemが「Makara」です。
master / readへのSQLの割り振りを勝手に自動で行ってくれます。
また、設定によってはmasterを必ず向くような事もできます。
    ※ リアルタイム性重視のものなど
Optionも豊富でとても良さそう印象を受けました。
検証項目もクリアです。しっかりとmasterとreadで割り振れる事を確認出来ました。

OCTOPUS VS MAKARAという記事が参考になりますので、お時間有る方は是非読んでみてください。

「Aurora Autoscalingで増えたDBにアクセスが広がってくれない」

こちらに関してのは、puma_worker_killerを導入して15分毎にrailsをrestartさせるという方法を取らせていただきました。
puma_worker_killerはメモリ増加対策で使われる事が多いのですが、変則的な使用方法です。

これによって一定時間でprocessをresartしてくれるので、ジワジワとアクセスが広がっていきます。

ただ、再起動する必要の無いプロセスをrestartさせてしまっているので、微妙な判断だったかな?という気もしています。
Autoscalingに関してはEventが取得出来ます。ですので、Scale in / Scale outのイベントを取得して、Railsのプロセスを再起動させるLambdaを作るのが綺麗かな?と思っています。
とは言え、puma_worker_killerを入れた事によって、DB側のclened upで残ってしまっている謎processが定期的に消されるというメリットもかなり大きいです。悩みどころです。
   ※ 謎processに関しても、まだ解明出来ておりません。。。DB or ARのVersionによるバグなのだろうか。。。

正直こちらに関しては、まだ最適解ではないと思っています。
余裕のあるタイミングで再度チューニングをしたいなと思っておりますので、こちらの記事を読んで他のアプローチを採用している方、思いついた方はご教授頂けますと幸いです。

まとめ

Rails on AuroraでのマルチDB対応に関しては、「Makara」「puma_worker_killer」「mysql2-aurora」の3つのgemを導入することにより実現することが出来ました。
本番動作も今の所問題なく、master / readの割り振りも綺麗にされております。
これで弊社はFargateとAurora Autoscalingを手に入れたのでアプリケーションもDBも冗長性が高くなりました。👏👏👏

検証に関して、細かく項目を洗い出して行ったり、AWSやクラスメソッドへも沢山質問させていただいたのですが、こちらで紹介してしまうと長くなってしまいますので、また別の機会に書かせて頂けましたら幸いです。

Aurora AutoscalingやFargateを導入するということはサービスが伸びているということでもあり、嬉しい限りです。
ですが、なかなかに知見が限られてしまいます。
一例として参考になりましたら幸いです。

Fargate移行の記事もありますので、良かったらご閲覧ください。

募集

弊社正直、エンジニアが足りてません!!!

Serverエンジニアに関して、下記現状です!!!
ご興味有る方是非ご連絡くださいー!!!

アピールポイント

・ Spec整備済み
・ OpenAPI 整備済み
・ 機能開発毎にエンジニアで設計や命名を話し合う風習あり
・ データマート(BigQuery)整備済み
・ Deployフロー整備済み
・ 負債に対しての定期的なMTGと、それを解像度高く分解して実行できる環境
・ ECS on Fargate、Aurora Auto Scaling とモダンなインフラを採用
・ データ分析、SRE、フロントエンド(React)、リコメンドエンジン等の幅の広いスキルの拡張が可能

現状の課題

・ 現状がRails5系、Ruby2.5系と、最新のバージョンへの追従が遅れている
=> 直近でVersionUP予定あり
・ APIで返却するJSONが最適化されていない
=> JSON整備予定あり
・ JSONResponse返却のGetRequestに対してのCDN整備が整っていない
=> 長期的観点でCDN導入予定
・施策速度優先とエンジニア不足により技術的負債を解消し切れていない  => DX、開発効率を上げていくためにも必須

この記事が気に入ったらサポートをしてみませんか?