[v3] Add dragonfly backend error handling and E2E tests - #2043
Merged
imeoer merged 3 commits intoAug 27, 2026
Conversation
Implements sophisticated error handling for Dragonfly backend with differentiated retry policies for prefetch vs on-demand reads. Adds configurable retry budgets, fallback throttling, and proper error classification. Prefetch reads retry up to 10 times before failing without fallback, while on-demand reads retry 3 times then fall back to origin registry. Rate limiting (429) triggers immediate fallback for on-demand reads and immediate failure for prefetch. Forbidden errors (403) are terminal for both read types. Adds fallback limiter to throttle consecutive origin requests with configurable interval. Implements proper error propagation with throttled marker for rate limiting scenarios. Updates telemetry metrics to track backend read attribution between proxy and origin. Signed-off-by: Peng Tao <bergwolf@hyper.sh>
bergwolf
requested review from
ClementMaH,
CooooolFrog,
EvanCley,
gaius-qi,
mingcheng and
yyzai384
and removed request for
a team
August 25, 2026 12:42
This commit adds comprehensive end-to-end testing infrastructure for Dragonfly integration, including GitHub Actions workflow, configuration files, and test implementations. The new E2E workflow tests both SDK-proxy and fallback modes with Dragonfly 2.4.3, validating nydus image distribution through the Dragonfly P2P network. Configuration files support multiple test scenarios including strict and fallback modes for both proxy and SDK integration patterns. Registry backend is extended with Dragonfly support to enable P2P-based image distribution, improving scalability and reducing registry load for large-scale deployments. Signed-off-by: Peng Tao <bergwolf@hyper.sh>
bergwolf
force-pushed
the
dragonfly-backend-error-handling
branch
from
August 25, 2026 13:19
092dc33 to
d209e73
Compare
There was a problem hiding this comment.
Pull request overview
Adds Dragonfly-aware retry, fallback, throttling, telemetry, and E2E coverage for registry reads.
Changes:
- Differentiates prefetch and on-demand Dragonfly failure policies.
- Adds delayed prefetch rescheduling and fallback telemetry.
- Introduces Dragonfly service configurations and CI workflows.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
.github/workflows/e2e-dragonfly.yml |
Adds Dragonfly E2E CI jobs. |
docs/nydus.md |
Documents policies, configuration, and metrics. |
misc/dragonfly/dfdaemon.yaml |
Configures the test dfdaemon. |
misc/dragonfly/manager.yaml |
Configures the Dragonfly manager. |
misc/dragonfly/scheduler.yaml |
Configures the Dragonfly scheduler. |
misc/dragonfly/nydus-sdk-fallback.yaml |
Defines SDK fallback mode. |
misc/dragonfly/nydus-sdk-strict.yaml |
Defines SDK strict mode. |
misc/dragonfly/nydus-proxy-error-fallback.yaml |
Configures fallback error tests. |
misc/dragonfly/nydus-proxy-error-strict.yaml |
Configures strict error tests. |
nydus-backend/src/lib.rs |
Adds throttling markers and read attribution. |
nydus-backend/src/registry/dragonfly.rs |
Classifies Dragonfly SDK failures. |
nydus-backend/src/registry/http.rs |
Exposes a non-retrying HTTP client. |
nydus-backend/src/registry/mod.rs |
Implements retry and fallback policy. |
nydus-config/src/lib.rs |
Adds policy and rescheduling settings. |
nydus-core/src/lib.rs |
Manages the prefetch stop flag. |
nydus-storage/src/prefetch.rs |
Reschedules throttled prefetches. |
nydus-telemetry/src/metrics.rs |
Adds policy and fallback metrics. |
nydus/src/bin/nydus/fuse.rs |
Passes prefetch settings and stops workers. |
tests/e2e/dragonfly_test.go |
Adds Dragonfly integration tests. |
tests/e2e/fanotify_test.go |
Uses the shared environment helper. |
tests/e2e/harness.go |
Adds the shared environment helper. |
tests/e2e/uffd_fault_test.go |
Prevents GC deadlocks during UFFD tests. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+298
to
+304
| postInject(t, http.StatusTooManyRequests, 1) | ||
| defer clearInject(t) | ||
| wipeCacheDir(env.cacheDir) | ||
| cleanup := env.startFuse(t) | ||
| defer cleanup() | ||
| _, data, err := readMountedFile(t, env.mountpoint) | ||
| require.NoError(t, err) |
bergwolf
force-pushed
the
dragonfly-backend-error-handling
branch
from
August 26, 2026 13:25
6ae619c to
541077f
Compare
Add proper error classification for mid-stream failures and improve backend attribution for CRC validation errors. The changes introduce retry logic for Dragonfly stream failures with proper fallback mechanisms when retry budget is exhausted. New telemetry metrics track CRC errors by backend target (origin/proxy) and Dragonfly stream failures. The attribution system now correctly assigns validation failures to the appropriate backend without requiring active read operations. Enhanced test coverage includes scenarios for mid-stream failures, retry exhaustion, and proper error attribution across the storage stack. Signed-off-by: Peng Tao <bergwolf@hyper.sh>
bergwolf
force-pushed
the
dragonfly-backend-error-handling
branch
from
August 26, 2026 13:28
541077f to
164ffc0
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 23 out of 23 changed files in this pull request and generated 4 comments.
Suppressed comments (6)
Previously missed (5) — in code that hasn't changed since the last review.
nydus/src/bin/nydus/fuse.rs:340
- The stop flag is raised only when
session.serve()returnsOk; any outer error from signal setup, controller-thread creation, or result-channel receive exits via?first and leaves the detached prefetcher rescheduling indefinitely. Capture the serve result, raise the flag on both paths, and only then propagate the error.
// The filesystem is unmounted: wind down the detached prefetch thread
// (it may be sleeping towards a rescheduled throttled-blob retry).
if let Some(stop) = prefetch_stop {
stop.store(true, std::sync::atomic::Ordering::Relaxed);
}
nydus-config/src/lib.rs:196
max_retriesis a retry budget in addition to the initial attempt: the fallback loop stops whenattempts > max_retries, and the test withmax_retries: 1expects two origin requests. The current “3 attempts” wording is off by one; the default permits four total attempts.
/// The maximum number of retry attempts per request, applied by the HTTP
/// client's retry middleware on direct origin requests — including origin
/// requests issued as Dragonfly fallbacks, so the default of 3 is what
/// bounds "origin failing 3 attempts" before a fallback read errors out.
docs/nydus.md:826
- This table describes
http.max_retriesas the total number of failed origin attempts, but the implementation performs one initial attempt plus that many retries. Clarify the total to avoid understating origin traffic and fallback latency.
| Proxy `429` | No retry, no origin fallback; the blob's prefetch fails and is rescheduled after a random `prefetch.retry_delay_min`–`prefetch.retry_delay_max` delay | No Dragonfly retry; fall back to the origin through the fallback throttle; the origin failing `http.max_retries` attempts → IO error |
nydus-config/src/lib.rs:308
- The canonical
config/registry.example.yamlis now stale: it still sayshttp.max_retriescontrols Dragonfly SDK retries, says all Dragonfly failures fall back, and does not expose any of these new policy fields. Update that example with the differentiated policy and configurable budgets so users are not guided to ineffective settings.
#[serde(
default = "default_dragonfly_fallback_interval",
with = "humantime_serde"
)]
pub fallback_interval: Duration,
.github/workflows/e2e-dragonfly.yml:368
- The newly added
misc/dragonfly/nydus-proxy-error-strict.yamlfixture is never referenced; this job always supplies only the fallback fixture. Add the strict fixture to a matrix/run if it is intended coverage, or remove the dead configuration.
NYDUS_CONFIG=${{ github.workspace }}/misc/dragonfly/nydus-sdk-fallback.yaml \
NYDUS_CACHE_DIR=/tmp/nydus-dragonfly/cache-proxy-fallback \
NYDUS_PROXY_ERROR_CONFIG=${{ github.workspace }}/misc/dragonfly/nydus-proxy-error-fallback.yaml \
nydus-backend/src/registry/mod.rs:705
- A fallback earlier in the same logical read sets
READ_SERVED_BYtoOrigin, but a subsequent Dragonfly success (for example during a 401 auth handshake or after evicting an expired redirect) does not restore it. The metered read and any CRC error are then incorrectly attributed to origin even though Dragonfly supplied the final bytes. RecordProxyon this success path.
let err = match self.request_dragonfly(dragonfly, url, headers.clone(), context) {
Ok(response) => return Ok(response),
Comment on lines
+413
to
+417
| .and_then(|v| v.parse::<usize>().ok()) | ||
| .unwrap_or(0); | ||
| let body = runtime().block_on(async { | ||
| let mut body = Vec::with_capacity(capacity); | ||
| self.reader.read_to_end(&mut body).await?; |
Comment on lines
+904
to
+906
| - `backend_dragonfly_read_errors{class,kind}` — Dragonfly read failures by | ||
| failure class (`rate_limited`, `forbidden`, `timeout`, `connect`, | ||
| `server_error`, `other`) and read kind (`ondemand`, `prefetch`). |
| require.Error(t, err) | ||
| }) | ||
|
|
||
| t.Run("status_429_ondemand_fallback", func(t *testing.T) { |
Comment on lines
+398
to
+403
| "Origin requests issued as Dragonfly fallbacks", | ||
| ), | ||
| backend_fallback_read_errors: counter( | ||
| ®istry, | ||
| "backend_fallback_read_errors", | ||
| "Failed origin requests issued as Dragonfly fallbacks", |
imeoer
approved these changes
Aug 27, 2026
imeoer
approved these changes
Aug 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR implements proper error handling for Dragonfly backend with differentiated retry policies for prefetch vs on-demand reads. Adds configurable retry budgets, fallback throttling, and proper error classification, plus a new E2E test workflow to validate the changes.
Prefetch reads retry up to 10 times before failing without fallback, while on-demand reads retry 3 times then fall back to origin registry. Rate limiting (429) triggers immediate fallback for on-demand reads and immediate failure for prefetch. Forbidden errors (403) are terminal for both read types.
Adds fallback limiter to throttle consecutive origin requests with configurable interval. Implements proper error propagation with throttled marker for rate limiting scenarios. Updates telemetry metrics to track backend read attribution between proxy and origin.
Example workflow run: https://github.com/bergwolf/nydus/actions/runs/32847782111