ЗАМЕР
bench/shutdown/practice.py
Скрипт, которым получены числа в статье, и запись прогона. Файл читается на сборке из репозитория — это тот самый код, который запускали, а не его копия.
- Цитируется в статье
- /ru/interview/sre/graceful-shutdown
- Как запустить
python3 bench/shutdown/drain.py > bench/shutdown/runs/drain.txt python3 bench/shutdown/practice.py > bench/shutdown/runs/practice.txt
Запись прогона
Замеры для урока «Graceful shutdown»
| Файл | Что делает |
|---|---|
drain.py |
настоящий сервер на loopback под нагрузкой из двадцати клиентов: сколько запросов обрывается при немедленном выходе по SIGTERM и при дренаже, и сколько занимает сама остановка |
practice.py |
ответы к задачам урока: число оборванных запросов в обоих режимах, код выхода и время остановки |
Запуск из корня репозитория:
python3 bench/shutdown/drain.py > bench/shutdown/runs/drain.txt
python3 bench/shutdown/practice.py > bench/shutdown/runs/practice.txt
Что воспроизводимо
Соотношение: без дренажа обрываются все запросы, что были в работе; с дренажем — ни одного. И то, что цена дренажа равна остатку уже начатой работы, а не льготному сроку.
Не воспроизводимо: миллисекунды остановки и выдержка обработки — её задаёт сам скрипт (300 мс).
Требования к среде
Только loopback. Сервер живёт в отдельном процессе, чтобы ему можно было послать сигнал так, как это делает оркестратор.
Числа сняты на CPython 3.11.15, Linux 6.18.44.
Скрипт
53 строк"""Практика к уроку про graceful shutdown: три ответа и одна цена.
ЗАЧЕМ ОТДЕЛЬНЫЙ ФАЙЛ. Задачи урока проверяются сборкой
(scripts/validate-practice.mjs): показанный читателю код обязан построчно быть
в скрипте, верный вариант — дословно встречаться в записи прогона, а
заявленное число — печататься самой программой.
ЧТО ЗДЕСЬ ПРОВЕРЯЕТСЯ. Одна и та же остановка под нагрузкой в двух режимах:
выход по `SIGTERM` немедленно и выход после дренажа. Задача — про то, сколько
запросов при этом обрывается, и про цену дренажа в миллисекундах.
Сервер и клиенты берутся из `drain.py`, чтобы задача и разбор урока стояли на
одном коде.
ЗАПУСК: python3 bench/shutdown/practice.py
Вывод: runs/practice.txt
"""
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from drain import CLIENTS, WORK_MS, run # noqa: E402
def main() -> None:
print(f"Python {sys.version.split()[0]} · Linux {os.uname().release} · loopback")
print()
# --- Часть 1: три ответа о поведении --------------------------------
abrupt = run("abrupt")
drained = run("drain")
print(abrupt["broken"])
print(drained["broken"])
print(abrupt["exit"])
# --- Часть 2: сколько стоит дренаж -----------------------------------
#
# ПОЧЕМУ ЭТО И ЕСТЬ ЦЕНА. Дренаж не «замедляет остановку вообще» — он
# ждёт ровно остаток работы, которая уже начата. Сигнал приходит на
# середине трёхсотмиллисекундной обработки, поэтому ожидание получается
# около половины от неё, а не льготный срок целиком.
print()
print(f"concurrent requests {CLIENTS}")
print(f"work per request, ms {WORK_MS}")
print(f"stop time, abrupt exit, ms {abrupt['stop_ms']:.1f}")
print(f"stop time, with draining, ms {drained['stop_ms']:.1f}")
if __name__ == "__main__":
main()