Skip to content

Conversation

@DolphinChips
Copy link

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

dtaa.mp4

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

Фрагмент 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

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants