From 465a58a1c98932b1a1e1df03b4d220a062fa753e Mon Sep 17 00:00:00 2001 From: erio Date: Fri, 6 Mar 2026 20:07:02 +0800 Subject: [PATCH] fix: replace CTE with inline UPDATE to prevent lost update on period reset CTE snapshot in READ COMMITTED can cause concurrent requests during period reset to lose one increment. Inline CASE in single UPDATE ensures PostgreSQL re-evaluates all expressions with latest row data. Co-Authored-By: Claude Opus 4.6 --- backend/internal/repository/account_repo.go | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/backend/internal/repository/account_repo.go b/backend/internal/repository/account_repo.go index 97aa0f11c4..6fda0c182a 100644 --- a/backend/internal/repository/account_repo.go +++ b/backend/internal/repository/account_repo.go @@ -1697,27 +1697,23 @@ func (r *accountRepository) IncrementQuotaUsed(ctx context.Context, id int64, am COALESCE((extra->>'quota_limit')::numeric, 0)`, amount, id) } else { - // 周期制:CTE 原子检查过期 → 重置 → 递增 + // 周期制:单条 UPDATE 内联过期判断,避免 CTE snapshot 在 READ COMMITTED 下的 lost update rows, err = r.sql.QueryContext(ctx, - `WITH reset_check AS ( - SELECT id, extra, - CASE WHEN COALESCE((extra->>'quota_period_start')::timestamptz, '1970-01-01'::timestamptz) - + $3::interval <= NOW() - THEN true ELSE false END AS needs_reset - FROM accounts WHERE id = $2 AND deleted_at IS NULL - ) - UPDATE accounts SET extra = jsonb_set( + `UPDATE accounts SET extra = jsonb_set( jsonb_set( COALESCE(extra, '{}'::jsonb), '{quota_used}', to_jsonb( - CASE WHEN (SELECT needs_reset FROM reset_check) THEN $1 + CASE WHEN COALESCE((extra->>'quota_period_start')::timestamptz, '1970-01-01'::timestamptz) + + $3::interval <= NOW() + THEN $1 ELSE COALESCE((extra->>'quota_used')::numeric, 0) + $1 END ) ), '{quota_period_start}', to_jsonb( - CASE WHEN (SELECT needs_reset FROM reset_check) + CASE WHEN COALESCE((extra->>'quota_period_start')::timestamptz, '1970-01-01'::timestamptz) + + $3::interval <= NOW() THEN to_char(NOW() AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.US"Z"') ELSE COALESCE(extra->>'quota_period_start', to_char(NOW() AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.US"Z"')) END )