【问题标题】:Race condition using Postgres hstore使用 Postgres hstore 的竞争条件
【发布时间】:2015-12-20 20:01:27
【问题描述】:

我有一张表,其中有许多其他字段之一hstore

db/schema.rb

create_table "requests", force: true do |t|
  t.hstore "parameters"
end

一些记录有一个字段parameters["company_id"],但不是全部。

我需要做的是确保只使用给定的parameters["company_id"] 创建一个Request 对象。可能有多次尝试同时保存记录 - 因此是竞争条件。

我正在整个表格的 hstore 中寻找唯一的 company_id 值。

我发现我可以运行一个事务来锁定数据库并检查给定parameters["company_id"] 的请求是否存在,如果不创建它。如果company_idRequest 模型上的一个简单字段,我可以这样做:

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 实现这一目标的最佳方法是什么?

我正在寻找快速的东西,因为表中有很多记录。 纯 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

【问题讨论】:

    标签: ruby-on-rails postgresql database-design hstore unique-index


    【解决方案1】:

    即使使用普通列,您的想法也不会针对并发访问进行保存。两个事务可能同时看到该值不存在,并且尝试插入。

    显然,有一个专用的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 时才有用。

    相关:

    SQL Fiddle.

    无论哪种方式,Postgres 现在都会处理其余的事情。任何尝试插入已经存在的company_id 行的事务(一种或另一种方式)都会引发唯一违规的异常并回滚整个事务。始终保证唯一性。

    如果您想记录被拒绝为重复的条目,您可以将 INSERT 封装在服务器端函数中,捕获唯一违规并改为写入日志表:

    您可以在 SO with this search 上找到示例。

    【讨论】:

    • 您能否详细说明一下如何创建 UNIQUE 约束?正如我所提到的,我的 SQL 背景非常薄弱:(
    • @Kocur4d:我想我现在更好地理解了您的要求:您希望在整个表格的 hstore 列中使用唯一的 company_id 键。
    • 我想要整个表的 hstore 中的 uniq company_ids 值
    • @Kocur4d:您会因重复尝试而获得独特的违规行为,如果您愿意,可以将其记录下来。 (无论采用标准日志配置,它都会在数据库日志中。)
    • 谢谢你,伙计——这是我可以要求的更多!谢谢!
    猜你喜欢
    • 2021-04-01
    • 2011-12-12
    • 2019-02-10
    • 2015-04-28
    • 1970-01-01
    • 1970-01-01
    • 2021-10-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多