Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
57c102a
feat(backend-common): withdraw and recover workers on engine loss
JulienDarve Oct 30, 2026
18f7644
feat(sidecar): withdraw and reconnect vLLM workers on engine loss
JulienDarve Oct 30, 2026
01d32b2
test(sidecar): move vLLM engine-loss tests into the testkit
JulienDarve Oct 30, 2026
e929de3
fix(backend-common): stop engine recovery when the endpoint stops
JulienDarve Oct 31, 2026
94ee53b
Merge branch 'jdarve/dis-3174-engine-loss-hooks' into jdarve/dis-3175…
JulienDarve Oct 31, 2026
e180795
fix(sidecar): connect to a sleeping vLLM as not ready
JulienDarve Oct 31, 2026
5a8e13c
Merge branch 'main' into jdarve/dis-3174-engine-loss-hooks
JulienDarve Nov 2, 2026
892dc3a
fix(backend-common): retry failed endpoint syncs from the serve loop
JulienDarve Nov 2, 2026
0ac0ffb
Merge branch 'jdarve/dis-3174-engine-loss-hooks' into jdarve/dis-3175…
JulienDarve Nov 2, 2026
7d1c105
refactor(sidecar): drop the unused reconnect flag from wait_for_services
JulienDarve Nov 2, 2026
de387db
Merge remote-tracking branch 'origin/main' into jdarve/dis-3174-engin…
JulienDarve Nov 3, 2026
ad72c7e
Merge branch 'jdarve/dis-3174-engine-loss-hooks' into jdarve/dis-3175…
JulienDarve Nov 3, 2026
aac9783
Merge remote-tracking branch 'origin/main' into jdarve/dis-3174-engin…
JulienDarve Nov 3, 2026
a68fccb
feat(backend-common): drain and exit the worker when its engine is lost
JulienDarve Nov 3, 2026
fcd4021
Merge branch 'jdarve/dis-3174-engine-loss-hooks' into jdarve/dis-3175…
JulienDarve Nov 3, 2026
963c7fa
feat(sidecar): restart the vLLM sidecar when vLLM stops serving
JulienDarve Nov 3, 2026
f41eed4
fix(sidecar): watch vLLM's overall health instead of vllm.Inference
JulienDarve Nov 3, 2026
7629691
docs(backend-common): state when the Worker polls and drops engine_lost
JulienDarve Nov 3, 2026
5e63927
Merge remote-tracking branch 'origin/jdarve/dis-3174-engine-loss-hook…
JulienDarve Nov 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion lib/backend-common/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ parse args, start engine, wire Prometheus serve requests drain-poll sh
return engine return metadata (optional) (concurrent) predicate release
```

The trait has twelve methods. `from_args` is NOT on the trait — each
The trait has seventeen methods. `from_args` is NOT on the trait — each
backend exposes a backend-specific constructor (typically a sync
`from_args(argv) -> Result<(Self, WorkerConfig)>` inherent method).
This keeps the trait fully object-safe without a `where Self: Sized`
Expand Down Expand Up @@ -123,6 +123,20 @@ opt-out and lets `run.rs` stay non-generic.
startup**. Engines that publish their own discovery records stash it
(e.g. vLLM dynamic LoRA via `register_model` / `unregister_model`).
Mirrors the `on_publisher_ready` handoff idiom.
- `availability()` / `recover()` / `on_recovered()` — optional, for
engines that can fail while the worker lives (e.g. a sidecar's separate
engine process). `availability()` returns a `watch` of `Ready` /
`NotReady` / `Lost`; the default `None` keeps today's behavior. On
`NotReady` the Worker withdraws the endpoint; on `Lost` it also
withdraws the model card, calls `recover()`, registers the returned
metadata with the caller's taints, then calls `on_recovered()` so the
engine can restore records that need the card (e.g. LoRA adapters). It
re-registers the endpoint only while the engine is `Ready` and no
operator pause is in effect. In-flight requests are
never cancelled, and a `recover()` error stops the worker. Discovery
writes for pause, resume, taints, engine state and shutdown go through
one lock and are never cancelled mid-write; endpoint writes are skipped
when discovery already matches.

## Contract for `generate`

Expand Down
42 changes: 41 additions & 1 deletion lib/backend-common/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use async_trait::async_trait;
use futures::stream::BoxStream;
use tokio::sync::watch;

use crate::error::DynamoError;
use crate::error::{BackendError, DynamoError, ErrorType};

pub use dynamo_llm::first_token::FirstTokenNotifier;
pub use dynamo_llm::kv_router::publisher::KvEventPublisher;
Expand Down Expand Up @@ -184,6 +184,17 @@ pub struct EngineConfig {
pub llm: Option<LlmRegistration>,
}

/// Whether the engine behind an [`LLMEngine`] can serve, as reported by
/// [`LLMEngine::availability`].
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum EngineAvailability {
Ready,
/// Alive but not accepting work, for example while paused or asleep.
NotReady,
/// Gone or unreachable until [`LLMEngine::recover`] reconnects.
Lost,
}

/// Inference engine trait.
///
/// Lifecycle:
Expand Down Expand Up @@ -422,6 +433,35 @@ pub trait LLMEngine: Send + Sync + 'static {
) -> Result<(), DynamoError> {
Ok(())
}

/// Live availability of an engine that can fail independently of the
/// worker. `None` (default) keeps the worker registered until shutdown.
/// On `NotReady` the worker withdraws its endpoint; on `Lost` it also
/// withdraws the model card and calls [`recover`](LLMEngine::recover).
/// `generate` must reject new requests while `Lost`.
fn availability(&self) -> Option<watch::Receiver<EngineAvailability>> {
None
}

/// Reconnect after [`EngineAvailability::Lost`] and return fresh
/// registration metadata once the engine serves again, with availability
/// no longer `Lost`. Runs while the worker is out of discovery and is
/// dropped if the worker shuts down; in-flight requests are not cancelled.
/// An error stops the worker, so the engine bounds its own retries. KV event
/// sources and metrics bindings from `start` are kept.
async fn recover(&self) -> Result<EngineConfig, DynamoError> {
Err(DynamoError::builder()
.error_type(ErrorType::Backend(BackendError::EngineShutdown))
.message("engine does not support recovery")
.build())
}

/// Called after recovery re-registers the model card and before the
/// endpoint is routable again, to restore discovery records that depend on
/// the card (for example LoRA adapters). An error is logged.
async fn on_recovered(&self) -> Result<(), DynamoError> {
Ok(())
}
}

/// Raw media-generation engine trait — the non-token sibling of [`LLMEngine`].
Expand Down
12 changes: 6 additions & 6 deletions lib/backend-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ pub use args::CommonArgs;
pub use disagg::DisaggregationMode;
pub use dynamo_llm::model_type::ModelInput;
pub use engine::{
AsyncEngineContext, BootstrapInfo, CompletionUsage, ComponentSnapshot, EngineConfig,
FinishReason, FirstTokenNotifier, GenerateContext, GuidedDecodingOptions, HEALTH_CHECK_KEY,
KV_HINT_TRANSFER_CAPABILITY_KEY, KvEventPublisher, KvEventSource, KvHint, KvHintAction,
KvSourceLocationsPayload, LLMEngine, LLMEngineOutput, LLMEngineOutputExt, LlmRegistration,
LogProbs, Metrics, MetricsBindings, MetricsCtx, MultimodalData, OnPublisherReady,
OnSnapshotPublisherReady, OutputOptions, PrefillResult, PreprocessedRequest,
AsyncEngineContext, BootstrapInfo, CompletionUsage, ComponentSnapshot, EngineAvailability,
EngineConfig, FinishReason, FirstTokenNotifier, GenerateContext, GuidedDecodingOptions,
HEALTH_CHECK_KEY, KV_HINT_TRANSFER_CAPABILITY_KEY, KvEventPublisher, KvEventSource, KvHint,
KvHintAction, KvSourceLocationsPayload, LLMEngine, LLMEngineOutput, LLMEngineOutputExt,
LlmRegistration, LogProbs, Metrics, MetricsBindings, MetricsCtx, MultimodalData,
OnPublisherReady, OnSnapshotPublisherReady, OutputOptions, PrefillResult, PreprocessedRequest,
PromptTokensDetails, RawEngine, SamplingOptions, StopConditions, StopReason, TopLogprob,
TopLogprobs, chunk, usage,
};
Expand Down
Loading
Loading