Prevent DELETE/UPSERT race for the same URI
This commit is contained in:
parent
0114653522
commit
922d1d567d
2 changed files with 31 additions and 5 deletions
|
|
@ -22,12 +22,15 @@ CREATE TABLE IF NOT EXISTS jobs (
|
|||
)
|
||||
"""
|
||||
|
||||
# Partial unique index: a (source_id, uri, op) triple can only have one live
|
||||
# job (queued or claimed) at a time. Once succeeded or dead, the row no
|
||||
# longer satisfies the WHERE clause and a re-enqueue is allowed.
|
||||
# Partial unique index: a (source_id, uri) pair can only have one live job
|
||||
# (queued or claimed) at a time, regardless of op. Live UPSERT and DELETE
|
||||
# for the same URI can't both exist — preventing a DELETE worker from
|
||||
# removing a document a sibling UPSERT just ingested. Once succeeded or
|
||||
# dead, the row no longer satisfies the WHERE clause and a re-enqueue is
|
||||
# allowed.
|
||||
JOBS_LIVE_INDEX = """
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_jobs_live
|
||||
ON jobs(source_id, uri, op)
|
||||
ON jobs(source_id, uri)
|
||||
WHERE status IN ('queued', 'claimed')
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -175,11 +175,34 @@ async def test_has_pending_is_per_source(jobs):
|
|||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_enqueue_different_ops_coexist(jobs):
|
||||
async def test_enqueue_drops_delete_when_upsert_is_live(jobs):
|
||||
"""Stops a DELETE worker from removing a document a sibling UPSERT
|
||||
just ingested."""
|
||||
upsert = await jobs.enqueue("s", "u", JobOp.UPSERT)
|
||||
delete = await jobs.enqueue("s", "u", JobOp.DELETE)
|
||||
assert upsert is not None
|
||||
assert delete is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_enqueue_drops_upsert_when_delete_is_live(jobs):
|
||||
"""Symmetric: live DELETE for a URI blocks a fresh UPSERT for the
|
||||
same URI. The next sweep after DELETE completes will re-emit the
|
||||
UPSERT if the file is back."""
|
||||
delete = await jobs.enqueue("s", "u", JobOp.DELETE)
|
||||
upsert = await jobs.enqueue("s", "u", JobOp.UPSERT)
|
||||
assert delete is not None
|
||||
assert upsert is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_enqueue_different_uris_independent_of_op(jobs):
|
||||
"""Uniqueness is per-(source_id, uri), not per-(source_id, uri, op).
|
||||
Different URIs can be queued regardless of which op each one is."""
|
||||
a = await jobs.enqueue("s", "u-a", JobOp.UPSERT)
|
||||
b = await jobs.enqueue("s", "u-b", JobOp.DELETE)
|
||||
assert a is not None
|
||||
assert b is not None
|
||||
|
||||
|
||||
# --- claim_next ---
|
||||
|
|
|
|||
Loading…
Reference in a new issue