Перейти к основному содержимому

For clean Markdown content of this page, append .md to this URL. For the complete documentation index, see https://docs.nvidia.com/dynamo/llms.txt. For full content including API reference and SDK examples, see https://docs.nvidia.com/dynamo/llms-full.txt.

Отмена запроса

Этот документ описывает, как Dynamo реализует отмену запросов, чтобы отменять выполняющиеся запросы между worker-ами Dynamo. Отмена запроса позволяет преждевременно завершать выполняющиеся запросы, экономя вычислительные ресурсы, которые иначе были бы потрачены на ответы, которые больше не нужны.

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

Обнаружение отмены на frontend

Frontend отслеживает каждое соединение клиента на предмет неожиданных разрывов. Когда клиент отключается до того, как ответ полностью доставлен, frontend обнаруживает это и инициирует отмену. Это покрывает два сценария:

  1. Соединение неожиданно закрыто — клиент отключается во время обработки запроса до начала потоковой передачи ответа.
  2. Поток неожиданно закрыт — клиент отключается, пока активный SSE-поток передает токены ответа.

В обоих случаях frontend отменяет AsyncEngineContext запроса, а это распространяет отмену на любые связанные дочерние контексты на downstream worker-ах.

Обнаружение отмены на worker

На стороне worker runtime отслеживает TCP-соединение от frontend на предмет сигналов отмены. Worker обнаруживает отмену в трех сценариях:

  1. Получено управляющее сообщение — frontend явно отправил управляющее сообщение об отмене.
  2. TCP-соединение разорвано — frontend отключился, не отправив управляющее сообщение (например, из-за сбоя frontend или сетевой ошибки).

Когда worker получает сигнал отмены, он устанавливает соответствующее состояние в AsyncEngineContext запроса. После этого реализация engine на стороне worker должна сама заметить отмену (например, через проверку is_stopped()) и завершить обработку соответствующим образом. Подробности реализации обработки отмены в backend worker см. в Руководстве по разработке backend.

Распространение отмены

Отмена распространяется по многоуровневым цепочкам запросов через связанные объекты AsyncEngineContext. Когда родительский контекст отменяется, все связанные дочерние контексты также автоматически отменяются. Это гарантирует, что когда клиент отменяет запрос на frontend, все связанные подзапросы на downstream worker-ах тоже автоматически отменяются, экономя вычислительные ресурсы во всей цепочке обработки запроса.

Метрики

Dynamo предоставляет метрики Prometheus для мониторинга отмен запросов как на уровне frontend, так и на уровне runtime.

Метрика frontend

MetricTypeDescription
dynamo_frontend_model_cancellation_totalCounterОбщее число отмен запросов, обнаруженных frontend

Метки

LabelDescriptionExample Values
modelИмя модели из запросаQwen/Qwen3-0.6B
endpointAPI endpoint, который получил запросcompletions, chat_completions, embeddings, images, videos, audios, responses, anthropic_messages, tensor
request_typeЯвляется ли запрос unary или streamingunary, stream

Endpoint: Доступна в HTTP-сервисе frontend по адресу /metrics.

Метрика runtime

MetricTypeDescription
dynamo_component_cancellation_totalCounterОбщее число запросов, отмененных обработчиком work

Эта метрика использует автоматически подставляемые Dynamo метки component:

LabelDescriptionExample Values
dynamo_namespaceПространство имен Dynamodynamo
dynamo_componentКомпонент, который обработал запросbackend, prefill, decode
dynamo_endpointEndpoint внутри компонентаgenerate

Счетчик использует логику дедупликации, чтобы каждый отмененный запрос учитывался только один раз, даже если для одного и того же запроса были обнаружены и управляющее сообщение, и закрытие сокета.

Обратите внимание, что эта метрика фиксирует сигналы отмены, полученные worker-ом, а не факт того, что запрос действительно был прерван на уровне engine. Реализация engine на стороне worker должна сама заметить отмену (например, через проверку is_stopped()) и завершить обработку соответствующим образом.

Endpoint: Доступна на порту системных метрик worker по адресу /metrics (обычно порт 9100).

Пример вывода метрик

Метрики frontend (из /metrics HTTP-сервиса frontend):

dynamo_frontend_model_cancellation_total{endpoint="chat_completions",model="Qwen/Qwen3-0.6B",request_type="stream"} 5
dynamo_frontend_model_cancellation_total{endpoint="chat_completions",model="Qwen/Qwen3-0.6B",request_type="unary"} 1
dynamo_frontend_model_cancellation_total{endpoint="completions",model="Qwen/Qwen3-0.6B",request_type="stream"} 2

Метрики runtime (из /metrics на системном порту worker):

dynamo_component_cancellation_total{dynamo_component="backend",dynamo_endpoint="generate",dynamo_namespace="dynamo"} 8

