Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Unique Job Locks Laravel Package

mohannadelemary/laravel-unique-job-locks

Prevent duplicate Laravel queue jobs from running at the same time. This package adds unique locks to dispatched jobs so identical tasks are skipped or deduplicated until the first one finishes. Simple setup, configurable lock keys and TTLs, and support for Redis and other cache stores.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

What it is: a read-only-first diagnostic package for a specific, high-toil failure class — ShouldBeUnique jobs that silently refuse to dispatch because a stale lock is held in Redis. v0.1.0 ships two Artisan commands and no runtime code paths.

  • Architecture fit: Excellent fit as tooling, not infrastructure. No service provider hooks into the queue, no listener, no middleware, no migrations, no schema changes. It registers two console commands and otherwise stays out of the request/worker lifecycle. That means near-zero production blast radius on install, which is the single most important property for a package adopted mid-incident.
  • Integration feasibility: composer require plus auto-discovery. Hard constraints are narrow: PHP 8.3+, Laravel 12/13, Redis cache store, phpredis or predis. This is effectively a modern-stack-only tool — teams on Laravel 10/11 or database/SQS-backed uniqueness cannot adopt it today. Database-store support is stated as planned, so it must be treated as unavailable in any roadmap commitment.
  • Correctness signal that matters: the commands respect uniqueVia(), which is the detail most homegrown scripts get wrong (locks land in a different cache store than the default and the "obvious" redis-cli KEYS check misleads). Reporting the resolved store, lock connection, and full key including cache and connection prefixes is the difference between a 5-minute answer and an hour of guessing which environment's prefix you're staring at.
  • Blast radius of the only mutating path: --release prompts before acting and performs an owner-checked delete (compare-and-delete), so a worker that re-acquired the lock between inspection and release isn't silently unlocked. That is the right primitive. Risk remains that the check is not atomic on the Redis side (WATCH/Lua vs. read-then-delete) — worth verifying before permitting it in production.
  • Technical risk (ranked):
    1. Key-format coupling. The package derives lock names from Laravel's internal cache key composition (laravel_unique_job: style prefixes, connection/cache prefixes). If a Laravel minor release changes that composition, the tool doesn't crash — it reports no lock held, which is a false negative during an incident. Silent-wrong is worse than broken here.
    2. Queue search cost. The "why" classification (still queued / running / crashed / orphaned) requires searching the queue, bounded by --limit. On large Horizon backlogs this is the difference between a fast answer and a scan that hurts. Whether it uses SCAN or KEYS internally is a go/no-go detail.
    3. Maturity. v0.1.0, single maintainer, no evidence yet of CI matrix, test coverage, or Redis Cluster support. Pre-1.0 command names and flags should be assumed unstable.
    4. Classification is heuristic. "A worker probably crashed" is inference, not fact. The wording is appropriately hedged, but on-call must be trained not to treat it as authoritative before deleting a lock.
  • Key questions to put to the maintainer / spike:
    • How is the Redis key format derived, and is there a test that fails loudly on Laravel key-schema drift?
    • Is owner-checked release atomic (Lua/WATCH) or read-then-delete?
    • Does the queue search use SCAN/cursor pagination, and what does --limit actually bound — jobs read, or keys scanned?
    • Redis Cluster and multiple named connections — supported or single-node only?
    • Is there a --json output mode for runbooks/automation, and does --release accept a non-interactive confirmation for scripted use?
    • Does it cover ShouldBeUniqueUntilProcessing, and jobs dispatched in batches/chains where the unique ID differs per dispatch?
    • Support policy and Laravel 11 backport appetite?

Integration Approach

Stack fit. Target profile: Laravel 12/13, PHP 8.3+, Redis cache store used as the uniqueness backend (directly or via uniqueVia()), Horizon or plain Redis workers. Every other shop should wait — the package has no value where uniqueness lives in the database cache store, and no value on Laravel ≤11 today.

