gdmn
gdmn copied to clipboard
Протокол для коротких запросов/задач
Написанное ниже -- тема для обсуждения. Только после обсуждения всех деталей стоит приступить к выполнению.
У сети есть две проблемы: пропускная способность и латентность.
С первым мы будем бороться используя оптимальные структуры данных, двоичные форматы, упаковку.
С последствиями второй можно бороться только разумно уменьшая количество взаимодействий запрос-ответ между клиентом и сервером.
Посмотрим на запросы, которые приходят с клиента на сервер:
- Это команды системы вроде уже реализованной у нас PING.
- Это запросы к данным вроде объекта IQuery.
- Это команды на выполнение длительных процессов (как макросы в Гедымине).
Для запроса к серверу нам обязательно знать ответ успех\ошибка. В случае успеха сервер может переслать нам еще какие-то полезные данные. В случае ошибки может придти дополнительная информация: код ошибки, сообщение, стек вызовов и т.п.
По многим запросам к серверу невозможно отследить ход выполнения (погресс) чтобы, например, вывести его на экран пользователю.
Многие запросы к серверу нельзя прервать после того, как они запущены на выполнение. Можно только запустить и ждать успеха выполнения или ошибки.
По большинству команд нет никакого смысла в получении дополнительной информации, что задача принята сервером -- поставлена в очередь -- принята к выполнению и т.п.
Всё это необходимо учесть при оптимизации нашего протокола. Основная задача:
Минимум взаимодействий с сервером!
Идем дальше. У нас будут на клиенте процессы (например, процесс начальной загрузки) когда надо будет выполнить сразу несколько команд к серверу. Чтобы понимать, Гедымин в процессе начальной загрузки, выполняет до 120 запросов к базе данных.
Напрашивается возможность собрать на клиенте команды в список (массив) и одним целым за один раз отправить на сервер. Сервер, получив массив команд добавит их все в очередь (или как там у нас организовано) на выполнение. В дальнейшем команды будут выполняться сервером в своем порядке, как будто они пришли не в массиве а поотдельности. И, соответственно, ответы на клиента (успех, ошибка, там где надо прогресс и т.п.) будут идти на сервер уже поотдельности.
Т.е. массив -- только для отсылки с клиента на сервер.
У нас будут длительные запросы и задачи, для которых актуально знать прогресс и которые пользователь может прервать, но будут и короткие. Например запрос на обновление данных в БД (UPDATE, INSERT, DELETE) или запрос на извлечение небольшого набора записей. Он может выполняться сотую долю секунд и ни прогресс, ни возможность прервать для него не актуальна. Для такого запроса нам важно только знать Он выполнился успешно или возникла ошибка при его выполнении. Если да, то какая.
Т.е. при посылке запроса на сервер у программиста должна быть возможность указать:
- упрощенный -- только информация об успешном завершении или ошибке.
- расширенный -- информация о старте и прогрессе. Возможность прервать.
- по умолчанию -- тип запроса определит сервер.
Если программист при написании воркера для задачи не делал расчёт прогресса, то и на клиент он отправляться не будет. Возможность «прервать» выполнение есть у каждой задачи. В случае если задача не прерываемая, например один запрос к базе, то после выполнения результат просто не уйдёт на клиент, либо если это инсерт, можно откатить транзакцию. В любом случае, мне кажется, это должно быть связано с каждой задачей напрямую без отражения этого в апи протокола.
Остаётся только уведомления об изменении статусов задачи: запущено, на паузе и тд. Можно добавить тип задачи - «только результат». Но насколько это будет правильно и оправданно не уверен. Если на клиенте на этих статусах будет завязана какая-либо логика или ещё что, это приведёт к излишнему усложнению.
Надо делать.... фактически мы сделаем как сейчас платформу, которая не сможет работать на мобильных сетях и в интернете, где большая латентность...
На диалоговом окне в Гедымине могут быть 40-50 контролов, каждый из которыхбудет выполнять запрос к БД (например лукапы). Мы должны собрать их в один пакет и выполнить на сервере за один раз. Никаких прогрессов! Никаких прерываний для ни не надо. Только простейший трафик -- туда запрос -- назад ответ.
Упрощенный режим для задач
-
[x] Cервер (сделано в 246b2b5d0364b10729371dc20bf4474a191c0881, a16f001b794a466c58a96123098fe3510ce1ef1d) При добавлении задач с клиента, есть возможность послать в
stomp
заголовкеframe
-а флагreply-mode=1
. Этот флаг отключает уведомление клиента по всем stomp подпискам кроме подписки на результат. Т.е отправили запрос и гарантированно получили ответ с результатом или ошибкой. -
[x] Клиент Предлагается, добавить поверх существующей реализации протокола на
observable
возможность делать простые одиночные запросы с помощьюpromise
. В этом случае запрос на сервер сформируется с флагомreply-mode=1
, что отключит все уведомления клиента кроме получения результата/ошибки, и обернет запрос вpromise
. Внутриpromise
-а произойдет подписка на все уведомления и отслеживание результата, в случае если придут лишние уведомления,promise
должен завершится с ошибкой бэка. В остальномpromise
разрешается в зависимости от результата.
По итогу с сохранением возможности работать со сложными задачами получим простое api для работы с упрощенными, вроде:
try {
const result = await apiService.fetchQuery(...);
} catch (error) {
...
}
Можно закрывать?