Файл TransactionTrait.php в Cotonti

Основное назначение файла TransactionTrait.php и его место в системе

Файл TransactionTrait.php в движке Cotonti CMF реализует трейд (помощник) для работы с транзакциями в базе данных. Его основное назначение — предоставить механизмы для управления транзакциями, включая начало транзакции, её коммит (подтверждение), откат (rollback), использование точек сохранения (savepoints) и обработку исключений, связанных с транзакциями.

Этот трейд используется в компонентах, которые работают с базой данных, и позволяет обеспечивать атомарность операций: транзакции гарантируют, что все изменения в базе данных либо будут применены, либо откатятся в случае ошибки. Это важно для предотвращения частичных изменений, которые могут нарушить целостность данных.

Трейд реализует основные функции для работы с транзакциями, включая управление уровнями изоляции транзакций, повторные попытки в случае ошибок параллелизма (например, deadlock), и обработку ошибок, связанных с потерей соединения с базой данных.

Основные задачи и условия, выполняемые кодом:

  1. Управление транзакциями:

    • Начало транзакции (beginTransaction).
    • Коммит (подтверждение) транзакции (commit).
    • Откат транзакции (rollBack).
    • Использование точек сохранения (savepoints) для возможности отката к определенному состоянию в рамках одной транзакции.
  2. Управление уровнями изоляции транзакций:

    • Установка уровня изоляции транзакции (setTransactionIsolationLevel).
  3. Повторные попытки транзакций в случае ошибок:

    • Перехват и обработка ошибок, таких как deadlock или потеря соединения, с автоматической попыткой повторения транзакции при необходимости.
  4. Обработка исключений:

    • Обработка ошибок при выполнении транзакций и соответствующие меры для восстановления соединения или отката изменений.

Классы в файле:

  • В файле TransactionTrait.php нет классов, так как это трейд, а не полноценный класс. Трейд используется другими классами для добавления функционала работы с транзакциями.

Методы и функции в файле:

  1. transaction(Closure $callback, ?string $isolationLevel = null, int $attempts = 1):

    • Выполняет переданный в качестве аргумента callback в рамках транзакции.
    • Позволяет задать уровень изоляции и количество попыток для транзакции в случае ошибок.
  2. beginTransaction(?string $isolationLevel = null):

    • Начинает транзакцию и устанавливает уровень изоляции, если он был передан.
  3. createTransaction(?string $isolationLevel = null):

    • Реализует логику начала транзакции, с проверкой на поддержку точек сохранения и уровня изоляции.
  4. setTransactionIsolationLevel(string $isolationLevel):

    • Устанавливает уровень изоляции для транзакции, например, READ COMMITTED, REPEATABLE READ, и т.д.
  5. supportsSavepoints(): bool:

    • Проверяет, поддерживает ли текущая СУБД (система управления базами данных) использование точек сохранения (savepoints).
  6. commit():

    • Подтверждает изменения в базе данных, завершая транзакцию.
  7. rollBack(?int $toLevel = null): void:

    • Откатывает транзакцию. Можно откатить до определенной точки сохранения, если она была установлена.
  8. performRollBack(int $toLevel): void:

    • Реализует сам процесс отката транзакции, включая откат до конкретной точки сохранения, если это необходимо.
  9. createSavepoint(string $name): void:

    • Создает точку сохранения в транзакции с заданным именем.
  10. releaseSavepoint(string $name): void:

    • (Не реализовано для MySQL с PDO) Предназначено для освобождения точки сохранения.
  11. rollBackSavepoint($name):

    • Откатывает изменения в транзакции до заданной точки сохранения.
  12. handleBeginTransactionException(Throwable $e): void:

    • Обрабатывает исключения, возникающие при начале транзакции, например, восстановление соединения в случае его потери.
  13. handleCommitTransactionException(Throwable $e, int $currentAttempt, int $maxAttempts): void:

    • Обрабатывает ошибки при коммите транзакции, например, deadlock или потеря соединения.
  14. handleRollBackException(Throwable $e):

    • Обрабатывает ошибки при откате транзакции, например, при потере соединения.
  15. handleTransactionException(Throwable $e, int $currentAttempt, int $maxAttempts):

    • Обрабатывает исключения при выполнении транзакционных операций, например, deadlock.
  16. isConcurrencyError(Throwable $e): bool:

    • Проверяет, является ли ошибка ошибкой параллельности, такой как deadlock, на основе кода ошибки или сообщения.

