Changelog

(unreleased)

  • Ci: bump checkout/setup-python to v6 (Node 24) [tobymao]

    Clears the Node.js 20 deprecation warning; v2 actions run on Node 20 which GitHub forces to Node 24 starting June 2026.

  • Harden test_start timing against CI load. [tobymao]

    Wait for terminal status via refresh(0) for the error job (FAILED), and use a longer-running sleeper plus more generous sleeps for the active/re-queue checks so they don’t race the worker under CI load.

  • Flaky test_start by waiting for in-flight job re-queue. [tobymao]

    The in-flight job is re-queued in the worker’s CancelledError handler, which stop() only awaits up to cancellation_hard_deadline_s. For the HTTP proxy that re-queue crosses the network, so the new QUEUED status can land just after the worker task returns, causing test_start to read ACTIVE under CI load. Sleep briefly before refreshing.

v0.26.4 (2026-05-21)

  • V0.26.4. [tobymao]

  • KeyError on concurrent same-key job attempts in job_task_contexts. [Timothy Redaelli]

    Job.hash is derived from .key, so two in-flight attempts of the same-key job (e.g. a sweep-driven retry of a job whose previous attempt is still winding down) share one slot in Worker.job_task_contexts. Whichever attempt’s finally pops the slot first leaves the other to raise KeyError at the completion check:

    File ".../saq/worker.py", line 374, in process
        if self.job_task_contexts[job]["aborted"] is None:
    KeyError: Job<... key: chat:1, attempts: 2, error: cancelled, status: active>
    

    Keep the dict keyed by Job (i.e. by .key), but hold a local reference to the JobTaskContext inside process() and read “aborted” through it. In the finally, only remove the slot if it still holds our ctx – a successor attempt’s slot is preserved. abort() is unchanged.

    Adds a regression test that reproduces the race by running two process() coroutines with same-key jobs and asserting both complete.

  • Allow redis-py 7.x (bump cap from <7.0 to <8.0) [taaacse4]

    redis-py 7.0.0 breaking changes (type annotations, RLock, removed parse_list_to_dict) do not affect SAQ’s usage of the redis library. SAQ uses redis.asyncio, pipelines, basic commands, and pub/sub — all unchanged in 7.x.

    Same analysis as the prior bump from <6.0 to <7.0 (cc8bef9).

    Closes #291

v0.26.3 (2026-03-04)

  • V0.26.3. [tobymao]

  • Pandas serde closes #287. [tobymao]

v0.26.2 (2026-03-04)

  • V0.26.2. [tobymao]

  • Again. [tobymao]

  • Fix again. [tobymao]

  • 3.9. [tobymao]

  • Support newer psycopg. [tobymao]

v0.26.1 (2026-01-08)

  • V0.26.1. [tobymao]

  • Remove signal handlers after stop. [Vikash]

  • Allow redis package version up to 6.x. [Malthe Jørgensen]

    Breaking changes and deprecations in 6.0.0 don’t affect how SAQ uses the redis library, so we can allow its use. Similarly the later available redis 6.x versions: 6.1.0, 6.1.1, 6.2.0, 6.3.0, 6.4.0 adhere to semantic versioning (double checked, because packages don’t always follow semantic versioning) and don’t have breaking changes nor deprecations.

    redis 6.x supports Python 3.9+ similarly to SAQ. Python 3.8 support was dropped in 6.2.0.

    Reference:

    • https://github.com/redis/redis-py/releases/tag/v6.0.0

    • https://github.com/redis/redis-py/tags

  • Style fix. [Simone Caldana]

  • Record worker_id in job metadata once a job is picked up. [Simone Caldana]

  • Try again. [tobymao]

  • Fix tests. [tobymao]

v0.26.0 (2025-10-14)

  • V0.26.0. [tobymao]

  • Global timeout for map function closes #270. [tobymao]

  • Chore: Avoid high cardinality job id in logger message. [ditsuke]

  • Do wait to cancel task in case of timeout. [ditsuke]

  • Avoid race in setting task status b/w process and abort. [ditsuke]

  • Tests: Small wait to let process() end. [ditsuke]

  • Chore: None for cancellation timeout (noop) [ditsuke]

  • Tests: Assert that .abort blocks until the task stops. [ditsuke]

  • Chore: Log on failure to cancel in time. [ditsuke]

  • Chore: Remove job-level completion allowance. [ditsuke]

  • Feat: Graceful shutdown and cancellation controls. [ditsuke]

  • Fix(tests): Bump wait for task to get picked. [ditsuke]

  • Test: Add testcase for shutdown hook order. [ditsuke]

  • Refactor: Remove AsyncExitStack. [ditsuke]

  • Avoid race in stop() [ditsuke]

  • Allow tasks to stop before cleanup. [ditsuke]

  • Py39 type compat. [ditsuke]

  • Type qualifiers. [ditsuke]

  • Chore: Remove redundant type alias. [ditsuke]

  • Chore: Improve worker context typing. [ditsuke]

  • Style: add generic for context. [euri10]

  • Docs: Complete and expand documentation for Tasks section. [Rakesh Bhatia]

  • Refactor: extract runner from main function. [Ivan Koldakov]

    Move the main execution logic into a separate run() function in saq.runner. It allows running the application programmatically without modifying sys.argv.

  • Set default port value. [Ivan Koldakov]

    Avoid passing None to start() as it violates the signature.

  • Remove all signal handlers for windows as they don’t work. [tobymao]

v0.25.2 (2025-07-10)

  • V0.25.2. [tobymao]

  • Feat: add optional polling to postgres. [tobymao]

  • Drop 3.8/add 3.13. [eakmanrq]

  • Chore: type ignore aiosignal 1.4.0. [eakmanrq]

v0.25.1 (2025-07-03)

  • V0.25.1. [tobymao]

  • Feat: add retry to http calls. [eakmanrq]

v0.25.0 (2025-07-01)

  • V0.25.0. [tobymao]

  • Add test. [tobymao]

  • Refactor!: remove pg locks for stability. [tobymao]

v0.24.13 (2025-06-30)

  • V0.24.13. [tobymao]

  • Handle case where connection pool closes connection. [eakmanrq]

v0.24.12 (2025-06-24)

  • V0.24.12. [tobymao]

  • Ensure there’s no race condition in between dequeue and processing for sweeper. [tobymao]

v0.24.11 (2025-06-23)

  • V0.24.11. [tobymao]

  • Formatting. [Haukur Páll]

  • Add poll_interval test. [Haukur Páll]

  • Add poll_interval example to readme. [Haukur Páll]

  • Formatting. [Haukur Páll]

  • Making the poll_interval docstring consistent betwen methods. [Haukur Páll]

  • Add poll_interval to apply, edit docstring. [Haukur Páll]

v0.24.10 (2025-06-20)

  • V0.24.10. [tobymao]

  • Ensure db status is source of truth for pg jobs. [tobymao]

v0.24.9 (2025-06-10)

  • V0.24.9. [Iaroslav Zeigerman]

  • Acquiring an advisory lock for a job as part of a predicate. [Iaroslav Zeigerman]

v0.24.8 (2025-05-29)

  • V0.24.8. [tobymao]

  • Ensure retried jobs are unlocked. [tobymao]

  • V0.24.7. [tobymao]

  • Ensure that we filter locked objects before limits so that we actually return jobs. [tobymao]

v0.24.6 (2025-05-16)

  • V0.24.6. [tobymao]