Практикум: почему таймаут не заменяет дедлайн¶
Скрипты, на которых основаны измерения в статье.
Все серверы запускаются в том же процессе на loopback-интерфейсе; внешняя сеть нужна только для установки пакетов.
Из каталога практикума запустите скрипты с версиями, закреплёнными в requirements.txt:
uv run --no-project --python 3.13 --with-requirements requirements.txt 01_read_timeout.py
uv run --no-project --python 3.13 --with-requirements requirements.txt 02_retry_multiplies.py
uv run --no-project --python 3.13 --with-requirements requirements.txt 03_grpc_chain.py
uv run --no-project --python 3.13 --with-requirements requirements.txt 04_deadline_header.py
| Скрипт | Что измеряет |
|---|---|
01_read_timeout.py |
Поведение httpx с timeout=1.0: тело ответа поступает по частям четыре секунды; сервер задерживает заголовки; запрос выполняется внутри asyncio.timeout или с ограничением total в clientwright |
02_retry_multiplies.py |
Самописный цикл из трёх попыток обращения к серверу, который не отвечает, и те же попытки с единым общим лимитом времени |
03_grpc_chain.py |
Цепочку gateway → orders → inventory, billing: дедлайн на каждом сервере при отдельном таймауте каждого вызова и при передаче дедлайна шлюза, а также бюджет 50 с при настроенном лимите вызова 5 с |
04_deadline_header.py |
Значение X-Deadline-Ms, которое получает вышестоящий HTTP-сервис при каждой попытке, если у вызывающей стороны есть общий бюджет запроса |
Время и значения заголовков зависят от планирования задач и установки соединений. HTTPX-адаптер в clientwright 0.2.2 выбрасывает HttpxDeadlineExceededError, который наследуется от clientwright.DeadlineExceededError и httpx.TimeoutException.
В gRPC-примере asyncio.shield() моделирует работу, уже принятую внешней платёжной системой. Она намеренно продолжается после отмены обработчика; это не утверждение о поведении всех записей в БД. Передача бюджета предотвращает запуск имитации списания при недостатке времени, но не отменяет уже выполненный резерв товара.