Download the PHP package siberfx/laravel-mutex-lock without Composer

On this page you can find all versions of the php package siberfx/laravel-mutex-lock. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package laravel-mutex-lock

# Laravel Mutex Lock **Database-backed mutexes for Laravel, built on [php-lock/lock](https://github.com/php-lock/lock).** Only one process runs a critical section at a time, across web requests, queue workers, the scheduler and servers. The lock lives in the database you already run, so you don't need Redis. [![Tests](https://github.com/siberfx/laravel-mutex-lock/actions/workflows/tests.yml/badge.svg)](https://github.com/siberfx/laravel-mutex-lock/actions/workflows/tests.yml) [![Latest Version](https://img.shields.io/packagist/v/siberfx/laravel-mutex-lock.svg)](https://packagist.org/packages/siberfx/laravel-mutex-lock) [![PHP](https://img.shields.io/badge/php-8.4%20%7C%208.5-777BB4.svg)](https://www.php.net) [![Laravel](https://img.shields.io/badge/laravel-12.x%20%7C%2013.x-FF2D20.svg)](https://laravel.com) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

Table of contents


Why

Race conditions don't show up in development. They show up in production, when two requests or two workers read the same row at the same moment. This package gives you a simple, explicit way to prevent that:

Supported engines

Engine Mechanism Driver Released on crash Re-entrant*
MySQL 5.7+ / 8+ GET_LOCK() / RELEASE_LOCK() Malkusch\Lock\Mutex\MySQLMutex ✅ When the session closes ✅
MariaDB 10+ GET_LOCK() / RELEASE_LOCK() Malkusch\Lock\Mutex\MySQLMutex ✅ When the session closes ✅
PostgreSQL 12+ pg_advisory_lock() / pg_try_advisory_lock() Malkusch\Lock\Mutex\PostgreSQLMutex ✅ When the session closes ✅
SQLite 3.24+ Row in a lock table (atomic UPSERT) Siberfx\MutexLock\Mutex\SqliteMutex ⏱ After sqlite.expire seconds ❌

* Re-entrant means a nested lock with the same name on the same connection won't block itself.

Requirements

Installation

The service provider and the Mutex facade are auto-discovered.

To publish the config file (optional):

Quick start

The lock is always released, whether the callback returns normally or throws.


Usage

Running code under a lock

synchronized() waits for the lock, runs your callback, releases the lock and returns the callback's result.

Exceptions thrown inside the callback are rethrown unchanged, after the lock is released:

Timeouts

The timeout is how long to wait for the lock, in seconds. It does not limit how long your callback may run.

[!NOTE] MySQL and MariaDB round GET_LOCK() timeouts up to whole seconds. PostgreSQL and SQLite support fractional timeouts.

Handling a busy lock

If the lock can't be acquired in time, Malkusch\Lock\Exception\LockAcquireTimeoutException is thrown. Your callback does not run.

A small helper for "skip if busy" semantics:

Double-checked locking

Double-checked locking avoids taking a lock when there's nothing to do. The check runs before the lock is acquired and again after. The callback runs only when both checks pass.

Without the second callback, then() returns false when a check fails.

Choosing a connection

By default, locks use mutex-lock.connection, or your default database connection if that's not set. You can override it per call:

[!IMPORTANT] Every process that competes for the same lock must use the same database server.

Dependency injection

The facade proxies Siberfx\MutexLock\MutexManager, a container singleton. You can inject it instead:

It's also available as app('mutex-lock').

Working with the raw php-lock mutex

Mutex::make() returns the underlying Malkusch\Lock\Mutex\Mutex. Use it to pass a mutex into code that is framework-agnostic:

Queue job middleware

WithMutex wraps a job's handle() in a lock. By default it tries once. If the lock is busy, the job goes back on the queue.

Option Behaviour
new WithMutex($name) Try once and release the job immediately if busy
new WithMutex($name, timeout: 5) Wait up to 5 seconds for the lock before giving up
new WithMutex($name, connection: 'pgsql') Lock on a specific connection
->releaseAfter(30) If busy, release the job back onto the queue with a 30 second delay
->dontRelease() If busy, throw LockAcquireTimeoutException (the attempt fails)

[!TIP] Each release counts as an attempt, so set $tries (or retryUntil()) high enough for your contention level.


Recipes

Preventing double spending

Idempotent webhooks

Payment providers retry webhooks, sometimes several times in parallel. Lock on the event ID and check whether the event was already processed:

Inventory reservation