Трейт AsyncEngineContext

В основе системы отмены запросов Dynamo лежит трейт AsyncEngineContext. Он связан с каждым потоком запроса и обеспечивает управление жизненным циклом async-операций, включая идентификацию потока, возможности корректного завершения и возможности немедленного завершения.

Основные методы

Идентификация

  • id(): Возвращает уникальный идентификатор потока. Этот ID задается пользователем для идентификации запроса, и тот же ID можно использовать для подзапросов, чтобы связать их с исходным пользовательским запросом.

Проверка состояния

  • is_stopped(): Возвращает true, если через stop_generating() была запрошена корректная отмена. Это сигнал worker-у о том, что запрос отменен и следует завершить работу раньше.
  • is_killed(): Возвращает true, если через kill() был выполнен жесткий останов. Обычно это означает, что сетевое соединение между клиентом и сервером разорвано либо требуется немедленное завершение.

Асинхронный мониторинг состояния

  • stopped(): Async-метод, который завершается, когда контекст становится stopped. Если он уже остановлен, возвращает результат сразу.
  • killed(): Async-метод, который завершается, когда контекст становится killed. Если он уже убит, возвращает результат сразу.

Управление отменой

  • stop_generating(): Рекомендуемый способ отмены запроса. Он сообщает engine, что нужно корректно остановить генерацию результатов для потока. Этот метод идемпотентен и не делает недействительными результаты, уже находящиеся в потоке.
  • stop(): Псевдоним stop_generating().
  • kill(): Расширяет stop_generating(), но дополнительно указывает предпочтение завершить работу без выгрузки оставшихся элементов из потока. Это зависит от реализации и может поддерживаться не всеми engine.

Управление дочерними запросами

  • link_child(child: Arc<dyn AsyncEngineContext>): Связывает дочерний AsyncEngineContext с этим контекстом. Когда для родительского контекста вызывается stop_generating(), stop() или kill(), тот же метод автоматически вызывается для всех связанных дочерних контекстов в порядке их связывания. Это особенно полезно в сценариях disaggregated serving, где frontend получает уведомление об отмене и должен отменить запросы к worker-ам, а worker затем может отменить свои подзапросы (например, удаленные prefill-операции).

Потокобезопасность

Трейт AsyncEngineContext обеспечивает потокобезопасность через ограничения Send + Sync, что позволяет безопасный параллельный доступ из нескольких потоков и async-задач.

Python bindings

Функциональность AsyncEngineContext доступна в Python через класс Context, который в основном обеспечивает соответствие один-к-одному между методами Rust и Python.

Класс Python Context

Класс Python Context оборачивает Rust AsyncEngineContext и предоставляет следующие методы:

  • id(): Возвращает уникальный идентификатор контекста
  • is_stopped(): Синхронный метод, эквивалентный Rust is_stopped()
  • is_killed(): Синхронный метод, эквивалентный Rust is_killed()
  • stop_generating(): Отправляет сигнал stop generating, эквивалентный Rust-методу
  • async_killed_or_stopped(): Async-метод, который завершается, когда контекст становится либо killed, либо stopped, в зависимости от того, что произойдет первым. Он объединяет функциональность Rust async-методов killed() и stopped() с помощью tokio::select!.

Рабочий пример отмены запроса см. в демонстрации отмены.

Использование контекста в Python

Контекст доступен опционально как для входящих, так и для исходящих запросов:

Входящие запросы

Для входящих запросов метод generate может дополнительно принимать аргумент context после аргумента request. Если параметр context указан в сигнатуре метода, он получит объект контекста входящего запроса. Обработчики запросов могут:

  • Проверять отмену синхронно с помощью context.is_stopped() перед началом дорогих операций
  • Ожидать отмену асинхронно с помощью await context.async_killed_or_stopped()

Пример:

async def generate(self, request, context):
for i in range(1000):
# Проверяем отмену перед дорогой работой
if context.is_stopped():
raise asyncio.CancelledError

# Выполняем работу...
await expensive_computation()
yield result

Исходящие запросы

Для исходящих запросов Python-скрипты могут дополнительно передавать объект контекста в операции client router для исходящих runtime endpoint (например, методы generate, round_robin, random, direct) как именованный аргумент. Скрипт может отменить исходящий запрос через переданный объект контекста.

Это особенно полезно, когда дочерние исходящие запросы нужно отменять при отмене родительского входящего запроса. В таких случаях скрипт может просто передать объект входящего контекста в исходящий запрос, автоматически связывая поведение отмены.

Пример:

async def generate(self, request, context):
# Передаем входящий контекст в исходящий запрос
# Если входящий запрос отменен, исходящий тоже будет отменен
stream = await self.client.generate(request, context=context)
async for response in stream:
yield response