PostgreSQLからsqliteの移行をRailsを使って行う

この記事は

Ruby on Railsのカレンダー | Advent Calendar 2021 - Qiita 18日目の記事です。
昨日に引き続き、空いていたので参加してみました。
 

背景

昨日、Advent Calender Ranking 2021New RelicをRailsアプリにinstallしたところ、DB部分で速度が出ていないことが分かりました。
 
当初、このRailsアプリはHerokuにデプロイし、postgres add-onn を利用していましたが、レコードの上限数を超えたため、とりあえず無料で使えるElephant SQLへ乗り換えていました。
(この辺の話は以下に記載)

herokuのRailsアプリでDBをElephantSQLにしてみた - rochefort's blog

 
こいつが若干遅いということは分かっていたのですが、今はHerokuからVPSに移行しているので、今回ElephantSQLからDBの移行を行いました。

以下、本題。

DBの選択

PostgreSQLMySQLサービスを使っても良いのですが、メモリ使うし、
まぁ、sqlite使って壊れても割と簡単にデータ復旧できるし問題ないだろうという判断でsqliteへの移行を行うことにしました。

ElephantSQLからのdump取得

これは簡単で、consoleからボタンひとつで取り出せます。

sqliteへの取り込み

ここが今回はまりました。

dumpはsql形式なので、ちょこっと編集すればsqliteに取り込むのも簡単だろうと思っていましたが、dumpはcopy句を使っていて、これが現在のsqliteでは使えません。

他にもPostgreSQLのdumpをsqliteに変換できるJava系のツールも公開されていたので試してみましたが、古いようで動きませんでした。

後は、csvに変換して取り込む方法ですが、NULLが空白になっちゃうし(アプリケーション的には問題ないけど、なんとなく嫌)、他の方法を思索したところ、 Rails6のMultiple Databaseの機能を使って移行できるのではないかという考えが思いつきました。

ご参考)
Active Record で複数のデータベース利用 - Railsガイド

やってみる

事前準備として、ElephantSQLのdumpを移行元PostgreSQLとしてimportしておきます。
また、移行先のsqliteも必要ですので、一時的にdatabase.ymlを変更し新しいDBを作って、rails db:migrate しておきます。

multiple DB設定

config/database.yml に移行先(sqlite)、移行元(PostgreSQL)の定義をしておきます。

development:
  # 移行先
  primary:
    database: db/production.sqlite3
    adapter: sqlite3
    pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

  # 移行元
  primary_replica:
    adapter: postgresql
    encoding: unicode
    database: postgres-rails-app
    pool: 5
    username: rochefort
    password:

ApplicationRecordに primary、primary_replicaのデータベースロール(writing, reading)を設定するだけ。

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true

  connects_to database: { writing: :primary, reading: :primary_replica }
end

移行

後はrails consoleで読み書きするだけです。
書き込みは、Rails6から利用できる insert_all を活用。

def copy_table(klass)
  list = nil
  ActiveRecord::Base.connected_to(role: :reading)  { list = klass.all }
  ActiveRecord::Base.connected_to(role: :writing)  { klass.insert_all(list.map(&:attributes)) }
end

# example
copy_table(Author)

感想

という感じで、サクッとできました。
当初考えていたdumpのsqlを手動で変更する手間もないですし、 csv経由でやるより簡単な印象です。
 
こんなことのためにMulti DB使ってるのは私ぐらいでしょうが、RailsのMulti DB自体は設定も簡単なので良いですね。

See Also