Заключение:

Трейд TransactionTrait предоставляет мощный механизм для работы с транзакциями, включая поддержку уровней изоляции, точек сохранения и механизмы для повторения транзакций в случае ошибок. Этот функционал позволяет эффективно управлять транзакциями в различных сценариях, минимизируя риск потери данных и обеспечивая устойчивость приложения при параллельных запросах и сбоях соединения.

находится по адресу:  /system/database/traits/TransactionTrait.php

 

<?php

declare(strict_types=1); // Строгая типизация обязательна, что улучшает контроль типов в коде.

namespace cot\database\traits; // Пространство имен для организации кода, относящегося к работе с базой данных.

use Closure; // Подключение класса Closure для работы с анонимными функциями.
use cot\database\exceptions\DeadlockException; // Подключение пользовательского исключения для обработки дедлоков в базе данных.
use PDOException; // Подключение исключения PDO для обработки ошибок при работе с базой данных.
use Throwable; // Подключение базового интерфейса для обработки всех исключений.

Этот фрагмент задает основу для работы с функциональностью транзакций в базе данных. Он подключает необходимые пространства имен и классы для обработки исключений и выполнения операций.

 

trait TransactionTrait
{
    /**
     * @var int
     */
    protected $transactionLevel = 0; // Переменная для отслеживания текущего уровня вложенности транзакций.

Этот фрагмент кода определяет трейт TransactionTrait, который будет содержать методы для работы с транзакциями. Переменная $transactionLevel используется для хранения уровня вложенности транзакций, что важно для управления вложенными транзакциями в базе данных.

 

public function transaction(Closure $callback, ?string $isolationLevel = null, int $attempts = 1) 
{ 
    // Метод `transaction` выполняет переданный callback внутри транзакции. 
    // Он поддерживает повторные попытки в случае deadlock'ов. 
    // Параметры:
    // - $callback: функция, которая выполняет действия в транзакции.
    // - $isolationLevel: уровень изоляции транзакции (опционально).
    // - $attempts: количество попыток выполнения транзакции.

    for ($currentAttempt = 1; $currentAttempt <= $attempts; $currentAttempt++) { 
        // Цикл повторных попыток выполнения транзакции.
        // $currentAttempt отслеживает текущую попытку.
        // Выполняется до тех пор, пока не будут исчерпаны попытки.

        $this->beginTransaction($isolationLevel); 
        // Начинает новую транзакцию с заданным уровнем изоляции ($isolationLevel).

        try { 
            $result = $callback($this); 
            // Выполняет переданную функцию $callback, передавая текущий объект соединения.
        } catch (Throwable $e) { 
            // Перехватывает любые ошибки или исключения, возникшие внутри транзакции.

            $this->handleTransactionException($e, $currentAttempt, $attempts); 
            // Обрабатывает исключение (например, deadlock) и решает, нужно ли продолжать попытки.

            continue; 
            // Если возникло исключение, продолжает цикл, переходя к следующей попытке.
        }

        try { 
            $this->commit(); 
            // Пытается зафиксировать (commit) транзакцию, если выполнение прошло без ошибок.
        } catch (Throwable $e) { 
            // Перехватывает любые ошибки или исключения, возникающие при фиксации транзакции.

            $this->handleCommitTransactionException($e, $currentAttempt, $attempts); 
            // Обрабатывает исключение, связанное с commit'ом (например, ошибки БД).

            continue; 
            // Если commit не удался, переходит к следующей попытке транзакции.
        }

        return $result; 
        // Если транзакция успешно завершена и зафиксирована, возвращает результат выполнения $callback.
    }

    return null; 
    // Возвращает null, если все попытки выполнения транзакции завершились неудачей.
}

Комментарии по коду:

