使用 Postgres hstore 的竞争条件
Race condition using Postgres hstore
我有一个 table,其中有一个 hstore
db/schema.rb
create_table "requests", force: true do |t|
t.hstore "parameters"
end
有些记录有字段 parameters["company_id"]
但不是全部。
我需要做的是确保只有一个 Request
对象是用给定的 parameters["company_id"]
创建的。可能有多次尝试同时保存记录 - 因此是竞争条件。
我正在 table 中的 hstore
中寻找唯一的 company_id
值。
我发现我可以 运行 一个事务来锁定数据库并检查给定 parameters["company_id"]
的请求是否存在 如果不创建它则结束。如果 company_id
是 Request
模型上的一个简单字段,我可以这样做:
Request.transaction do
if Request.find_by(company_id: *id* )
log_duplication_attempt_and_quit
else
create_new_record
log_successful_creation
end
end
很遗憾,它是 hstore
,我无法更改它。使用 hstore
实现此目标的最佳方法是什么?
我正在快速查找内容,因为 table 中有很多记录。
纯 SQL 查询是可以的 - 不幸的是我没有足够的 SQL 背景来弄清楚我自己。
可以将其编入索引以提高性能吗?
示例:
a = Request.new(parameters: {company_id: 567, name: "John"})
b = Request.new(parameters: {name: "Doesn't have company_id in the hstore"})
c = Request.new(parameters: {company_id: 567, name: "Galt"})
a.save // valid success
b.save // valid success even if company_id hasn't been provided
c.save // not valid Request with company_id 567 already in the table
即使使用普通列,您的想法也不会针对并发访问进行保存。两个事务可能 both 看到该值同时不存在并且 both 尝试插入。
显然,有一个 专用的 company_id
列 会更干净,然后一个简单的 UNIQUE 约束就可以完成这项工作:
ALTER TABLE requests ADD CONSTRAINT requests_company_id_uni UNIQUE (company_id);
这样你就有了一个索引自动:
您甚至可以将该列作为外键引用...
使用 设置,您 拥有 ,您仍然可以使用 functional UNIQUE index:
CREATE UNIQUE INDEX requests_parameters_company_id_uni
ON requests ((parameters->'company_id')); -- all parentheses required
两种变体都允许多个 NULL 值,通常允许没有 'company_id'
键的条目。您甚至可以将其设为 partial, functional UNIQUE index 以从索引中排除不相关的行(使索引更小):
CREATE UNIQUE INDEX requests_parameters_company_id_uni
ON requests ((parameters->'company_id'))
WHERE (parameters->'company_id') IS NOT NULL;
只有当你有很多没有 company_id
时才有用。
相关:
- Composite PRIMARY KEY enforces NOT NULL constraints on involved columns
无论哪种方式,Postgres 现在都会处理剩下的事情。任何事务试图插入一个已经存在的 company_id
的行(一种方式或另一种方式)都会引发唯一违规的异常并回滚整个事务。始终保证唯一性。
如果您想记录被拒绝为重复项的条目,您可以将 INSERT 封装在服务器端函数中,捕获唯一违规并改为写入日志 table:
您会在 SO with this search 上找到示例。
我有一个 table,其中有一个 hstore
db/schema.rb
create_table "requests", force: true do |t|
t.hstore "parameters"
end
有些记录有字段 parameters["company_id"]
但不是全部。
我需要做的是确保只有一个 Request
对象是用给定的 parameters["company_id"]
创建的。可能有多次尝试同时保存记录 - 因此是竞争条件。
我正在 table 中的 hstore
中寻找唯一的 company_id
值。
我发现我可以 运行 一个事务来锁定数据库并检查给定 parameters["company_id"]
的请求是否存在 如果不创建它则结束。如果 company_id
是 Request
模型上的一个简单字段,我可以这样做:
Request.transaction do
if Request.find_by(company_id: *id* )
log_duplication_attempt_and_quit
else
create_new_record
log_successful_creation
end
end
很遗憾,它是 hstore
,我无法更改它。使用 hstore
实现此目标的最佳方法是什么?
我正在快速查找内容,因为 table 中有很多记录。 纯 SQL 查询是可以的 - 不幸的是我没有足够的 SQL 背景来弄清楚我自己。 可以将其编入索引以提高性能吗?
示例:
a = Request.new(parameters: {company_id: 567, name: "John"})
b = Request.new(parameters: {name: "Doesn't have company_id in the hstore"})
c = Request.new(parameters: {company_id: 567, name: "Galt"})
a.save // valid success
b.save // valid success even if company_id hasn't been provided
c.save // not valid Request with company_id 567 already in the table
即使使用普通列,您的想法也不会针对并发访问进行保存。两个事务可能 both 看到该值同时不存在并且 both 尝试插入。
显然,有一个 专用的 company_id
列 会更干净,然后一个简单的 UNIQUE 约束就可以完成这项工作:
ALTER TABLE requests ADD CONSTRAINT requests_company_id_uni UNIQUE (company_id);
这样你就有了一个索引自动:
您甚至可以将该列作为外键引用...
使用 设置,您 拥有 ,您仍然可以使用 functional UNIQUE index:
CREATE UNIQUE INDEX requests_parameters_company_id_uni
ON requests ((parameters->'company_id')); -- all parentheses required
两种变体都允许多个 NULL 值,通常允许没有 'company_id'
键的条目。您甚至可以将其设为 partial, functional UNIQUE index 以从索引中排除不相关的行(使索引更小):
CREATE UNIQUE INDEX requests_parameters_company_id_uni
ON requests ((parameters->'company_id'))
WHERE (parameters->'company_id') IS NOT NULL;
只有当你有很多没有 company_id
时才有用。
相关:
- Composite PRIMARY KEY enforces NOT NULL constraints on involved columns
无论哪种方式,Postgres 现在都会处理剩下的事情。任何事务试图插入一个已经存在的 company_id
的行(一种方式或另一种方式)都会引发唯一违规的异常并回滚整个事务。始终保证唯一性。
如果您想记录被拒绝为重复项的条目,您可以将 INSERT 封装在服务器端函数中,捕获唯一违规并改为写入日志 table:
您会在 SO with this search 上找到示例。