ruby - ActiveRecord Postgres 数据库未锁定 - 获取竞争条件
问题描述
我正在努力锁定我正在处理的 PostgreSQL 表。理想情况下,我想锁定整个表,但只要它们实际工作,个别行就可以了。
我有几个并发的 ruby 脚本,它们都查询 AWS 上的中央作业数据库(通过一个DatabaseAccessor
类),找到一个尚未开始的作业,将状态更改为started
并执行它。问题是,由于这些都是同时运行的,它们通常会同时找到相同的未开始工作,并开始执行,浪费时间并混淆结果。
我已经尝试了很多东西.lock
,,,宝石.transaction
,fatalistic
但它们似乎没有工作,至少,没有被撬开。
我的代码如下:
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。有几种不同的表级锁,在此处详述。
在这里做出决定有三个因素:
- 读取不是线程安全的。读取同一记录的两个线程将导致该作业一次运行多次。
- 记录仅更新一次(标记为已完成)并创建,而不是初始读取和更新开始。创建新记录的脚本不会读取该表。
- 阅读频率不同。等待解锁并不重要。
考虑到这些因素,如果有一个仍然允许写入的读锁,这是可以接受的,但是,没有,所以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
这将导致:
如果没有具有 a
status
的作业,则输出空哈希0
如果找到并更新了符号化哈希的输出
如果数据库当前被锁定,则休眠,然后在解锁后返回上述内容。
该表将保持锁定状态,直到该COMMIT WORK
语句。
顺便说一句,我希望有一种更简洁的方法将结果转换为哈希。如果有人有任何建议,请在评论中告诉我!:)
推荐阅读
- html - 当容器滚动到底部时元素向上扩展
- python-3.x - 如何生成频率为周二至周六的熊猫日期范围?
- javascript - 当用户在提示中输入他的名字时,我的 JavaScript If-Statement 代码没有给出正确的结果
- jenkins - 想用 jenkins 参数参数化 ansible playbook 中的值
- google-play - 为什么我在 Play 商店中有效的最新 Google App 版本在第一次更新时不适用于现有用户
- javascript - 如何让 DataTables 搜索与 AJAX 结果一起正常工作?
- javascript - 为什么我的 API 调用函数返回一个未定义的对象?
- python - 如何在没有 python 控制台的情况下运行 tkinter 应用程序
- python - 如何获取与用户模型一对一相关的对象
- google-bigquery - Google Data Studio - BigQuery 数据源列表,如何跟踪用作数据源的 bigquery 表