使用 Rails 和 Postgres 获取原子计数器(增量)的值

Get value of atomic counter (increment) with Rails and Postgres

我需要自动增加模型计数器并使用它的新值(由 Sidekiq 作业处理)。

目前,我使用

Group.increment_counter :tasks_count, @task.id

在我的模型中自动递增计数器。

但是我还需要它的新值来发送通知,如果计数器有,例如值 50。有任何想法吗?锁定 table/row 还是有更简单的方法?

编辑/解决方案

基于mu is too short的答案和Rails的update_counters方法,我实现了一个实例方法(用PostgreSQL测试)。

def self.increment_counter_and_return_value(counter_name, id)
  quoted_column = connection.quote_column_name(counter_name)
  quoted_table = connection.quote_table_name(table_name)
  quoted_primary_key = connection.quote_column_name(primary_key)
  quoted_primary_key_value = connection.quote(id)

  sql = "UPDATE #{quoted_table} SET #{quoted_column} = COALESCE(#{quoted_column}, 0) + 1 WHERE #{quoted_table}.#{quoted_primary_key} = #{quoted_primary_key_value} RETURNING #{quoted_column}"
  connection.select_value(sql).to_i
end

像这样使用它:

Group.increment_counter_and_return_value(:tasks_count, @task.id)

它使用 RETURNING 在同一查询中获取新值。

您的 Group.increment_counter 调用将 SQL 像这样发送到数据库:

update groups
set tasks_count = coalesce(tasks_counter, 0) + 1
where id = X

其中 X@task.id。 SQL 获取新 tasks_counter 值的方法是包含一个 RETURNING 子句:

update groups
set tasks_count = coalesce(tasks_counter, 0) + 1
where id = X
returning tasks_count

我不知道有什么方便的 Railsy 方法可以将 SQL 到数据库。通常的 Rails 方法是要么做一堆锁定并重新加载 @task 要么跳过锁定并希望最好:

Group.increment_counter :tasks_count, @task.id
@task.reload
# and now look at @task.tasks_count to get the new value

不过您可以像这样使用 RETURNING:

new_count = Group.connection.execute(%Q{
    update groups
    set tasks_count = coalesce(tasks_counter, 0) + 1
    where id = #{Group.connection.quote(@task.id)}
    returning tasks_count
}).first['tasks_count'].to_i

你可能想把那些乱七八糟的东西隐藏在 Group 的方法后面,这样你就可以这样说:

n = Group.increment_tasks_count_for(@task)
# or
n = @task.increment_tasks_count