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

Lockable Laravel Package

laravel-appkit/lockable

Lockable adds record-level locking to Laravel Eloquent models, helping prevent concurrent edits and race conditions. Acquire, release, and auto-expire locks through a simple trait and fluent API, keeping your data safe in multi-user applications.

View on GitHub
Deep Wiki
Context7

Getting Started

This is the initial v1.0.0 release, and the release notes are deliberately thin ("some very basic locking functionality"), so treat the source as the spec. Start in this order:

  1. README — confirm the public API surface (facade vs. helper vs. injected service) and the exact acquire/release method names.
  2. Service provider — check what is bound into the container, whether a config file is published, and whether a facade alias is registered.
  3. src/ — read the lock implementation itself; at v1.0.0 expect a thin wrapper over Laravel's cache lock primitives (Cache::lock()) or a database-backed lock. Confirm which.
  4. tests/ — the tests are the most reliable documentation of intended behavior at this maturity level.
  5. composer.json — note the supported Laravel/PHP versions and whether a specific cache driver is assumed.

Minimal first use case: protect a single non-reentrant critical section — for example, a scheduled command that must not overlap, or a controller action that must not double-process a webhook. Acquire the lock, run the work inside a try, and release in finally. Verify behavior locally against your cache driver (Redis/Memcached/database) before trusting it in production, because "basic" locking packages often only support a subset of drivers.

Before adopting, pin the version and read the issue tracker — an initial release has no track record, and locking bugs manifest as silent double-execution rather than loud failures.

Implementation Patterns

Guard a critical section with guaranteed release. Wrap acquisition and work so the lock is always freed, even on exceptions:

$lock = Locker::acquire('report:generate', ttl: 300);

if (! $lock) {
    return; // someone else owns it
}

try {
    $this->generateReport();
} finally {
    $lock->release();
}

Block-with-timeout for contended work. If the operation should wait rather than skip, use a bounded wait so a stuck lock cannot hang a request or worker indefinitely. Prefer a short timeout plus a fallback (queue it, return 409, or log and skip).

Overlap protection in the scheduler. The most common day-to-day use is preventing overlapping scheduled tasks. Combine the package lock with withoutOverlapping() semantics, or use the lock alone if the package provides the finer control you need (per-tenant keys, custom TTL).

Per-resource lock keys. Namespace keys by the entity being protected (invoice:{$id}:reconcile) rather than using a single global key. This is where a lock wrapper earns its keep over ad-hoc Cache::lock() calls — but verify the package allows arbitrary string keys and doesn't impose a prefix you can't control.

Queued jobs and idempotency. For jobs that mutate shared state, acquire inside handle() and release in a finally. Combine with $tries/backoff so a lock miss is treated as a retry rather than a failure. Do not hold the lock across dispatch() of chained work — acquire in the child job instead.

Testing. Assert both the happy path and the contention path: acquire twice, assert the second returns falsy; assert release is called on exception. If the package exposes a fake or an in-memory driver, use it — otherwise point tests at the array cache driver and confirm the package respects it.

Integration tip: keep acquisition near the edge of the critical section (as late as possible) and release as early as possible. Holding a lock across HTTP calls to third parties is the fastest way to blow your TTL.

Gotchas and Tips

  • Release is not automatic on process death. A crashed worker leaves the lock held until TTL expiry. Set TTLs based on the worst-case runtime of the section, not the typical runtime, and add a monitored failure path.
  • TTL vs. long jobs. If the work can exceed the TTL, the lock silently expires mid-execution and a second worker enters. Either refresh the lock periodically (if supported) or size TTLs generously and alarm on near-expiry.
  • Driver dependency. Confirm which cache store the package uses. file and array drivers do not provide cross-process locking in the way Redis/Memcached/database do. A lock that works in a single-process test will not protect you across servers.
  • Owner tokens. Make sure release only succeeds for the owner of the lock (Laravel's cache locks use an owner token). A blanket delete-style release can free someone else's lock after a TTL rollover — check the implementation for this.
  • Reentrancy. Basic locking is almost always non-reentrant. If a service acquires a lock and then calls another method that acquires the same key, you deadlock or skip unexpectedly. Thread a "already locked" flag through, or split the critical section.
  • Clock skew and multi-node time. TTL-based locks rely on the backing store's clock, not yours; avoid computing expiry locally.
  • Config quirks. If a config file is publishable, re-publish after upgrades and diff it — initial releases frequently rename keys. Check for a configurable key prefix and TTL default; hard-coded values are a common early-release smell.
  • Extension points. At v1.0.0 the safest extension point is the container binding — bind your own implementation or decorator around the package's lock service rather than forking. If a facade is registered, confirm it resolves from the container so you can swap it.
  • Debugging. When a lock "isn't working," inspect the cache store directly (Redis KEYS, database lock table) to confirm keys are being written and deleted. Log acquire/release with the key and owner token; silent lock misses are the hardest failures to trace.
  • Semver risk. Treat ^1.0 as ~1.0 for now. A package this young is likely to make breaking changes in minor releases while the API settles.
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.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource