AWS Lambda + ActiveRecord で複数データベースに接続する
Rails ではない Ruby + ActiveRecord なコードベースを AWS Lambda で動作させるような状況についての備忘録です。
バッチ用途だったり API Gateway と組み合わせて API サーバーとして実装するような状況についてです。
単一のデータベースに接続する情報はよく見かけるのですが、 API Gateway + Lambda(Ruby + ActiveRecord) + Aurora MySQL(writer/reader) というような構成での接続初期設定についてはまとまった情報がなかったためこちらにまとめました。
確認した環境
- AWS Lambda (+ API Gateway 等)
- Ruby 2.7.x
- ActiveRecord 7.x
最小コード
最小コードは以下になります。
writer/reader だけではなく、データベースが別の場合の設定(connects_to
等の設定が別になる場合)は Rails で設定する場合 と一緒なので割愛します。
require 'active_record'
ENV['RAILS_ENV'] = 'test'
ActiveRecord::Base
config = ActiveSupport::ConfigurationFile.parse(Pathname.new('database.yml'))
db_config = ActiveRecord::DatabaseConfigurations.new(config)
ActiveRecord::Base.configurations = db_config
ActiveRecord::Base.establish_connection
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
connects_to database: { writing: :primary, reading: :primary_replica }
end
class User < ApplicationRecord
end
User.find(1)
database.yml は環境によりますが、最小だと以下のようになります。
_replica
に replica=true
が必須です。
DATABASE_URL_WRITER
と DATABASE_URL_READER
は SSM 等から注入するイメージです。
default: &default
# なにかあれば
test:
primary:
<<: *default
url: <%= ENV['DATABASE_URL_WRITER'] || 'mysql2://root:@127.0.0.1/db_test' %>
primary_replica:
<<: *default
url: <%= ENV['DATABASE_URL_READER'] || 'mysql2://root:@127.0.0.1/db_test?replica=true' %>
development:
primary:
<<: *default
url: <%= ENV['DATABASE_URL_WRITER'] %>
primary_replica:
<<: *default
url: <%= ENV['DATABASE_URL_READER'] %>
production:
primary:
<<: *default
url: <%= ENV['DATABASE_URL_WRITER'] %>
primary_replica:
<<: *default
url: <%= ENV['DATABASE_URL_READER'] %>
※ DATABASE_URL
だけで複数データベースの設定をする方法はないと思っていますがもしあったら教えて下さい。
明示的に writer / reader を利用する
writer を利用する場合は以下のようにします。(デフォルトは writer になっているので使うケースは少なそうです)
ActiveRecord::Base.connected_to(role: :writing) { User.find(1) }
reader を利用する場合は以下のようにします。
ActiveRecord::Base.connected_to(role: :reading) { User.find(1) }
Process, Thread まわり
AWS Lambda は Cold Start / Warm Start とあり、それぞれ観測している範囲では
- Cold Start: 新しい Process でハンドラーが起動される
- Warm Start: 既存 Process / 既存 Thread でハンドラーが起動される
という挙動を示しています。よって、 INIT コード部分で establish_connection
しておくことで、 Warm Start している間は
- ActiveRecord の ConnectionHandler によるコネクションプールが使い回される
- アプリケーションが明示的にマルチスレッドなコードを書かない限り、シングルスレッドでの動作を前提としてよい
- シングルスレッドかつ逐次実行するコードであれば、デフォルト設定である
pool=5
のままでよい(減らしてもよい)
と言えるかと思います。
参考文献
おわり