Lỗi drop Rails + Postgres: cơ sở dữ liệu đang được người dùng khác truy cập


89

Tôi có một ứng dụng đường ray đang chạy trên Postgres.

Tôi có hai máy chủ: một để thử nghiệm và một để sản xuất.

Rất thường xuyên, tôi cần sao chép DB sản xuất trên máy chủ thử nghiệm.

Lệnh tôi đang chạy qua Vlad là:

rake RAILS_ENV='test_server' db:drop db:create

Vấn đề tôi đang gặp phải là tôi nhận được lỗi sau:

ActiveRecord::StatementInvalid: PGError: ERROR: database <database_name> is being accessed by other users DROP DATABASE IF EXISTS <database_name>

Điều này xảy ra nếu ai đó đã truy cập ứng dụng qua web gần đây (postgres giữ một "phiên" được mở)

Có cách nào để tôi có thể kết thúc các phiên trên DB postgres không?

Cảm ơn bạn.

Biên tập

Tôi có thể xóa cơ sở dữ liệu bằng giao diện của phppgadmin nhưng không phải với tác vụ rake.

Làm cách nào tôi có thể sao chép sự sụt giảm của phppgadmin với một tác vụ rake?


Đảm bảo rằng bạn không có kết nối với cơ sở dữ liệu nếu không nó sẽ không làm rớt nó. Kiểm tra thêm về điều này ở đây .
Nesha Zoric

Câu trả lời:


80

Nếu bạn tắt các kết nối postgresql đang chạy cho ứng dụng của mình, thì bạn có thể chạy db: drop. Vậy làm thế nào để tiêu diệt những kết nối đó? Tôi sử dụng tác vụ cào sau:

# lib/tasks/kill_postgres_connections.rake
task :kill_postgres_connections => :environment do
  db_name = "#{File.basename(Rails.root)}_#{Rails.env}"
  sh = <<EOF
ps xa \
  | grep postgres: \
  | grep #{db_name} \
  | grep -v grep \
  | awk '{print $1}' \
  | xargs kill
EOF
  puts `#{sh}`
end

task "db:drop" => :kill_postgres_connections

Việc loại bỏ các kết nối từ bên dưới đường ray đôi khi sẽ khiến nó bị ngắt vào lần sau khi bạn cố tải một trang, nhưng việc tải lại nó một lần nữa sẽ thiết lập lại kết nối.


2
Tôi đã phải thêm sudo vào xargs và thay đổi tên cơ sở dữ liệu, nhưng nó hoạt động. TY
lzap

1
Tương tự cho tôi ... đổi thành "xargs sudo giết" và db_name để "my-phát triển cơ sở dữ liệu tên" mã hóa cứng
Kevin Dewalt

6
task "db:drop" => :kill_postgres_connectionsTôi nghĩ rằng dòng này nên được loại bỏ, nó nguy hiểm để mở rộng hành vi của nhiệm vụ hệ thống, theo quan điểm của tôi.
msa.im

Thay vì mã hóa tên cơ sở dữ liệu của bạn, chỉ cần sử dụng như sau:db_name = Rails.configuration.database_configuration[Rails.env]['database']
tala

39

Cách dễ dàng hơn và cập nhật hơn là: 1. Sử dụng ps -ef | grep postgresđể tìm kết nối # 2.sudo kill -9 "# of the connection

Lưu ý: Có thể có PID giống hệt nhau. Giết một người giết tất cả.


Con số nào trong kết quả đại diện cho PID? Tôi thấy 3 cột không được gắn nhãn có các số trông giống như PID.
BradGreens

1
@BradGreens cột thứ hai (Tôi đang sử dụng Mac Terminal)
s2t2

Không tìm thấy gì với ps, nhưng vẫn gặp lỗi trên db: drop.
JosephK

17

Đây là một cách nhanh chóng để loại bỏ tất cả các kết nối đến cơ sở dữ liệu postgres của bạn.

sudo kill -9 `ps -u postgres -o pid` 

Cảnh báo: điều này sẽ giết mọi tiến trình đang chạy mà postgresngười dùng đã mở, vì vậy hãy đảm bảo rằng bạn muốn thực hiện việc này trước.


11
Trong hệ thống của tôi, tôi sử dụng sudo kill -9 `ps -u postgres -o pid=` thay thế, vì vậy tiêu đề PID sẽ không được in ra ps, do đó, đối số chuỗi không được chuyển đến kill, do đó lỗi sẽ không được phát sinh. Mẹo tuyệt vời trong mọi trường hợp.
deivid

1
Tôi liên tục nhận được lượt ủng hộ và lượt phản đối, dẫn đến xếp hạng gần như bằng không. Có vẻ là một "sửa chữa nhanh" gây tranh cãi. Hãy để tôi chỉ cho biết hồ sơ mà tôi đã đưa ra cảnh báo rằng nó nguy hiểm. :)
Jamon Holmgren

3
Sử dụng điều này để bắt đầu lại postgresql nếu bạn đang sử dụng Ubuntu:sudo service postgresql start
Frikster

9

Khi chúng tôi sử dụng phương thức "kill process" ở trên, db: drop không thành công (if: kill_postgres_connections là điều kiện tiên quyết). Tôi tin rằng đó là vì kết nối mà lệnh rake đang sử dụng đã bị giết. Thay vào đó, chúng tôi đang sử dụng lệnh sql để bỏ kết nối. Điều này hoạt động như một điều kiện tiên quyết cho db: drop, tránh nguy cơ giết chết các quy trình thông qua một lệnh khá phức tạp và nó sẽ hoạt động trên bất kỳ hệ điều hành nào (gentoo yêu cầu cú pháp khác cho kill).

cmd = %(psql -c "SELECT pg_terminate_backend(procpid) FROM pg_stat_activity WHERE procpid <> pg_backend_pid();" -d '#{db_name}')

Đây là tác vụ rake đọc tên cơ sở dữ liệu từ database.yml và chạy lệnh (IMHO) được cải tiến. Nó cũng thêm db: kill_postgres_connections làm điều kiện tiên quyết cho db: drop. Nó bao gồm một cảnh báo hét lên sau khi bạn nâng cấp đường ray, cho biết rằng bản vá này có thể không còn cần thiết nữa.

xem: https://gist.github.com/4455341 , bao gồm tài liệu tham khảo


8

Tôi sử dụng tác vụ rake sau để ghi đè drop_databasephương thức Rails .

lib/database.rake

require 'active_record/connection_adapters/postgresql_adapter'
module ActiveRecord
  module ConnectionAdapters
    class PostgreSQLAdapter < AbstractAdapter
      def drop_database(name)
        raise "Nah, I won't drop the production database" if Rails.env.production?
        execute <<-SQL
          UPDATE pg_catalog.pg_database
          SET datallowconn=false WHERE datname='#{name}'
        SQL

        execute <<-SQL
          SELECT pg_terminate_backend(pg_stat_activity.pid)
          FROM pg_stat_activity
          WHERE pg_stat_activity.datname = '#{name}';
        SQL
        execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
      end
    end
  end
end

Bạn đã bao giờ đọc cảnh báo sản xuất đó chưa? Chỉ tò mò thôi: P
Vinicius Brasil

6

Vui lòng kiểm tra xem bảng điều khiển hoặc máy chủ rails của bạn có đang chạy trong tab khác không và sau đó

dừng máy chủ và bảng điều khiển rails.

sau đó chạy

 rake db:drop

5

Để ứng dụng của bạn đóng kết nối khi hoàn tất. PostgreSQL không giữ cho các kết nối mở, mà là ứng dụng giữ kết nối.


1
Tôi có thể xóa cơ sở dữ liệu bằng giao diện của phppgadmin nhưng không phải với tác vụ rake. Làm cách nào tôi có thể sao chép sự sụt giảm của phppgadmin với một tác vụ rake?
fjuan

Xin lỗi, không thể giúp bạn ở đó, tôi không có kinh nghiệm về cào. Tuy nhiên, lỗi cho biết một người dùng khác vẫn đang sử dụng cơ sở dữ liệu. Đó là lý do tại sao bạn không thể xóa cơ sở dữ liệu, không phải bằng cách cào không bằng PhpPgAdmin, không thể. Từ hướng dẫn sử dụng, DROP DATABASE: nó không thể được thực thi khi bạn hoặc bất kỳ ai khác được kết nối với cơ sở dữ liệu đích.
Frank Heikens

3

Rails có khả năng kết nối với cơ sở dữ liệu để thả nó nhưng khi bạn đăng nhập qua phppgadmin, nó đang đăng nhập qua cơ sở dữ liệu template1 hoặc postgres, do đó bạn không bị ảnh hưởng bởi nó.


Làm thế nào tôi có thể buộc các đường ray thả cơ sở dữ liệu? Tôi có nên xác định hành động rake của riêng mình bằng cách sử dụng các lệnh SQL postgres không?
fjuan

2