Migration path. There is none, and that is the point. No data migration, no config publish required, no worker restart, no deploy coupling. Adoption is a composer.json line and a composer update. Recommend landing it as require-dev first, then promoting to a production dependency only if on-call runbooks reference it (a tool nobody can run during an incident is a tool that doesn't exist).

Compatibility checks before merge:

  • Confirm the CI matrix covers your exact Laravel minor, not just the major — cache key composition is the fragile surface.
  • Confirm phpredis vs. predis parity, since the two expose Redis differently and a diagnostic tool that works on one client and not the other fails precisely when someone is under pressure.
  • Verify behavior with your real CACHE_PREFIX / per-store connection prefixes across local, staging, and prod. Multi-tenant or prefix-per-deploy setups are where the displayed key either saves or wastes the incident.
  • Confirm it degrades gracefully when Redis is unreachable.

Sequencing (two sprints, low cost):

  1. Spike (days): install in a branch, reproduce a stuck lock on staging by killing a worker mid-job, and confirm both commands report the four states correctly.
  2. Validate the mutating path: run --release against a lock, then against a lock a worker has just re-acquired, and confirm the owner check refuses. Do not document --release until this passes.
  3. Runbook: add a short section to the queue-incident runbook — symptom ("job won't dispatch, no error"), the two commands, --limit guidance, and an explicit rule that --release requires a second pair of eyes and confirmation that no worker is mid-retry.
  4. Decide prod dependency: promote from require-dev if the spike passes; pin the version and treat pre-1.0 bumps as reviewable changes.
  5. Re-evaluate on database-store support or a 1.0 tag, whichever comes first.

Operational Impact

Maintenance. Small surface, single maintainer, pre-1.0. Expect flag and command-name churn; pin exactly and review diffs on upgrade. The real maintenance burden isn't the package — it's the implicit dependency on Laravel's internal cache key format. Budget a check on every Laravel minor upgrade: run the tool against a known-held lock and confirm it still sees it. Add that to the upgrade checklist, otherwise the failure mode is a silently useless diagnostic.

Support. This is the package's core TPM value: it compresses a recurring, high-frustration incident class into one command with a definitive answer (job, unique ID, TTL remaining, owner, exact key, store). The "why" classification — still queued / running / probably crashed / orphaned — is genuinely useful triage vocabulary and matches how on-call engineers actually reason. The TTL-left field is underrated: it tells you whether to wait or intervene, which is the first decision anyone makes.

Scaling. Read cost scales with the queue search, not with lock count. On a Horizon deployment with a deep backlog, --limit is the throttle and the default matters — confirm it's conservative. Push back hard on any implementation that uses KEYS over a large keyspace; a diagnostic tool that degrades production Redis while diagnosing production Redis is a net negative. Encourage a --json path so the check can be automated into alerting ("lock held > 2× expected job duration") rather than remaining manual.

Failure modes to write down:

  • False negative after a Laravel upgrade — key format drift yields "no lock held" while the lock exists. Most dangerous mode; mitigated only by the upgrade checklist above.
  • Wrong environment/prefix — operator inspects staging prefix against a prod symptom. The explicit prefix display largely prevents this, which is why it's a good design choice.
  • Misclassification of a slow job as "crashed" — a legitimately long-running job looks orphaned; a premature --release then races a live worker. Owner-checked delete limits but does not eliminate the consequence (typically a duplicate execution).
  • uniqueVia() on a non-default store — handled, but only if the CLI process resolves the same config as the worker (env/config divergence between a jumpbox and a worker pod is a real trap).
  • Redis unavailable or cluster-only topology — command errors; ensure the runbook notes it's not a fallback for redis-cli.

Ramp-up. Very low: two self-describing command names, no concepts to teach, roughly 15 minutes for an on-call engineer including the runbook paragraph. The training investment is not in the tool but in the judgment around --release — specifically, that "orphaned" and "probably crashed" are inferences and that releasing a lock is a correctness action, not a cleanup action. Recommend one 10-minute walkthrough at the next on-call handover, with a live demo on staging.

Net recommendation: adopt behind a staging spike, ship as require-dev immediately, promote to production once the owner-check release path and the key-format behavior on your Laravel minor are verified. Do not plan against database-store support or Redis Cluster until they appear in a release. Value is narrow but sharp — it targets one of the most expensive-to-diagnose, cheapest-to-fix failure modes in a Redis-backed Laravel queue, and its read-only default plus owner-checked release show appropriate respect for production.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib