dta icon indicating copy to clipboard operation
dta copied to clipboard

Разработка функциональности автоматического обновления статуса задачи в ЦАП

Open DolphinChips opened this issue 10 months ago • 0 comments

Демонстрация

https://github.com/user-attachments/assets/8b53fc13-e3ee-4f71-a501-400eed4902fb

Как оно работает?

Фрагмент HTML со статусом задачи был выделен в отдельный шаблон student/task_status.jinja, student/task.jinja безусловно его включает в себя. Кроме того, этот статус теперь находится в div с id равным task-status. POST запрос на /group/<int:gid>/variant/<int:vid>/task/<int:tid>/ws при успешном отправлении задачи перенаправляет на себя же.

Была добавлена ручка /group/<int:gid>/variant/<int:vid>/task/<int:tid>/ws с WebSocket. При подключении он ждёт пока данная задача не будет проверена, после чего отправляет HTML с новым статусом задачи и закрывает сокет.

JS на /group/<int:gid>/variant/<int:vid>/task/<int:tid>, который добавляется на уровне шаблона только если задача проверяется, подключается к выше упомянутому WebSocket и заменяет содержимое #task-status на каждое пришедшее сообщение.

Почему оно так работает?

Было 3 варианта решить эту задачу:

  • Поллинг на стороне клиента - просто, чуть меньше нагружает сервер, возможно чуть больше тратит трафика, и мне вроде говорили так не делать
  • SSE - не нужно подключать сторонние библиотеки, старше чем сами вебсокеты (вроде), проще тестировать из pytest, но данный функционал будет сложнее вынести в отдельный процесс, о чём будет ниже
  • WebSocket - текущая реализация

Так как для последних двух способов необходимо держать "живой" запрос, которому необходимо занимать один из потоков у WSGI-сервера, придётся увеличить пул воркеров и потоков у каждого воркера, иначе эти долгоживущие запросы имеют вероятность повесить сервер. Для избежания этого в текущей реализации сервер закрывает сокет либо если задача не отправлена, либо как только она проверена.

В реализации с WS это проще вынести в отдельный процесс, а reverse-proxy вроде nginx будет распознавать запросы к этому WS и перенаправлять их на этот процесс. Это снизит нагрузку на основное приложение и избавит от необходимости увеличивать количество потоков.

Что стоит изменить в будущем?

Добавить PubSub

На данный момент сервер проверяет статус задачи простым поллингом в цикле без каких либо задержек. Можно либо добавить Redis/Valkey как ещё один сервис, либо поддерживать только PostgreSQL и использовать его аналогичные способности. SQLAlchemy в теории поддерживает похожий функционал, но он не очень хорошо работает с multiprocessing, поэтому им не воспользовался.

Тесты

Я также не нашёл способ тестировать WS из pytest, поэтому тесты не добавил, но make coverage мне говорит, что /group/<int:gid>/variant/<int:vid>/task/<int:tid> в целом не покрыт тестами, так что считаю своё решение чуточку более обоснованным (:

Минимизация static/js/autoreload.js

Руками это не очень хорошо делать, поэтому надо придумать способ внедрить это в сборочную систему, который видимо сейчас тоже нет.

DolphinChips avatar Mar 31 '25 16:03 DolphinChips