Artisan commands that must not overlap

This works across servers even without a shared cache:

Cache stampede protection

When a hot cache key expires, only one process rebuilds it. The others wait, then read the fresh value:

Lazy one-time initialisation

Per-tenant locks

Use the prefix option when several apps share one database server:

Or build names that include the tenant:


Configuration

config/mutex-lock.php:

Key Env Default Description
connection MUTEX_CONNECTION null Connection to lock on (null = default connection)
timeout MUTEX_TIMEOUT 10 Seconds to wait for a lock (0 = try once, null = forever)
prefix MUTEX_PREFIX '' Prepended to every lock name
sqlite.table MUTEX_SQLITE_TABLE mutex_locks Lock table name
sqlite.expire MUTEX_SQLITE_EXPIRE 300 Seconds after which a lock left by a crashed process can be reclaimed (null = never)
sqlite.auto_create_table MUTEX_SQLITE_AUTO_CREATE_TABLE true Create the lock table on first use

Managing the SQLite table with migrations

If you prefer migrations over auto-creation:

MySQL, MariaDB and PostgreSQL don't need a table.


Exceptions

All exceptions come from php-lock and extend Malkusch\Lock\Exception\MutexException (a RuntimeException).

When releasing the lock fails, LockReleaseException still gives you the outcome of your callback:


API reference

Method Returns Description
Mutex::synchronized(string $name, callable $callback, float\|int\|null $timeout = null, ?string $connection = null) mixed Acquire, run $callback, release and return its result
Mutex::check(string $name, callable $check, float\|int\|null $timeout = null, ?string $connection = null) DoubleCheckedLocking Start a double-checked lock. Call ->then($success, ?$fail) on the result
Mutex::make(string $name, float\|int\|null $timeout = null, ?string $connection = null) Malkusch\Lock\Mutex\Mutex Build the underlying php-lock mutex

$timeout: null uses the configured default, 0 tries once, INF waits forever, and any other value is a number of seconds.


How it works

MySQL / MariaDB. SELECT GET_LOCK(name, timeout) takes a named lock that belongs to the database session. RELEASE_LOCK(name) frees it. If the PHP process dies, the connection closes and the server frees the lock. Names are prefixed with php-malkusch-lock:. Names longer than 46 characters are hashed (sha1:…) to stay within MySQL's 64-character limit.

PostgreSQL. The lock name is hashed into two 32-bit keys for pg_advisory_lock(k1, k2). If the timeout is infinite, it blocks on the server. Otherwise it polls pg_try_advisory_lock with exponential backoff until the timeout runs out. Like MySQL, the lock is released when the session ends.

SQLite. SQLite has no advisory locks, so each lock is a row (name, token, expires_at) in the mutex_locks table. A single atomic UPSERT acquires the lock. It succeeds only if the row doesn't exist or has expired. Release deletes the row with the acquiring token, so a process can never release someone else's lock. If the database is busy, the attempt counts as "not acquired yet" and is retried with backoff.


Caveats


Mutex vs. Cache::lock()

Laravel ships atomic locks through the cache. Both are good tools. Pick based on your infrastructure and failure model:

Mutex (this package) Cache::lock()
Backend Your SQL database Redis, Memcached, DynamoDB, database, …
Released if the process dies Immediately (MySQL/PG), on expiry (SQLite) On TTL expiry
TTL you must estimate No (MySQL/PG) Yes
Blocking wait Server-side (MySQL, PG with INF) Polling
Double-checked locking Built in Manual

Testing your code

The facade can be mocked like any Laravel facade. To just run the callback in tests:

To simulate a busy lock:

The real implementation also works in tests on an in-memory SQLite database. No mocking is needed when you only care about the callback's effects.


Running the package tests

DB_HOST, DB_PORT and DB_PASSWORD are also read. CI runs the full matrix: PHP 8.4/8.5 × Laravel 12/13 × SQLite/MySQL/MariaDB/PostgreSQL × lowest/latest dependencies.

Changelog

See CHANGELOG.md.

Credits

License

The MIT License (MIT). See LICENSE.


All versions of laravel-mutex-lock with dependencies

PHP Build Version
Package Version
Requires php Version ^8.4
ext-pdo Version *
illuminate/contracts Version ^12.0 || ^13.0
illuminate/database Version ^12.0 || ^13.0
illuminate/support Version ^12.0 || ^13.0
malkusch/lock Version ^3.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package siberfx/laravel-mutex-lock contains the following files

Loading the files please wait ...