Tôi đã viết một viên ngọc có tên là pgreset sẽ tự động ngắt các kết nối đến cơ sở dữ liệu được đề cập khi bạn chạy rake db: drop (hoặc db: reset, v.v.). Tất cả những gì bạn phải làm là thêm nó vào Gemfile của bạn và vấn đề này sẽ biến mất. Tại thời điểm viết bài này, nó hoạt động với Rails 4 trở lên và đã được thử nghiệm trên Postgres 9.x. Mã nguồn có sẵn trên github cho bất kỳ ai quan tâm.

gem 'pgreset'

Không có gì ngoại trừ viên ngọc của bạn sẽ làm điều đó - kết nối không tồn tại (tìm kiếm ps grep, v.v.), nhưng rails nghĩ rằng nó đã làm được. Cảm ơn nhiều!!
JosephK

1

Bạn có thể chỉ cần bắt mã ActiveRecord để thực hiện việc giảm.

Đối với Rails 3.x:

# lib/tasks/databases.rake
def drop_database(config)
  raise 'Only for Postgres...' unless config['adapter'] == 'postgresql'
  Rake::Task['environment'].invoke
  ActiveRecord::Base.connection.select_all "select pg_terminate_backend(pg_stat_activity.pid) from pg_stat_activity where datname='#{config['database']}' AND state='idle';"
  ActiveRecord::Base.establish_connection config.merge('database' => 'postgres', 'schema_search_path' => 'public')
  ActiveRecord::Base.connection.drop_database config['database']
end

Đối với Rails 4.x:

# config/initializers/postgresql_database_tasks.rb
module ActiveRecord
  module Tasks
    class PostgreSQLDatabaseTasks
      def drop
        establish_master_connection
        connection.select_all "select pg_terminate_backend(pg_stat_activity.pid) from pg_stat_activity where datname='#{configuration['database']}' AND state='idle';"
        connection.drop_database configuration['database']
      end
    end
  end
end

(từ: http://www.krautcomputing.com/blog/2014/01/10/how-to-drop-your-postgres-database-with-rails-4/ )


1

Tôi đã gặp vấn đề tương tự khi làm việc với ứng dụng Rails 5.2 và cơ sở dữ liệu PostgreSQL trong sản xuất.

Đây là cách tôi đã giải quyết nó :

Trước tiên, hãy đăng xuất mọi kết nối với máy chủ cơ sở dữ liệu trên PGAdmin Client nếu có.

Dừng mọi phiên sử dụng cơ sở dữ liệu từ thiết bị đầu cuối.

sudo kill -9 `ps -u postgres -o pid=`

Khởi động máy chủ PostgreSQL, vì thao tác hủy ở trên đã dừng máy chủ PostgreSQL.

sudo systemctl start postgresql

Thả cơ sở dữ liệu trong môi trường sản xuất thêm các đối số sản xuất.

rails db:drop RAILS_ENV=production DISABLE_DATABASE_ENVIRONMENT_CHECK=1

Đó là tất cả.

Tôi hi vọng cái này giúp được


1

Điều này đã làm việc cho tôi (rails 6): rake db:drop:_unsafe

Tôi nghĩ rằng chúng tôi đã có một cái gì đó trong cơ sở mã của chúng tôi đã khởi tạo kết nối db trước khi tác vụ rake cố gắng loại bỏ nó.


0

Chỉ cần đảm bảo rằng bạn đã thoát bảng điều khiển rails trên bất kỳ cửa sổ đầu cuối nào đang mở và thoát khỏi máy chủ rails ... đây là một trong những lỗi phổ biến nhất của mọi người


0

Tôi đã gặp lỗi tương tự khi nói rằng 1 người dùng đang sử dụng cơ sở dữ liệu, tôi nhận ra đó là TÔI! Tôi tắt máy chủ rails của mình và sau đó thực hiện lệnh rake: drop và nó hoạt động!


0

Sau khi khởi động lại máy chủ hoặc máy tính, vui lòng thử lại.

Nó có thể là giải pháp đơn giản.


0

Giải pháp

Tập lệnh bash

ENV=development

# restart postgresql
brew services restart postgresql

# get name of the db from rails app
RAILS_CONSOLE_COMMAND="bundle exec rails c -e $ENV"
DB_NAME=$(echo 'ActiveRecord::Base.connection_config[:database]' | $RAILS_CONSOLE_COMMAND | tail -2 | tr -d '\"')

# delete all connections to $DB_NAME
for pid in $(ps -ef | grep $DB_NAME | awk {'print$2'})
do
   kill -9 $pid
done

# drop db
DISABLE_DATABASE_ENVIRONMENT_CHECK=1 RAILS_ENV=$ENV bundle exec rails db:drop:_unsafe
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.