Deep Engineering

ЗАМЕР

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()