  1. Назначение метода: Этот метод обрабатывает выполнение кода внутри транзакции. Он включает в себя обработку исключений, повторные попытки транзакции в случае deadlock'ов и управление уровнями изоляции.
  2. Уровень изоляции: Если уровень изоляции задан, он применяется перед началом транзакции.
  3. Обработка исключений: Если внутри транзакции возникает ошибка или deadlock, метод обрабатывает её и, при необходимости, повторяет попытку.
  4. Попытки транзакции: Параметр $attempts определяет, сколько раз транзакция будет попытана до выброса исключения.
  5. Результат: Если транзакция успешно завершена, возвращается результат выполнения $callback. Если все попытки не удались, возвращается null.

 

/**
 * Starts a transaction.
 * @param ?string $isolationLevel The isolation level to use for this transaction.
 */
public function beginTransaction(?string $isolationLevel = null): void
{
    $this->createTransaction($isolationLevel); // Создаётся новая транзакция с указанным уровнем изоляции (если задан).
    $this->transactionLevel++; // Увеличивается уровень вложенности транзакций.
}

Объяснение:

  1. $this->createTransaction($isolationLevel);
    Метод создаёт новую транзакцию с уровнем изоляции, переданным в качестве параметра. Если уровень изоляции не указан, будет использоваться настройка по умолчанию.

  2. $this->transactionLevel++;
    Счётчик уровней транзакции увеличивается. Это важно для отслеживания вложенных транзакций, чтобы не допустить ошибок при их вложении.

 

protected function createTransaction(?string $isolationLevel = null): void
{
    if ($this->transactionLevel === 0) { // Если это первая транзакция (не вложенная)
        if ($isolationLevel !== null) { // Если передан уровень изоляции
            $this->setTransactionIsolationLevel($isolationLevel); // Устанавливаем уровень изоляции для транзакции.
        }
        try {
            $this->adapter->beginTransaction(); // Начинаем новую транзакцию.
        } catch (Throwable $e) { // Если возникла ошибка при начале транзакции
            $this->handleBeginTransactionException($e); // Обрабатываем исключение при начале транзакции.
        }
        return; // Завершаем выполнение метода, так как транзакция началась.
    }

    if (!$this->supportsSavepoints()) { // Если не поддерживаются сохранённые точки
        return; // Прерываем выполнение метода.
    }

    if ($this->adapter->inTransaction()) { // Если текущая сессия уже находится в транзакции
        $this->createSavepoint('LEVEL_' . $this->transactionLevel); // Создаём сохранённую точку с уникальным названием для уровня транзакции.
    }
}

Объяснение:

  1. if ($this->transactionLevel === 0)
    Этот блок проверяет, не является ли текущая транзакция первой. Если это так, то инициируется новая транзакция с возможностью задать уровень изоляции.

  2. $this->setTransactionIsolationLevel($isolationLevel);
    Устанавливается уровень изоляции для текущей транзакции, если он был передан.

  3. $this->adapter->beginTransaction();
    Метод начинает саму транзакцию через адаптер базы данных (например, через PDO).

  4. if (!$this->supportsSavepoints())
    Если база данных не поддерживает сохранённые точки, то метод просто завершает выполнение, так как сохранённые точки не могут быть использованы.

  5. $this->adapter->inTransaction()
    Проверка на то, находится ли текущая сессия уже в транзакции. Если да, то создаётся сохранённая точка для вложенной транзакции.

 

/**
 * @param ?string $isolationLevel
 */
public function setTransactionIsolationLevel(string $isolationLevel): void
{
    $this->query("SET TRANSACTION ISOLATION LEVEL $isolationLevel")->execute(); // Выполняем SQL-запрос для установки уровня изоляции транзакции.
}

Объяснение:

  1. $this->query("SET TRANSACTION ISOLATION LEVEL $isolationLevel")->execute();
    Этот метод формирует и выполняет SQL-запрос для установки уровня изоляции транзакции. Он использует параметр $isolationLevel, чтобы задать нужный уровень (например, READ COMMITTED, SERIALIZABLE и т.д.). Запрос будет отправлен в базу данных для изменения поведения транзакции.

 

public function supportsSavepoints(): bool
{
    return true; // Метод всегда возвращает true, что означает, что поддержка сохранённых точек (savepoints) доступна.
}

Объяснение:

  1. return true;
    Этот метод всегда возвращает true, что указывает на то, что система поддерживает сохранённые точки транзакций (savepoints). Это полезно для многоуровневых транзакций, когда необходимо откатить транзакцию до определённой точки, а не до начала транзакции.

 

 

public function commit()
{
    $this->transactionLevel--; // Уменьшаем уровень вложенности транзакции.

    if ($this->transactionLevel === 0) { // Если уровень транзакции вернулся к 0, значит это самая внешняя транзакция.
        // ensure that the transaction wasn't autocommitted
        if ($this->adapter->inTransaction()) { // Проверяем, находится ли текущая сессия в транзакции.
            $this->adapter->commit(); // Подтверждаем транзакцию в базе данных.
        }
        return; // Завершаем метод, так как внешняя транзакция завершена.
    }

    // @todo is it really needed? // Комментарий, указывающий, что код ниже может быть не нужен.

    // if ($this->supportsSavepoints()) { // Проверка на поддержку сохранённых точек транзакции.
    //     if ($this->adapter->inTransaction()) { // Проверка, в транзакции ли мы.
    //         $this->releaseSavepoint('LEVEL_' . $this->transactionLevel); // Освобождаем сохранённую точку для текущего уровня.
    //     }
    // }
}

Объяснение:

  1. $this->transactionLevel--;
    Уменьшается уровень текущей транзакции. Это позволяет отслеживать, сколько уровней вложенности транзакций активны в данный момент.

  2. if ($this->transactionLevel === 0)
    Если уровень транзакции достиг нуля, это означает, что транзакция больше не вложена в другую и является основной.

  3. if ($this->adapter->inTransaction())
    Проверка, находится ли в данный момент система в активной транзакции. Это нужно для того, чтобы не попытаться завершить транзакцию, если она не была начата.

  4. $this->adapter->commit();
    Подтверждение транзакции в базе данных. Это гарантирует, что все изменения, сделанные в рамках транзакции, будут сохранены.

  5. Закомментированный код о сохранённых точках:
    Этот блок кода проверяет поддержку сохранённых точек и, если они поддерживаются, освобождает сохранённую точку на основе текущего уровня транзакции. Однако этот код закомментирован, и в комментарии отмечено, что возможно он не нужен.

 

 

/**
 * Rolls back a transaction.
 */
public function rollBack(?int $toLevel = null): void
{
    // Если уровень возврата не указан, то откатываем на предыдущий уровень транзакции.
    $toLevel = $toLevel === null ? $this->transactionLevel - 1 : $toLevel;

    // Если уровень возврата меньше 0 или больше текущего уровня, то откат невозможен.
    if ($toLevel < 0 || $toLevel >= $this->transactionLevel) {
        return; // Прерываем выполнение метода, так как условия для отката некорректны.
    }

    try {
        $this->performRollBack($toLevel); // Попытка выполнить откат на указанный уровень транзакции.
    } catch (Throwable $e) {
        $this->handleRollBackException($e); // Обработка исключения, если откат не удался.
    }

    $this->transactionLevel = $toLevel; // Обновляем уровень транзакции после отката.
}

Объяснение:

  1. $toLevel = $toLevel === null ? $this->transactionLevel - 1 : $toLevel;
    Если параметр $toLevel не передан, то откатываемся на уровень, который был до текущего, то есть на предыдущий уровень транзакции.

  2. if ($toLevel < 0 || $toLevel >= $this->transactionLevel)
    Проверка, чтобы убедиться, что указанный уровень отката корректен. Если уровень отката меньше нуля или больше текущего уровня транзакции, откат не будет выполнен.

  3. $this->performRollBack($toLevel);
    Если уровень отката корректен, выполняем откат транзакции до указанного уровня.

  4. $this->handleRollBackException($e);
    В случае ошибки при выполнении отката обрабатываем исключение.

  5. $this->transactionLevel = $toLevel;
    После выполнения отката обновляем текущий уровень транзакции, чтобы он соответствовал уровню после отката.

 

 

protected function performRollBack(int $toLevel): void
{
    // Если уровень отката равен 0, откатываем все транзакции до начального состояния.
    if ($toLevel === 0) {
        if ($this->adapter->inTransaction()) {
            $this->adapter->rollBack(); // Откатываем всю транзакцию, если она активна.
        }
        return; // Прерываем выполнение метода, так как откат завершён.
    }

    // Если поддерживаются savepoints (точки сохранения), откатываем транзакцию до указанного уровня.
    if ($this->supportsSavepoints()) {
        if ($this->adapter->inTransaction()) {
            $this->rollBackSavepoint('LEVEL_' . $toLevel); // Откатываем на указанный уровень savepoint.
        }
    }
}

Объяснение:

  1. if ($toLevel === 0)
    Если уровень отката равен 0, это означает, что нужно откатить всю транзакцию до начального состояния (нулевого уровня). В таком случае вызывается метод rollBack() адаптера для отката всей транзакции.

  2. $this->adapter->inTransaction()
    Проверяется, активна ли текущая транзакция, перед выполнением операции отката.

  3. $this->rollBackSavepoint('LEVEL_' . $toLevel);
    Если поддерживаются точки сохранения (savepoints), откат происходит до определённого savepoint, заданного через уровень $toLevel.

  4. return;
    После выполнения отката на уровень 0 выполнение метода завершается.

 

 

protected function createSavepoint(string $name): void
{
    // Создаём точку сохранения с указанным именем.
    $this->query("SAVEPOINT $name")->execute(); // Выполняем SQL-запрос для создания savepoint с заданным именем.
}

Объяснение:

  1. $this->query("SAVEPOINT $name")->execute();
    Здесь выполняется SQL-запрос, который создаёт точку сохранения в базе данных с именем, переданным в параметре $name. Это позволяет позже откатить транзакцию к этому состоянию, не отменяя всю транзакцию, если нужно.

 

 

protected function releaseSavepoint(string $name): void
{
    // По какой-то причине это не работает в MySQL с PDO
    //$this->query("RELEASE SAVEPOINT $name")->execute(); // Закомментированный код для выполнения SQL-запроса, который бы освобождал точку сохранения с указанным именем.
}

Объяснение:

  1. Закомментированный код:
    • Комментарий указывает, что попытка выполнить команду RELEASE SAVEPOINT с использованием PDO в MySQL не работает как ожидалось.
    • Строка кода, которая закомментирована, была бы использована для освобождения точки сохранения с именем, переданным в параметре $name.
    • Это освобождение точки сохранения позволяет продолжить транзакцию после отката, исключая точку сохранения, к которой был выполнен откат.

Таким образом, код явно не работает в текущей конфигурации, но был бы полезен для освобождения savepoint в другой базе данных, которая поддерживает эту команду.

 

public function rollBackSavepoint($name)
{
    $this->query("ROLLBACK TO SAVEPOINT $name")->execute(); // Выполняет SQL-запрос, откатывая транзакцию до указанной точки сохранения с именем $name.
}

Объяснение:

  • Метод rollBackSavepoint: Этот метод выполняет откат транзакции до сохраненной точки (savepoint), указанной в параметре $name.
    • Запрос ROLLBACK TO SAVEPOINT $name откатывает все изменения, сделанные после создания точки сохранения с именем $name, при этом сама точка сохранения остается доступной для дальнейшего использования в этой транзакции.
  • Метод полезен, когда необходимо откатить только часть транзакции, не теряя всю информацию и возможность продолжить с того места, где была точка сохранения.
 
 
/**
 * Обрабатывает исключение, возникшее при начале транзакции.
 */
protected function handleBeginTransactionException(Throwable $e): void
{
    if ($this->isLostConnectionError($e)) { // Если ошибка связана с потерей соединения с базой данных
        $this->reconnect(); // Попытка переподключения к базе данных

        $this->adapter->beginTransaction(); // Повторно начинает транзакцию
        return; // Завершаем выполнение метода, так как транзакция была успешно восстановлена
    }
    throw $e; // В случае другой ошибки, выбрасываем исключение снова
}

Объяснение:

  • Метод handleBeginTransactionException: Этот метод обрабатывает исключения, которые могут возникнуть при попытке начать транзакцию.
    • Если ошибка связана с потерей соединения (например, потеря связи с базой данных), вызывается метод reconnect() для восстановления соединения с базой данных, и затем повторно инициируется начало транзакции с помощью $this->adapter->beginTransaction().
    • Если ошибка не связана с потерей соединения, исключение пробрасывается дальше с помощью throw $e.
  • Этот подход полезен, когда необходимо обеспечить восстановление транзакции после потери соединения, чтобы предотвратить сбои в процессе работы с базой данных.
 
 
 
protected function handleCommitTransactionException(Throwable $e, int $currentAttempt, int $maxAttempts): void
{
    $this->transactionLevel = max(0, $this->transactionLevel - 1); // Уменьшаем уровень транзакции, но не ниже 0

    if ($this->isConcurrencyError($e)) { // Если ошибка связана с конкуренцией (например, мертвая блокировка)
        if ($currentAttempt < $maxAttempts) { // Если количество попыток меньше максимального
            return; // Попробуем снова, не выбрасывая исключение
        }
        throw new DeadlockException($e->getMessage(), is_int($e->getCode()) ? $e->getCode() : 0, $e); // Иначе выбрасываем исключение DeadlockException
    }

    if ($this->isLostConnectionError($e)) { // Если ошибка связана с потерей соединения
        $this->transactionLevel = 0; // Сбрасываем уровень транзакции
    }

    throw $e; // В любом другом случае выбрасываем исходное исключение
}

Объяснение:

  • Метод handleCommitTransactionException: Этот метод обрабатывает исключения, возникающие при попытке зафиксировать транзакцию (commit).

    • Если ошибка является ошибкой конкуренции (например, мертвая блокировка), и количество текущих попыток меньше максимального, транзакция не отменяется, а выполняется повторная попытка.
    • Если количество попыток превышает максимальное, выбрасывается исключение DeadlockException.
    • Если ошибка связана с потерей соединения, уровень транзакции сбрасывается в 0.
    • В любом другом случае выбрасывается исходное исключение, переданное в метод.
  • Этот подход позволяет гибко обрабатывать ошибки транзакций, такие как конкуренция и проблемы с соединением, и при необходимости повторять транзакцию или выбрасывать исключение после нескольких попыток.

 

protected function handleRollBackException(Throwable $e)
{
    if ($this->isLostConnectionError($e)) { // Если ошибка связана с потерей соединения
        $this->transactionLevel = 0; // Сбрасываем уровень транзакции в 0
    }

    throw $e; // В любом другом случае выбрасываем исходное исключение
}

Объяснение:

  • Метод handleRollBackException: Этот метод обрабатывает исключения, возникающие при откате транзакции (rollback).

    • Если ошибка связана с потерей соединения, уровень транзакции сбрасывается в 0, чтобы избежать дальнейших проблем с транзакциями.
    • В любом другом случае выбрасывается исходное исключение, переданное в метод.
  • Этот подход позволяет эффективно управлять состоянием транзакций при возникновении ошибок, таких как потеря соединения, и обрабатывать их корректно, сбрасывая уровень транзакции, чтобы предотвратить дальнейшие сбои.

 

/**
 * Handle an exception encountered when running a transacted statement.
 */
protected function handleTransactionException(Throwable $e, int $currentAttempt, int $maxAttempts)
{
    // При мертвой блокировке (deadlock), MySQL откатывает всю транзакцию, поэтому мы не можем просто
    // повторить запрос. Нам нужно выбросить это исключение наружу и
    // позволить разработчику обработать это другим способом. Также мы уменьшаем уровень транзакции.
    if ($this->isConcurrencyError($e)) { // Проверка на ошибку параллельности (deadlock)
        if ($this->transactionLevel > 1) { // Если уровень транзакции больше 1
            $this->transactionLevel--; // Уменьшаем уровень транзакции
            throw new DeadlockException($e->getMessage(), is_int($e->getCode()) ? $e->getCode() : 0, $e); // Генерируем исключение DeadlockException
        }

        $this->rollBack(); // Откатываем транзакцию, если уровень транзакции 1

        if ($currentAttempt < $maxAttempts) { // Если попыток еще осталось
            return; // Повторяем попытку выполнения транзакции
        }
    }

    throw $e; // Если ошибка не связана с deadlock, выбрасываем исходное исключение
}

Объяснение:

