ruby - ActiveRecord Postgres 数据库未锁定 - 获取竞争条件

标签 ruby multithreading postgresql activerecord

我正在努力锁定我正在处理的 PostgreSQL 表。理想情况下,我想锁定整个表,但只要它们实际工作,个别行就可以。

我有几个并发的 ruby​​ 脚本,它们都查询 AWS 上的中央作业数据库(通过 DatabaseAccessor 类),找到一个尚未开始的作业,将状态更改为 开始并执行它。问题是,由于这些都是同时运行的,它们通常会同时找到相同的未开始工作,并开始执行它,这既浪费时间又混淆了结果。

我已经尝试了很多东西,.lock.transactionfatalistic gem,但它们似乎不是工作,至少,不是偷偷摸摸的。

我的代码如下:

class DatabaseAccessor
  require 'pg'
  require 'pry'
  require 'active_record'
  class Jobs < ActiveRecord::Base
    enum status: [ :unstarted, :started, :slow, :completed]
  end

  def initialize(db_credentials)
    ActiveRecord::Base.establish_connection(
      adapter:  db_credentials[:adapter],
      database: db_credentials[:database],
      username: db_credentials[:username],
      password: db_credentials[:password],
      host:     db_credentials[:host]
    )
  end

  def find_unstarted_job
    job = Jobs.where(status: 0).limit(1)
    job.started!
    job
  end
end

有人有什么建议吗?

编辑:看来 LOCK TABLE jobs IN ACCESS EXCLUSIVE MODE; 是执行此操作的方法 - 但是,我正在努力返回结果更新后。 RETURNING * 将在更新后返回结果,但不在事务内。

最佳答案

已解决!

所以这里的关键是在 Postgres 中锁定。有几种不同的表级锁,详解here .

这里有三个因素来做出决定:

  1. 读取不是线程安全的。读取同一条记录的两个线程将导致该作业同时运行多次。
  2. 记录仅更新一次(标记为已完成)和创建,而不是从初始读取和更新开始。创建新记录的脚本不会读取该表。
  3. 阅读的频率各不相同。等待解锁并不重要。

考虑到这些因素,如果有一个仍然允许写入的读锁,这是可以接受的,但是,没有,所以 ACCESS EXCLUSIVE 是我们最好的选择。

鉴于此,我们如何处理锁定?翻阅 ActiveRecord 文档没有提及它。

值得庆幸的是,存在其他处理 PostgreSQL 的方法,即 ruby-pg gem。稍后玩 SQL,测试锁定,我得到以下方法:

def converter
  result_hash = {}
  conn = PG::Connection.open(:dbname => 'my_db')
  conn.exec("BEGIN WORK;
      LOCK TABLE jobs IN ACCESS EXCLUSIVE MODE;")
  conn.exec("UPDATE jobs SET status = 1 WHERE id = 
      (SELECT id FROM jobs WHERE status = 0 ORDER BY ID LIMIT 1)
      RETURNING *;") do |result|
          result.each { |row| result_hash = row }
      end
  conn.exec("COMMIT WORK;")
  result_hash.transform_keys!(&:to_sym)
end

这将导致:

  • 如果没有 status0

    的作业,则输出空哈希值
  • 如果找到并更新了一个符号化散列的输出

  • 如果数据库当前处于锁定状态,则休眠,解锁后返回上述内容。

该表将保持锁定状态,直到 COMMIT WORK 语句。

顺便说一句,我希望有一种更简洁的方法将结果转换为散列。如果有人有任何建议,请在评论中告诉我! :)

关于ruby - ActiveRecord Postgres 数据库未锁定 - 获取竞争条件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50308730/

相关文章:

mysql - 在一个 Controller 中调用两个相互依赖的模型进行 View

multithreading - 在其他线程内部生成线程时借用的问题

sql - 如何使用 Unicode 字母声明 SQL INSERT 语句

postgresql - 我如何在 PostgreSQL psql 命令行工具中评论 special\commands?

ruby-on-rails - 在 Rails 中,数据库锁定是否会确保只有进程一次检查属性值而不会成为巨大的瓶颈?

ruby - 在 Windows 系统上安装 'wdm' gem 时出错

ruby - 如何从命令行调用旧版本的 gem?

ruby - 为什么 Ruby 输出 [[ :rest]] when the parameter method is called on String objects?

c - 实现临界区

java - JTableModelListener.tableChanged() 线程安全吗?