Основной аудит Laravel/MySQL с использованием триггеров

Вопрос или проблема

Для API, использующего Laravel и MySQL, мы пытаемся настроить механизм аудита с помощью триггеров MySQL. Мы расширили Illuminate\Database\DatabaseManager чем-то вроде

    public function connection($name = null)
    {
        $conn = parent::connection($name);

        $conn->statement("SET @app_user_id=?", [Auth::user()->id ?? 'no_login']);

        return $conn;
    }

Затем внутри триггера BEFORE UPDATE мы получаем @app_user_id и устанавливаем updated_by. Это, похоже, хорошо работает, есть ли лучший способ сделать это? Мы не хотим использовать события моделей Eloquent и нам вполне устраивают триггеры MySQL. С какими возможными проблемами мы можем столкнуться? Пример:

  1. Когда несколько пользователей одновременно используют приложение, возможно ли столкнуться с состояниями гонки, например, из-за повторного использования пула соединений, и что тот же @app_user_id используется в запросах разных пользователей?
  2. Мы заметили, что метод DatabaseManager->connection() вызывается очень часто, и также могут быть случаи, когда мы вовсе не хотим устанавливать @app_user_id, например, при массовом обновлении через команду. Можем ли мы установить @app_user_id внутри промежуточного программного обеспечения, возможно, расширив Illuminate\Auth\Middleware\Authenticate, не потеряв при этом ничего? Возможно ли, что обновление базы данных, которое происходит позже, будет использовать другое соединение, у которого @app_user_id не установлено?

Заранее спасибо, и пожалуйста, дайте знать, если потребуется больше объяснений.

Ответ или решение

Для настройки механизма аудита в API с использованием Laravel и MySQL важно учитывать несколько аспектов при работе с триггерами. Вы уже реализовали подход с использованием переменной @app_user_id, и это действительно рабочее решение. Однако, давайте рассмотрим ваши вопросы более подробно и предложим возможные оптимизации.

1. Возможные проблемы с гонками при использовании соединений

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

Рекомендации для этого случая:

  • Убедитесь, что каждый запрос использует отдельное соединение. Один из способов это сделать — подавлять использование пула или использовать другой способ обработки соединений для вашего контекста, если это возможно.
  • Рассмотрите возможность использования временных таблиц или атомарных операций для записи данных.

2. Установка @app_user_id в Middleware

Установка переменной @app_user_id в Middleware может быть хорошим решением, особенно если вы хотите избежать её установки во время массовых операций. Однако, вы должны гарантировать, что переменная будет установлена перед выполнением операции, которая требует её наличия.

Рекомендации для использования Middleware:

  • Используйте Middleware, чтобы установить @app_user_id. Это может быть сделано с помощью:
public function handle($request, Closure $next)
{
    if (Auth::check()) {
        DB::statement("SET @app_user_id=?", [Auth::user()->id]);
    } else {
        DB::statement("SET @app_user_id='no_login'");
    }

    return $next($request);
}
  • Убедитесь, что процесс обновления не использует другой экземпляр соединения. Если происходит использование другого соединения при выполнении операции (например, если вы вызываете команду, не используя DB напрямую), вы можете столкнуться с проблемой, когда переменная не будет доступна. В этом случае вы должны либо явно вызывать соединение по умолчанию, либо перенастраивать ваше подключение в классе команды.

Заключение

Для достижения надежного механизма аудита с использованием триггеров в MySQL, необходимо учитывать проблемы с соединением и правильную настройку переменных в контексте каждого запроса. Ваша текущая архитектура вполне обоснованна, но добавление Middleware для установки переменной @app_user_id может дать вам гибкость и упростить управление состоянием. Однако, тщательно следите за тем, чтобы переменная была действительно доступна во всех случаях, когда она необходима.

Если у вас возникнут дополнительные вопросы или будет нужна более подробная помощь с реализацией, не стесняйтесь обращаться.

Оцените материал
Добавить комментарий

Капча загружается...