  • Метод handleTransactionException: Этот метод обрабатывает исключения, возникшие при выполнении транзакции.

    • Deadlock (мертвая блокировка): Если ошибка связана с deadlock (конкуренцией), то при нескольких уровнях транзакции, уровень уменьшается, и выбрасывается исключение DeadlockException.
    • Если попытки повторить транзакцию превышены, то она откатывается, и происходит выброс исходного исключения.
    • Важно, что при ошибке параллельности (например, deadlock) метод позволяет повторить транзакцию, если число попыток еще не исчерпано.
    • Если же ошибка не связана с deadlock, просто выбрасывается исходное исключение.
  • Это помогает эффективно обрабатывать ситуацию с deadlock, предлагая возможность повторить транзакцию в случае ошибок параллельности.

 

protected function isConcurrencyError(Throwable $e): bool
{
    // Проверка, является ли ошибка ошибкой параллельности (например, deadlock)
    if ($e instanceof PDOException && ($e->getCode() === 40001 || $e->getCode() === '40001')) { // Если ошибка PDO с кодом 40001
        return true; // Ошибка параллельности (deadlock)
    }

    // Список ключевых фраз, которые могут указывать на deadlock
    $needles = [
        'Deadlock found when trying to get lock', // Сообщение о deadlock при попытке заблокировать ресурс
        'deadlock detected', // Сообщение о детектированном deadlock
        'The database file is locked', // База данных заблокирована
        'database is locked', // База данных заблокирована
        'database table is locked', // Таблица базы данных заблокирована
        'A table in the database is locked', // Таблица в базе данных заблокирована
        'has been chosen as the deadlock victim', // Было выбрано как жертва deadlock
        'Lock wait timeout exceeded; try restarting transaction', // Превышен тайм-аут ожидания блокировки, попробуйте перезапустить транзакцию
        'WSREP detected deadlock/conflict and aborted the transaction. Try restarting the transaction', // Сообщение о deadlock с использованием WSREP
    ];

    $haystack = mb_strtolower($e->getMessage()); // Приводим сообщение об ошибке к нижнему регистру для поиска

    // Проверка, содержит ли сообщение об ошибке одну из ключевых фраз
    foreach ($needles as $needle) { // Перебираем все ключевые фразы
        if (mb_stripos($haystack, $needle) !== null) { // Если фраза найдена в сообщении об ошибке
            return true; // Это ошибка параллельности
        }
    }

    return false; // Если ни одна из фраз не найдена, то это не ошибка параллельности
}

Объяснение:

  • Метод isConcurrencyError: Этот метод проверяет, является ли ошибка ошибкой параллельности (например, deadlock).

    • Сначала проверяется, является ли ошибка PDOException с кодом ошибки 40001, который часто указывает на deadlock.
    • Затем метод проверяет сообщение об ошибке на наличие ключевых фраз, таких как "Deadlock found when trying to get lock", "database is locked" и других, которые могут свидетельствовать о проблемах с блокировкой базы данных или deadlock.
    • Если хотя бы одна из ключевых фраз найдена в сообщении, метод возвращает true, указывая, что ошибка связана с deadlock.
    • Если фраз не найдено, возвращается false.
  • Этот метод важен для обработки транзакционных ошибок, связанных с блокировками, и позволяет принять решение о повторении транзакции при deadlock.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
 
 
 
 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

17 минут чтения Administrator

Комментарии (0)

Комментарии отсутствуют
Добавление комментариев доступно только зарегистрированным пользователям

Автор контента

Administrator

Оффлайн

Administrator

Последняя авторизация: 05.07.2026 18:32

  • Страница размещена: 28.01.2025 02:05
  • Последнее обновление: 28.01.2025 02:57