Rails + PostgreSQL でデータベースを作り直す
PostgreSQLでは、他のセッションが同じdatabaseに接続しているときは DROP DATABASE
できないようになっている。
my_database=# DROP DATABASE my_database; ERROR: database "my_database" is being accessed by other users DETAIL: There is 1 other session using the database.
そのため、Rails で以下のようにデータベースを作り直すときにしばしば問題になる。
$ rails db:drop db:setup PG::ObjectInUse: ERROR: database "my_database" is being accessed by other users DETAIL: There is 1 other session using the database. Couldn't drop database 'my_database' rails aborted! ActiveRecord::StatementInvalid: PG::ObjectInUse: ERROR: database "my_database" is being accessed by other users (ActiveRecord::StatementInvalid) DETAIL: There is 1 other session using the database. Caused by: PG::ObjectInUse: ERROR: database "my_database" is being accessed by other users (PG::ObjectInUse) DETAIL: There is 1 other session using the database. Tasks: TOP => db:drop:_unsafe (See full trace by running task with --trace)
これを解決するためには、他のセッションを切断してから再度 DROP DATABASE
すればいいが、以下のSQLを実行することでも切断することができる。
SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'my_database' AND pid <> pg_backend_pid();
my_database=# SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'my_database' AND pid <> pg_backend_pid(); pg_terminate_backend ---------------------- t (1 row)
そのため、以下のように他セッションの接続を切断する Rake task を作成し実行すれば他のセッションを気にすることなく作成しなおすことができるようになる。
lib/tasks/db/connection.rake:
namespace :db do namespace :connection do desc 'Close all DB connections' task :close => :environment do db_name = ActiveRecord::Base.connection.current_database ActiveRecord::Base.connection.execute(ActiveRecord::Base.sanitize_sql_array([<<~SQL, db_name])) SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '%s' AND pid <> pg_backend_pid(); SQL end end end
$ rails db:connection:close db:drop db:setup Dropped database 'my_database' Created database 'my_database'
環境
- Ruby: 3.2.2
- Rails: 7.0.7
- PostgreSQL: 15.x