Skip to content

Celery deduplication guard#121

Merged
wpak-ai merged 4 commits into
cppalliance:developfrom
whisper67265:fix/celery-dedup
Jun 17, 2026
Merged

Celery deduplication guard#121
wpak-ai merged 4 commits into
cppalliance:developfrom
whisper67265:fix/celery-dedup

Conversation

@whisper67265

@whisper67265 whisper67265 commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Close #114.

Summary by CodeRabbit

  • Improvements

    • Added Redis-backed distributed locking for Celery add-or-update tasks to prevent concurrent duplicates.
    • Added a stable error code for duplicate task attempts and includes a lock identifier in error details.
    • Added configurable task-lock behavior via environment variables (timeout, conflict handling, and wait time).
  • Testing

    • Added deterministic tests for lock acquisition/release behavior and stable lock-key generation.
  • Chores

    • Updated Dependabot to restrict uv updates to direct dependencies only.
    • Updated the Weblate pin-bump workflow to install required system packages first.
    • Updated .env.example with the new task-lock configuration options.

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 18aaa2ad-a6a9-4751-82e5-1799c3b8e99e

📥 Commits

Reviewing files that changed from the base of the PR and between 2438ea7 and 20c61e0.

📒 Files selected for processing (1)
  • .env.example

📝 Walkthrough

Walkthrough

Adds a Redis-based distributed lock to prevent concurrent executions of boost_add_or_update_task. A new task_lock.py module provides key canonicalization via SHA-256 hashing and a redis_task_lock decorator. Settings are added to settings_override.py, a TASK_DUPLICATE error code is added, and tests cover lock behavior. Two minor CI and Dependabot changes are also included.

Changes

Redis task deduplication lock

Layer / File(s) Summary
Lock key builder and decorator factory
src/boost_weblate/utils/task_lock.py, src/boost_weblate/endpoint/errors.py
build_add_or_update_lock_key canonicalizes and SHA-256-hashes task kwargs into a namespaced Redis key. redis_task_lock wraps a Celery task to acquire the lock, raise BoostEndpointError(TASK_DUPLICATE) on conflict, and always release in a finally block. TASK_DUPLICATE = "task_duplicate" is added to BoostEndpointErrorCode.
Lock settings and task wiring
src/boost_weblate/settings_override.py, src/boost_weblate/endpoint/tasks.py, .env.example
boost_task_lock_settings() reads env-overridable defaults for BOOST_TASK_LOCK_TIMEOUT, BOOST_TASK_LOCK_ON_CONFLICT, and BOOST_TASK_LOCK_WAIT_TIMEOUT. boost_add_or_update_task gains @redis_task_lock(key_builder=build_add_or_update_lock_key) decorator. .env.example documents the task lock configuration variables.
Task lock tests and fixtures
tests/endpoint/test_views.py
Shared _TASK_KWARGS constant and a _FakeLock stub; autouse fixture patches Redis lock acquisition. Three new tests cover duplicate rejection with error metadata, lock release on success, and key stability across argument ordering and version changes.

CI and Dependabot configuration

Layer / File(s) Summary
Dependabot and workflow updates
.github/dependabot.yml, .github/workflows/weblate-pin-bump.yml
Dependabot uv block restricts to direct dependencies only; Weblate pin-bump workflow adds an apt-install step before version resolution.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant redis_task_lock
  participant build_add_or_update_lock_key
  participant Redis
  participant boost_add_or_update_task

  Caller->>redis_task_lock: invoke(**kwargs)
  redis_task_lock->>build_add_or_update_lock_key: compute key from sorted/hashed kwargs
  build_add_or_update_lock_key-->>redis_task_lock: "boost_weblate:add_or_update:<digest>"
  redis_task_lock->>Redis: Lock.acquire(blocking=wait|False)
  alt acquired
    Redis-->>redis_task_lock: True
    redis_task_lock->>boost_add_or_update_task: call wrapped function
    boost_add_or_update_task-->>redis_task_lock: result
    redis_task_lock->>Redis: lock.release()
    redis_task_lock-->>Caller: result
  else not acquired
    Redis-->>redis_task_lock: False
    redis_task_lock-->>Caller: raise BoostEndpointError(TASK_DUPLICATE, lock_key=...)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐇 Hoppity-hop through the Redis cache,
A lock on the task keeps the queue in place!
Two bunnies can't burrow the same carrot row—
TASK_DUPLICATE tells the second one "no!"
SHA-256 keys and a sorted stash,
No duplicate work, just a clean, fast dash! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 24.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Celery deduplication guard' clearly and accurately summarizes the main objective of implementing a Redis-based deduplication mechanism for Celery tasks.
Linked Issues check ✅ Passed All acceptance criteria from issue #114 are met: duplicate task rejection with TASK_DUPLICATE error [#114], lock key metadata inclusion [#114], lock release after completion [#114], and stable lock keys regardless of ordering [#114].
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the Celery deduplication guard: new error code, task lock utilities, decorator application, settings configuration, tests, and documentation updates.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/boost_weblate/settings_override.py`:
- Around line 151-154: The `BOOST_TASK_LOCK_ON_CONFLICT` environment variable
value is being used directly without validation or normalization, which can
cause silent behavior changes if the value has different casing (like "WAIT") or
whitespace. Normalize the environment variable value by converting it to
lowercase and stripping whitespace before assigning it to the `on_conflict` key
in the settings dictionary at lines 151-154. This ensures the value matches the
exact string check (like "wait") performed in
src/boost_weblate/utils/task_lock.py at line 86.

In `@src/boost_weblate/utils/task_lock.py`:
- Around line 105-108: The `lock.release()` call in the `finally` block only
catches `LockError`, but redis-py's Lock.release() can raise other exceptions
like `ConnectionError`, `TimeoutError`, and other `RedisError` subclasses that
aren't subclasses of LockError. When these infrastructure errors occur after
successful task execution, they cause the task to fail despite success. Replace
the single except clause with two separate exception handlers: catch `LockError`
first (which includes LockNotOwnedError as a subclass) with debug logging, then
catch `RedisError` with warning logging. This way lock-specific errors are
handled differently from Redis infrastructure errors, preventing false task
failures that trigger duplicate reprocessing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f43343bd-ba1f-406d-aca9-5ec47b57bda9

📥 Commits

Reviewing files that changed from the base of the PR and between 7026f96 and d6a0438.

📒 Files selected for processing (7)
  • .github/dependabot.yml
  • .github/workflows/weblate-pin-bump.yml
  • src/boost_weblate/endpoint/errors.py
  • src/boost_weblate/endpoint/tasks.py
  • src/boost_weblate/settings_override.py
  • src/boost_weblate/utils/task_lock.py
  • tests/endpoint/test_views.py

Comment thread src/boost_weblate/settings_override.py Outdated
Comment thread src/boost_weblate/utils/task_lock.py Outdated

@henry0816191 henry0816191 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Update boost-endpoint-api.md for BoostEndpointErrorCode.TASK_DUPLICATE

Comment thread src/boost_weblate/settings_override.py
@whisper67265 whisper67265 requested a review from wpak-ai June 17, 2026 18:26
@wpak-ai wpak-ai merged commit ec49aeb into cppalliance:develop Jun 17, 2026
9 checks passed
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.

Celery dedup guard

3 participants