v1.0.176: spec_events — FOR UPDATE + transaction for seq race
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
"""spec_events — event sourcing: apply ops, reset contract."""
|
"""spec_events — event sourcing: apply ops, reset contract."""
|
||||||
import json, uuid
|
import json, uuid
|
||||||
from .connection import query, execute
|
from .connection import query, execute, get_pool
|
||||||
|
|
||||||
|
|
||||||
def reset(contract_id):
|
def reset(contract_id):
|
||||||
@@ -10,11 +10,25 @@ def reset(contract_id):
|
|||||||
|
|
||||||
|
|
||||||
def get_next_seq(contract_id):
|
def get_next_seq(contract_id):
|
||||||
rows = query(
|
"""Get next sequence number with row lock to prevent race conditions."""
|
||||||
"SELECT COALESCE(MAX(seq), 0) + 1 AS n FROM spec_events WHERE contract_id = %s",
|
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,),
|
(contract_id,),
|
||||||
)
|
)
|
||||||
return rows[0]["n"] if rows else 1
|
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):
|
def apply_ops(contract_id, supplement_id, document_id, ops, prompt_id, raw_llm_response):
|
||||||
|
|||||||
Reference in New Issue
Block a user