Дополнительно/Специальные возможности/Асинхронность/Сброс состояния после запроса

Сброс состояния для асинхронных запросов

Во фреймворке HLEB2 есть возможность выполнять асинхронные запросы, что накладывает дополнительные требования к коду. Одно из основных требований - нужно избавляться от хранимого состояния при завершении запроса.

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

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

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

Современные практики программирования не рекомендуют пользоваться состоянием, хранимым как статическое свойство класса, но часто оно удобно и забота о нём возникает только при переходе на асинхронный режим.
Чтобы этот переход был удобным во фреймворке HLEB2 есть специальный интерфейс RollbackInterface с одним статическим методом rollback.

Например, есть такое хранимое состояние с данными текущего пользователя (упрощённый код):

class Example
{
    private static ?
User $currentUser null;

    public function 
set(User $user): void
    
{
        
self::$currentUser $user;
    }
}

Чтобы состояние сбросилось, добавлен интерфейс RollbackInterface и реализован метод rollback:

class Example implements \Hleb\Base\RollbackInterface
{
    private static ?
User $currentUser null;

    public function 
set(User $user): void
    
{
        
self::$currentUser $user;
    }

    #[\Override]
    public static function 
rollback(): void
    
{
        
self::$currentUser null;
    }
}

Теперь при завершении асинхронного запроса фреймворк проверит, есть ли у класса интерфейс RollbackInterface и выполнит метод сброса rollback. Необходимо принять во внимание, что метод, сбрасывающий состояние, должен быть идемпотентным и не делать ничего больше. То есть при повторном выполнении результат применения не будет другим.

Для чего нужна идемпотентность видно из следующего, более сложного примера, в котором интерфейс применяется в наследовании (метод сброса может быть вызван дважды):

class Example implements \Hleb\Base\RollbackInterface
{
    private static ?
User $currentUser null;

    #[\Override]
    public static function 
rollback(): void
    
{
        
self::$currentUser null;
    }
}

class 
ChildExample extends Example
{
    
/** @param User[] */
    
private static array $currentUserFriends = [];

    #[\Override]
    public static function 
rollback(): void
    
{
        
parent::rollback();
        
self::$currentUserFriends = [];
    }
}

Если необходимо выполнить какое-либо действие по завершению асинхронного запроса, не связанное со сбросом состояния в конкретном классе, то его можно добавить в метод rollback класса App\Bootstrap\ContainerFactory.

Страница создана: @fomiash
К началу страницы