From 0e0939bb321e480fccabcb8d3292ba659af80a0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Wed, 24 Jun 2026 18:06:05 +0400 Subject: [PATCH] =?UTF-8?q?v1.0.176:=20spec=5Fevents=20=E2=80=94=20FOR=20U?= =?UTF-8?q?PDATE=20+=20transaction=20for=20seq=20race?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/db/spec_events.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/deploy/db/spec_events.py b/deploy/db/spec_events.py index 8544724..b9e765a 100644 --- a/deploy/db/spec_events.py +++ b/deploy/db/spec_events.py @@ -1,6 +1,6 @@ """spec_events — event sourcing: apply ops, reset contract.""" import json, uuid -from .connection import query, execute +from .connection import query, execute, get_pool def reset(contract_id): @@ -10,11 +10,25 @@ def reset(contract_id): def get_next_seq(contract_id): - rows = query( - "SELECT COALESCE(MAX(seq), 0) + 1 AS n FROM spec_events WHERE contract_id = %s", - (contract_id,), - ) - return rows[0]["n"] if rows else 1 + """Get next sequence number with row lock to prevent race conditions.""" + pool = get_pool() + conn = pool.getconn() + try: + conn.autocommit = False + with conn.cursor() as cur: + cur.execute( + "SELECT COALESCE(MAX(seq), 0) + 1 FROM spec_events WHERE contract_id = %s FOR UPDATE", + (contract_id,), + ) + seq = cur.fetchone()[0] + conn.commit() + return seq + except Exception: + conn.rollback() + raise + finally: + conn.autocommit = True + pool.putconn(conn) def apply_ops(contract_id, supplement_id, document_id, ops, prompt_id, raw_llm_response):