关于ruby on rails:使用zeus测试黄瓜和RSpec:其他用户正在访问Postgres

Cucumber and RSpec testing with zeus: Postgres is being accessed by other users

在我的Rails 3.2.13应用程序中,我正在使用Zeus。 在测试环境中我使用PostgreSQL。 当我运行Cucumber然后运行RSpec(或其他方式)时,10次中有9次我收到消息:

1
2
3
4
5
6
PG::Error: ERROR:  DATABASE"bp_test" IS being accessed BY other users
DETAIL:  There are 1 other SESSION(s) USING the DATABASE.
: DROP DATABASE IF EXISTS"bp_test"

Tasks: TOP => db:test:LOAD => db:test:purge
(See FULL trace BY running task WITH --trace)

如此处所述,尝试杀死数据库连接以使其再次工作需要一个完整的非确定性马戏团。 但这并不总是有效,也是一个很大的麻烦。 必须有一个更好的解决方案。 有人知道吗?


受此答案的启发,我们创建了以下database.rake文件。 原始答案仅适用于PostgreSQL 9.1,这个修改后也适用于PostgreSQL 9.2。 机制不是最漂亮的:当9.1命令失败时,它只是执行9.2命令。 但最重要的是:它有效!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#{Rails.root}/lib/tasks/DATABASES.rake
# monkey patch ActiveRecord TO avoid There are n other SESSION(s) USING the DATABASE.
def drop_database(config)
  CASE config['adapter']
  WHEN /mysql/
    ActiveRecord::Base.establish_connection(config)
    ActiveRecord::Base.connection.drop_database config['database']
  WHEN /sqlite/
    require 'pathname'
    path = Pathname.new(config['database'])
    file = path.absolute? ? path.to_s : File.join(Rails.root, path)

    FileUtils.rm(file)
  WHEN /postgresql/
    BEGIN
      ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
      ActiveRecord::Base.connection.select_all("select * from pg_stat_activity order by procpid;").each do |x|
        IF config['database'] == x['datname'] && x['current_query'] =~ /<IDLE>/
          ActiveRecord::Base.connection.execute("select pg_terminate_backend(#{x['procpid']})")
        END
      END
      ActiveRecord::Base.connection.drop_database config['database']
    rescue # IN PG 9.2 COLUMN procpid was renamed pid AND the query STATUS IS checked NOT USING 'current_query' but USING 'state'
      ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
      ActiveRecord::Base.connection.select_all("select * from pg_stat_activity order by pid;").each do |x|
        IF config['database'] == x['datname'] && x['state'] =~ /idle/
          ActiveRecord::Base.connection.execute("select pg_terminate_backend(#{x['pid']})")
        END
      END
      ActiveRecord::Base.connection.drop_database config['database']
    END
  END
END

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
namespace :db do

  DESC 'Clear the database'
  task :clear_db => :environment do |t,args|
    ActiveRecord::Base.establish_connection
    ActiveRecord::Base.connection.tables.each do |TABLE|
      NEXT IF TABLE == 'schema_migrations'
      ActiveRecord::Base.connection.execute("TRUNCATE #{table}")
    END
  END

  DESC 'Delete all tables (but not the database)'
  task :drop_schema => :environment do |t,args|
    ActiveRecord::Base.establish_connection
    ActiveRecord::Base.connection.execute("DROP SCHEMA public CASCADE")
    ActiveRecord::Base.connection.execute("CREATE SCHEMA public")
    ActiveRecord::Base.connection.execute("GRANT ALL ON SCHEMA public TO postgres")
    ActiveRecord::Base.connection.execute("GRANT ALL ON SCHEMA public TO public")
    ActiveRecord::Base.connection.execute("COMMENT ON SCHEMA public IS 'standard public schema'")
  END

  DESC 'Recreate the database and seed'
  task :redo_db => :environment do |t,args|
    # Executes the dependencies, but ONLY once
    Rake::Task["db:drop_schema"].invoke
    Rake::Task["db:migrate"].invoke
    Rake::Task["db:migrate:status"].invoke
    Rake::Task["db:structure:dump"].invoke
    Rake::Task["db:seed"].invoke
  END

END