Ускоряем Laravel Sanctum

laravel-sanctum-performance

При использовании персональных токенов доступа Sanctum для API аутентификации, я заметил, что часть входящих запросов, выполняются раз в 5-10 дольше, чем другие. Покопавшись я обнаружил, что при каждом запросе Sanctum обновляет поле last_used_at в таблице personal_access_tokens. Большинство запросов всё же выполнялись быстро, но мере увеличения их объёма, некоторые начинали тормозить.

Не я первый это заметил, но пока нет простого способа это отключить.

Решение

В проекте мне была не нужна информация о последнем использовании токена, поэтому, чтобы её убрать я создал модель PersonalAccessToken с методом save(), который проверяет изменено ли только поле last_used_at.

Сначала, с помощью artisan-команды я создал новую модель:

php artisan make:model PersonalAccessToken

Затем внутри модели я использовал следующий код:

namespace App;

use Laravel\Sanctum\PersonalAccessToken as SanctumPersonalAccessToken;

class PersonalAccessToken extends SanctumPersonalAccessToken
{
    /**
     * Ограничение сохранения записей PersonalAccessToken 
     *
     * Будет сохранять, только если изменилось
     * что-то кроме поля last_used_at.
     * Это предотвратит лишние записи в БД, 
     * так как мы нее используем это поле.
     *
     * @param  array  $options
     * @return bool
     */
    public function save(array $options = [])
    {
        $changes = $this->getDirty();
        // Проверяем два изменения, так как одно из них это всегда поле updated_at
        if (! array_key_exists('last_used_at', $changes) || count($changes) > 2) {
            parent::save();
        }
        return false;
    }
}

Наша кастомная модель расширяет модель из Sanctum и имеет всего лишь один метод save().

Она получает все изменённые значения и проверяет будет ли сохранятся что-то кроме last_used_at. Если да, то оно вызывает родительский метод save() из Sanctum. В противном случае просто возвращается false, который указывает, что сохранения не было.

После создания кастомной модели нам нужно задать её использование вместо дефолтной. Сделать это можно в методе boot() класса Providers/AppServiceProvider.php. В Sanctum есть статический метод usePersonalAccessTokenModel(), который можно использовать для задания модели для токенов.

Выглядеть это будет примерно так:

/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    // Используем нашу модель для токенов
    Sanctum::usePersonalAccessTokenModel(
        PersonalAccessToken::class
    );
}

После этого я сразу же заметил прирост производительности, так как запись в БД это дорогая операция, особенно если вы используете репликацию и разделение чтение/записи.

Автор: William Earnhardt
Перевод: Алексей Широков

Наш Телеграм-канал — следите за новостями о Laravel.