PHPackages                             beeline/yii2-pgsql-advisory-mutex - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. beeline/yii2-pgsql-advisory-mutex

ActiveYii2-extension[Database &amp; ORM](/categories/database)

beeline/yii2-pgsql-advisory-mutex
=================================

PostgreSQL transaction-level advisory locks based mutex for Yii2

1.0.2(6mo ago)00LGPL-3.0-or-laterPHPPHP ^8.4CI passing

Since Nov 7Pushed 6mo agoCompare

[ Source](https://github.com/pozitronik/yii2-pgsql-advisory-mutex)[ Packagist](https://packagist.org/packages/beeline/yii2-pgsql-advisory-mutex)[ RSS](/packages/beeline-yii2-pgsql-advisory-mutex/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (2)Versions (4)Used By (0)

PostgreSQL Advisory Mutex для Yii2
==================================

[](#postgresql-advisory-mutex-для-yii2)

[![Tests](https://github.com/pozitronik/yii2-pgsql-advisory-mutex/actions/workflows/tests.yml/badge.svg)](https://github.com/pozitronik/yii2-pgsql-advisory-mutex/actions/workflows/tests.yml)[![Codecov](https://camo.githubusercontent.com/b4bc9a396d972fbaf9d7dbdfbed63afa8d0e484d873ba0fed66de7c8a87fa4db/68747470733a2f2f636f6465636f762e696f2f67682f706f7a6974726f6e696b2f796969322d706773716c2d61647669736f72792d6d757465782f6272616e63682f6d61737465722f67726170682f62616467652e737667)](https://codecov.io/gh/pozitronik/yii2-pgsql-advisory-mutex)[![Packagist Version](https://camo.githubusercontent.com/fef1841db467a14002d3e45648a675da196c4b14c0504535d4fa4277c6a473e3/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6265656c696e652f796969322d706773716c2d61647669736f72792d6d75746578)](https://packagist.org/packages/beeline/yii2-pgsql-advisory-mutex)[![Packagist License](https://camo.githubusercontent.com/3cc99fca0f8a77a782812d66b3947afb13f66617f478fc599c041fb68307ce01/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6265656c696e652f796969322d706773716c2d61647669736f72792d6d75746578)](https://packagist.org/packages/beeline/yii2-pgsql-advisory-mutex)[![Packagist Downloads](https://camo.githubusercontent.com/f94ca89a70f3cba5ce036155adbe2eb7bac55f404e46f03670ff404f0569dce1/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6265656c696e652f796969322d706773716c2d61647669736f72792d6d75746578)](https://packagist.org/packages/beeline/yii2-pgsql-advisory-mutex)

Реализация распределённого mutex для Yii2 на основе транзакционных advisory locks PostgreSQL. Обеспечивает безопасную, быструю и надёжную распределённую блокировку без физического хранения в таблицах.

Возможности
-----------

[](#возможности)

- **Транзакционные блокировки**: Автоматическое освобождение при COMMIT/ROLLBACK
- **Совместимость с PgBouncer**: Работает с пулингом соединений в любом режиме (session/transaction/statement)
- **Поддержка таймаутов**: Таймаут захвата блокировки с миллисекундной точностью
- **Разделяемые/эксклюзивные режимы**: Поддержка паттернов читатель-писатель
- **Нулевые накладные расходы на хранение**: Виртуальные блокировки без физических строк в таблицах
- **Генерация ключей через xxHash64**: Быстрое, устойчивое к коллизиям хеширование ключей блокировок
- **PHP 8.4+**: Современный PHP со строгой типизацией

Установка
---------

[](#установка)

```
composer require beeline/yii2-pgsql-advisory-mutex
```

Требования
----------

[](#требования)

- PHP &gt;= 8.4
- PostgreSQL &gt;= 9.1
- Yii2 &gt;= 2.0.45
- ext-pgsql

Настройка
---------

[](#настройка)

### 1. Применение миграции

[](#1-применение-миграции)

Mutex требует наличия PostgreSQL функции `try_advisory_xact_lock_timeout`. Примените миграцию:

```
# Используя команду миграции Yii2
php yii migrate --migrationPath=@vendor/beeline/yii2-pgsql-advisory-mutex/src/migrations
```

Или вручную выполните SQL из файла `src/migrations/m250202_000000_create_advisory_lock_timeout_function.php`.

### 2. Конфигурация приложения

[](#2-конфигурация-приложения)

```
return [
    'components' => [
        'mutex' => [
            'class' => \beeline\PgsqlAdvisoryMutex\PgsqlAdvisoryMutex::class,
            'db' => 'db', // ID компонента базы данных
        ],
    ],
];
```

Использование
-------------

[](#использование)

### Базовая блокировка

[](#базовая-блокировка)

```
use Yii;

$mutex = Yii::$app->mutex;

// Захват блокировки (ожидание бесконечно)
if ($mutex->acquire('my_lock')) {
    try {
        // Критическая секция - только один процесс выполняет это одновременно
        performCriticalOperation();
    } finally {
        $mutex->release('my_lock');
    }
} else {
    // Не удалось захватить блокировку
}
```

### Поддержка таймаутов

[](#поддержка-таймаутов)

```
// Попытка захватить блокировку с таймаутом 5 секунд
if ($mutex->acquire('my_lock', 5)) {
    try {
        performCriticalOperation();
    } finally {
        $mutex->release('my_lock');
    }
} else {
    // Истёк таймаут или блокировка удерживается другим процессом
    echo "Не удалось захватить блокировку в течение 5 секунд\n";
}

// Без ожидания (timeout = 0)
if ($mutex->acquire('my_lock', 0)) {
    // Получили блокировку немедленно
} else {
    // Блокировка занята
}
```

### Разделяемые блокировки (паттерн читатель-писатель)

[](#разделяемые-блокировки-паттерн-читатель-писатель)

```
// Несколько читателей могут одновременно захватить разделяемые блокировки
$readerMutex = new \beeline\PgsqlAdvisoryMutex\PgsqlAdvisoryMutex([
    'db' => Yii::$app->db,
    'sharedMode' => true,
]);

if ($readerMutex->acquire('resource')) {
    // Несколько читателей могут находиться здесь одновременно
    $data = readResource();
    $readerMutex->release('resource');
}

// Писатель использует эксклюзивную блокировку (блокирует и читателей, и писателей)
$writerMutex = new \beeline\PgsqlAdvisoryMutex\PgsqlAdvisoryMutex([
    'db' => Yii::$app->db,
    'sharedMode' => false, // по умолчанию
]);

if ($writerMutex->acquire('resource')) {
    // Эксклюзивный доступ
    writeResource($data);
    $writerMutex->release('resource');
}
```

### Расширенное использование

[](#расширенное-использование)

```
$mutex = Yii::$app->mutex;

// Получение информации о текущих advisory locks в базе данных
$activeLocks = $mutex->getActiveLocks();
// Возвращает: [['pid' => 12345, 'locktype' => 'advisory', 'mode' => 'ExclusiveLock', ...], ...]

// Получение блокировок, захваченных этим экземпляром mutex
$acquired = $mutex->getAcquiredLocks();
// Возвращает: ['my_lock' => ['lockKey' => -1234567890, 'sharedMode' => false], ...]

// Освобождение всех блокировок, захваченных этим экземпляром
$count = $mutex->releaseAll();
echo "Освобождено {$count} блокировок\n";
```

Принцип работы
--------------

[](#принцип-работы)

### Транзакционные Advisory Locks

[](#транзакционные-advisory-locks)

В отличие от session-level advisory locks, транзакционные блокировки автоматически освобождаются при коммите или откате транзакции. Это делает их безопасными для использования с пулингом соединений:

```
// Блокировка захватывается внутри транзакции
$mutex->acquire('my_lock');

// Если приложение упадёт или соединение потеряется,
// PostgreSQL автоматически освободит блокировку при откате транзакции
```

### Генерация ключей блокировок

[](#генерация-ключей-блокировок)

Имена блокировок хешируются в int64 с использованием xxHash64:

```
$mutex->acquire('user_123_profile');
// Внутренне: xxHash64('user_123_profile') -> -8234567890123456789
```

Вероятность коллизии с xxHash64 чрезвычайно мала (~10⁻¹⁹ для 1 миллиарда блокировок).

### Реализация таймаута

[](#реализация-таймаута)

Mutex использует настройку PostgreSQL `lock_timeout` для поддержки таймаутов:

```
-- Внутренне для timeout=2000ms
SET LOCAL lock_timeout = '2000ms';
SELECT pg_advisory_xact_lock(key);
-- lock_timeout восстанавливается после возврата функции
```

Важные ограничения
------------------

[](#важные-ограничения)

### Не реентерабельные

[](#не-реентерабельные)

Транзакционные advisory locks НЕ являются реентерабельными. Попытка захватить одну и ту же блокировку дважды в одной транзакции приведёт к блокировке:

```
$mutex->acquire('lock1'); // OK
$mutex->acquire('lock1'); // DEADLOCK - зависнет!
```

### Вложенные транзакции

[](#вложенные-транзакции)

В окружениях с вложенными транзакциями (например, Codeception с TransactionForcer) блокировки НЕ будут освобождены до коммита самой внешней транзакции:

```
// В тесте Codeception с TransactionForcer
$mutex->acquire('lock1'); // Создаётся SAVEPOINT, не новая транзакция
$mutex->release('lock1'); // SAVEPOINT закоммичен, но xact lock НЕ освобождён
// Блокировка освобождается только при коммите/откате внешней тестовой транзакции
```

### Совместимость с PgBouncer

[](#совместимость-с-pgbouncer)

Работает во **всех** режимах PgBouncer:

- **Session mode**: ✅ Полная поддержка
- **Transaction mode**: ✅ Полная поддержка (транзакционные блокировки)
- **Statement mode**: ✅ Полная поддержка (каждый запрос в своей транзакции)

Параметры конфигурации
----------------------

[](#параметры-конфигурации)

СвойствоТипПо умолчаниюОписание`db`Connection|string'db'Компонент базы данных или его ID`sharedMode`boolfalseИспользовать разделяемые блокировки (читатели) вместо эксклюзивных (писатель)`functionName`string'try\_advisory\_xact\_lock\_timeout'Имя функции PostgreSQLТестирование
------------

[](#тестирование)

### Локальное тестирование с Docker

[](#локальное-тестирование-с-docker)

```
# Запуск PostgreSQL
docker-compose up -d

# Установка зависимостей
composer install

# Запуск тестов
vendor/bin/phpunit

# Запуск с покрытием
vendor/bin/phpunit --coverage-html coverage/

# Остановка PostgreSQL
docker-compose down
```

### Переменные окружения

[](#переменные-окружения)

Настройте подключение к базе данных через переменные окружения:

```
export DB_HOST=localhost
export DB_PORT=5432
export DB_NAME=test_mutex
export DB_USER=postgres
export DB_PASSWORD=postgres

vendor/bin/phpunit
```

Производительность
------------------

[](#производительность)

Advisory locks обладают высокой производительностью:

- **Без дискового I/O**: Блокировки хранятся только в памяти
- **Быстрый захват**: Время захвата блокировки менее миллисекунды
- **Низкие накладные расходы**: Минимальное использование CPU и памяти
- **Масштабируемость**: Тысячи одновременных блокировок

Бенчмарк (PostgreSQL 16, одно ядро):

- Захват/освобождение блокировки: ~0.1мс
- Пропускная способность: ~10,000 операций/сек на соединение

Примеры использования
---------------------

[](#примеры-использования)

### Распределённая обработка задач

[](#распределённая-обработка-задач)

```
// Гарантируем, что только один воркер обрабатывает каждую задачу
if ($mutex->acquire("task:{$taskId}", 0)) {
    try {
        processTask($taskId);
    } finally {
        $mutex->release("task:{$taskId}");
    }
}
```

### Предотвращение cache stampede

[](#предотвращение-cache-stampede)

```
$cacheKey = 'expensive_data';
$data = Cache::get($cacheKey);

if ($data === null) {
    if ($mutex->acquire($cacheKey, 5)) {
        try {
            // Двойная проверка после захвата блокировки
            $data = Cache::get($cacheKey);
            if ($data === null) {
                $data = computeExpensiveData();
                Cache::set($cacheKey, $data);
            }
        } finally {
            $mutex->release($cacheKey);
        }
    } else {
        // Запасной вариант, если не удалось захватить блокировку
        $data = computeExpensiveData();
    }
}
```

### Координация миграций базы данных

[](#координация-миграций-базы-данных)

```
// Гарантируем, что только один экземпляр запускает миграции
if ($mutex->acquire('schema_migration', 0)) {
    try {
        runMigrations();
    } finally {
        $mutex->release('schema_migration');
    }
}
```

### Атомарные операции с внешними ресурсами

[](#атомарные-операции-с-внешними-ресурсами)

```
use beeline\PgsqlAdvisoryMutex\PgsqlAdvisoryMutex;

class FileProcessor
{
    private PgsqlAdvisoryMutex $mutex;

    public function __construct()
    {
        $this->mutex = new PgsqlAdvisoryMutex(['db' => Yii::$app->db]);
    }

    public function processFile(string $filename): void
    {
        // Используем имя файла как ключ блокировки
        if (!$this->mutex->acquire("file:{$filename}", 10)) {
            throw new \RuntimeException("Файл {$filename} уже обрабатывается");
        }

        try {
            // Только один процесс обрабатывает этот файл
            $content = file_get_contents($filename);
            $processed = $this->process($content);
            file_put_contents($filename, $processed);
        } finally {
            $this->mutex->release("file:{$filename}");
        }
    }
}
```

### Распределённый счётчик с блокировкой

[](#распределённый-счётчик-с-блокировкой)

```
use beeline\PgsqlAdvisoryMutex\PgsqlAdvisoryMutex;

class DistributedCounter
{
    private PgsqlAdvisoryMutex $mutex;

    public function __construct()
    {
        $this->mutex = new PgsqlAdvisoryMutex(['db' => Yii::$app->db]);
    }

    public function increment(string $counterName): int
    {
        if (!$this->mutex->acquire("counter:{$counterName}", 5)) {
            throw new \RuntimeException('Не удалось захватить блокировку счётчика');
        }

        try {
            $current = (int)Cache::get($counterName, 0);
            $new = $current + 1;
            Cache::set($counterName, $new);
            return $new;
        } finally {
            $this->mutex->release("counter:{$counterName}");
        }
    }
}
```

Отладка
-------

[](#отладка)

### Просмотр активных блокировок

[](#просмотр-активных-блокировок)

```
$mutex = Yii::$app->mutex;
$locks = $mutex->getActiveLocks();

foreach ($locks as $lock) {
    echo "PID: {$lock['pid']}, ";
    echo "Lock Key: {$lock['lock_key']}, ";
    echo "Mode: {$lock['mode']}, ";
    echo "Granted: " . ($lock['granted'] ? 'Yes' : 'No') . "\n";
}
```

### Мониторинг блокировок через SQL

[](#мониторинг-блокировок-через-sql)

```
-- Просмотр всех advisory locks
SELECT
    pid,
    locktype,
    mode,
    granted,
    objid as lock_key
FROM pg_locks
WHERE locktype = 'advisory'
ORDER BY pid, objid;

-- Поиск заблокированных процессов
SELECT
    blocked_locks.pid AS blocked_pid,
    blocking_locks.pid AS blocking_pid,
    blocked_activity.usename AS blocked_user,
    blocking_activity.usename AS blocking_user,
    blocked_activity.query AS blocked_statement,
    blocking_activity.query AS blocking_statement
FROM pg_catalog.pg_locks blocked_locks
JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid
JOIN pg_catalog.pg_locks blocking_locks
    ON blocking_locks.locktype = blocked_locks.locktype
    AND blocking_locks.objid = blocked_locks.objid
    AND blocking_locks.pid != blocked_locks.pid
JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
WHERE NOT blocked_locks.granted
AND blocked_locks.locktype = 'advisory';
```

Лицензия
--------

[](#лицензия)

GNU Lesser General Public License 3.0

###  Health Score

34

—

LowBetter than 77% of packages

Maintenance69

Regular maintenance activity

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity54

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

3

Last Release

183d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/76ddc43524bb32eb36568e25b6ae283bc25bb51a4572789997909092145af0f5?d=identicon)[pozitronik](/maintainers/pozitronik)

---

Top Contributors

[![pozitronik](https://avatars.githubusercontent.com/u/2357892?v=4)](https://github.com/pozitronik "pozitronik (7 commits)")

---

Tags

postgresqlmutexyii2advisory-locksdistributed-lock

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/beeline-yii2-pgsql-advisory-mutex/health.svg)

```
[![Health](https://phpackages.com/badges/beeline-yii2-pgsql-advisory-mutex/health.svg)](https://phpackages.com/packages/beeline-yii2-pgsql-advisory-mutex)
```

###  Alternatives

[malkusch/lock

Mutex library for exclusive code execution.

9459.6M27](/packages/malkusch-lock)[tigrov/yii2-pgsql

Improved PostgreSQL schemas for Yii2

3467.0k](/packages/tigrov-yii2-pgsql)[yii2tech/illuminate

Yii2 to Laravel Migration Package

11315.1k](/packages/yii2tech-illuminate)[nanson/yii2-postgis

Yii2-extension to work with postgis data

1851.6k](/packages/nanson-yii2-postgis)[mootensai/yii2-relation-trait

Yii 2 Models load with relation, &amp; transaction save with relation

47220.3k9](/packages/mootensai-yii2-relation-trait)[nhkey/yii2-activerecord-history

Storage history of changes to ActiveRecord

46143.4k1](/packages/nhkey-yii2-activerecord-history)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
