我有这张桌子:
create_table :api_usages do |t|
t.references :api_key, null: false, foreign_key: true
t.date :billing_date, null: false
t.integer :request_count, null: false
t.timestamps
end
发出 API 请求时,我想增加(如果是新行则设置为 1)具有匹配
api_key
和 billing_date
的行。如果用户快速连续发出两个 API 请求,我想确保不存在竞争条件。
这足够了吗?
api_usage = ApiUsage.lock.find_or_initialize_by(api_key_id: api_key_id, billing_date: billing_date)
api_usage.request_count ||= 0
api_usage.request_count += 1
api_usage.save!
# The lock is released after save! completes, as part of the implicit transaction's lifecycle.
或者我应该打包交易吗?
ApiUsage.transaction do
api_usage = ApiUsage.lock.find_or_initialize_by(api_key_id: api_key_id, billing_date: billing_date)
api_usage.request_count ||= 0
api_usage.request_count += 1
api_usage.save!
# The lock is held until the end of the transaction block.
end
# The lock is released after the transaction block completes.
这是我尝试通过更新插入来解决这个问题,没有锁或事务:
ApiUsage.upsert(
{
api_key_id: api_key_id, billing_date: billing_date, request_count: 1,
created_at: Time.current, updated_at: Time.current },
unique_by: { columns: %i[api_key_id billing_date] },
on_duplicate: Arel.sql('request_count = api_usages.request_count + 1, updated_at = EXCLUDED.updated_at')
}
)