From 9d8104f7a5a3eb0d2b45506b8c0471bf6d6be278 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Mon, 31 Aug 2026 08:13:29 -0400 Subject: [PATCH] fix(embeddings): key_share alone is FOR NO KEY UPDATE, not FOR KEY SHARE (#3262) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SQLAlchemy spells Postgres's four row locks as a read/key_share pair, so `with_for_update(key_share=True)` renders FOR NO KEY UPDATE — an exclusive lock that two refreshes of the same record would fight over, and that an ordinary concurrent edit would block. The claim needs `read=True` as well to be the FOR KEY SHARE the docstring describes. Caught by the unit test that compiles the statement, which is the whole reason it asserts on the rendered lock mode rather than on behaviour that looks identical either way. --- src/scribe/services/embeddings.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/scribe/services/embeddings.py b/src/scribe/services/embeddings.py index a905b1e..bf66e49 100644 --- a/src/scribe/services/embeddings.py +++ b/src/scribe/services/embeddings.py @@ -374,7 +374,10 @@ async def _claim_parent_row(session, id_column, row_id: int, label: str) -> bool held = (await session.execute( select(id_column) .where(id_column == row_id) - .with_for_update(key_share=True, nowait=True) + # BOTH flags: SQLAlchemy spells the four Postgres row locks as a + # read/key_share pair, and key_share alone is FOR NO KEY UPDATE — + # which would make two refreshes of one record fight each other. + .with_for_update(read=True, key_share=True, nowait=True) )).scalar_one_or_none() except Exception: # LockNotAvailable: this record is being deleted right now. Not an