Skip to content

fix(schedule): skip Cluster schedules removed after scanning - #148

Draft
cw-jlu wants to merge 1 commit into
taskiq-python:mainfrom
cw-jlu:fix/cluster-schedule-delete-race
Draft

cw-jlu wants to merge 1 commit into
taskiq-python:mainfrom
cw-jlu:fix/cluster-schedule-delete-race

Conversation

@cw-jlu

@cw-jlu cw-jlu commented Nov 12, 2026 •

Copy link
Copy Markdown

Deleting a schedule after Redis SCAN returned its key but before the source's GET made RedisClusterScheduleSource.get_schedules() decode None and raise TypeError. Taskiq's actual scheduler snapshot helper catches that error and substitutes an empty list, so unrelated valid schedules from this source disappear from that refresh. Skip only a missing GET value; malformed non-missing payloads still report errors. This does not make SCAN atomic or change the indexing approach.

Fixes #147. Assignment has been requested; the issue is not assigned. This is independent of the result-client lifetime in #146 and the List-source indexing work in #121.

The regressions use two real Cluster clients and an event barrier inserted in the original SCAN iterator, between a real scanned key and the source's GET. Another client performs the actual DELETE. No keys/results are fabricated and the Redis server is not paused. They cover source/scheduler-helper entry points, surviving/all-deleted records, later re-addition, and a corrupt empty-payload control.

Actual local verification on Windows 11, Python 3.14.7, redis-py 8.0.0 and taskiq 0.13.0:

  • Final five tests copied to untouched main 8f70bc85dd6a469cae7d3b467af7dea720c92434, same interpreter/dependencies and real Redis 8.2.11 Cluster: 4 failed, 1 passed. The direct reads raise TypeError; the scheduler helper loses a live schedule or logs an error for an ordinarily empty snapshot. The corruption control passes on both baseline and patch.
  • python -m pytest -q --tb=short: 70 passed, with isolated Redis 8.2.11 standalone, three-primary Cluster and Sentinel services available.
  • python -m pytest tests/test_schedule_source.py tests/test_cluster_schedule_deletion.py -q -n 2 --tb=short: 21 passed on real Redis 7.4.12 services, including parallel test execution.
  • uv run --frozen prek run --files taskiq_redis/schedule_source.py tests/test_cluster_schedule_deletion.py: all applicable hooks passed, including repository-wide Ruff/mypy (16 files), Black, whitespace and EOF checks. git diff --check passed.

The SCAN iterator is instrumented only to control the concurrency window. This exercises real SCAN/DELETE/GET and the installed Taskiq scheduler snapshot helper, not a full scheduler daemon or OS/Python matrix. The helper recovers from errors, so the failure is a lost refresh, not a crashed process. CI and independent review remain pending.

Additional local composition check: #144, #146 and #148 were cherry-picked onto unchanged main 8f70bc85dd6a469cae7d3b467af7dea720c92434 in an isolated worktree. python -m pytest -q -n 2 --tb=short returned 81 passed with the same real Redis 8.2.11 services. This local integration branch was not pushed; the public PR branches remain independent. This is not a GitHub CI result.

Prepared with OpenAI Codex assistance. Please keep this PR in Draft pending independent and account-owner review. No account-owner human review is claimed.

Prepared with OpenAI Codex assistance; independent and account-owner review are pending.
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.

Concurrent schedule deletion empties a Cluster source refresh

1 participant