Skip to content

Single-run lock per ingestion job - #93

Open
koenvo wants to merge 3 commits into
mainfrom
feat/single-run-lock
Open

Single-run lock per ingestion job#93
koenvo wants to merge 3 commits into
mainfrom
feat/single-run-lock

Conversation

@koenvo

@koenvo koenvo commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

In lots of cases ingestify is scheduled using a cron. This works fine when jobs finish before the next starts. In case an api is slow this might not be the case and things get messy.

This PR tries to solve this by using locks per IngestionJob. Different IngestionJobs can run in parallel (from different sources for example). But the same job from the same Source cannot run in parallel.

Enforce (best-effort) that one (source, dataset_type, selector) job never runs
in two processes at once -- a framework invariant, not a config knob, since
concurrent same-job execution is undefined. Plan: a Postgres session-level
advisory lock in the metadata store, held on a dedicated connection so it is
released the instant the process ends; no-op on stores without one. Taken in
IngestionJob.execute before the RUNNING summary.

Design: docs/design/single-run-lock.md
Tests: ingestify/tests/test_run_lock.py specify DatasetRepository.acquire_run_lock
(currently red: method not implemented yet).

Claude-Session: https://claude.ai/code/session_01B5EfLJqoafjW1FhvkxGSmg
@koenvo
koenvo force-pushed the feat/single-run-lock branch from 2abb0f1 to 1bcd65f Compare August 7, 2026 08:41
- DatasetRepository.acquire_run_lock: Postgres pg_try_advisory_lock, MySQL GET_LOCK,
  on a dedicated AUTOCOMMIT connection held for the run (released the instant the
  connection/process ends). Concrete no-op default for stores without a session lock.
- DatasetStore delegates it; IngestionJob.execute takes the lock before the RUNNING
  summary and, if another run holds it, records a SKIPPED summary and returns without
  running -- releasing the lock in a finally around the existing body.
- test_run_lock.py: repository exclusivity/no-op unit tests plus a two-instance
  integration test (two engines over one DB; instance 1 holds the lock while blocked,
  instance 2 must skip -- its source is never invoked). Runs on Postgres/MySQL.

Claude-Session: https://claude.ai/code/session_01B5EfLJqoafjW1FhvkxGSmg
@koenvo koenvo changed the title Design + failing test for single-run lock per ingestion job Single-run lock per ingestion job Aug 7, 2026
The threaded version hung on MySQL/Postgres: the background thread left an
idle-in-transaction connection its scoped_session could not clean from the main
thread, so cleanup's DROP TABLE waited on it forever. Instead hold the run lock
directly on a second store connection (exactly what a second instance does),
run the other instance -> it must skip, then release and run again -> it runs.
No threads. Cleanup now disposes every pool before DROP.

Claude-Session: https://claude.ai/code/session_01B5EfLJqoafjW1FhvkxGSmg
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant