Commit 10befd0
authored
fix(vfs): surface real Hub error and map HTTP status to errno on write failure (#198)
## Problem
When a streaming (append-only) write fails, two diagnosability problems
stack up:
1. The streaming worker captures the real error from `writer.write()`,
but the `Finish` handler throws it away and returns a hardcoded
`Error::hub("streaming write failed")`. The log only shows a generic
message with no Hub status.
2. Every commit failure maps to a blanket `EIO`, so an importing client
(e.g. Radarr/Sonarr) cannot tell a quota/storage reject apart from a
real I/O error, and retries straight into the quota wall.
This surfaced during a real quota incident: the Hub rejected writes
(quota exceeded), but the log only said `Hub API error: streaming write
failed` with `errno=5`. The actual cause had to be guessed by hand, and
the importing app kept looping with file loss.
## Changes
- Keep the structured `Error` (not just its string) in the streaming
worker so the HTTP status survives, and propagate it on `Finish`.
- Preserve the HTTP status from CAS `ClientError` in
`From<xet_data::DataError>` instead of flattening it into an opaque
string. (Verified the upload error path keeps the structured
`DataError::ClientError`, whose `status()` exposes the code.)
- Add `Error::status()` and `Error::to_errno()`: map `413`/`507` to
`ENOSPC`, `403` to `EACCES`, `429` to `EAGAIN`, everything else stays
`EIO`.
- Return `e.to_errno()` from the streaming write/commit failure paths.
## Result
A quota reject now:
- logs the real `Hub API error (413): ...` instead of a generic string,
and
- surfaces as `ENOSPC` ("No space left on device") to the FUSE client,
so the importing app stops retrying into a quota wall instead of
looping.
## Design notes
- `413`/`507` map to `ENOSPC` rather than `EDQUOT`: `ENOSPC` is more
widely recognized by importing apps and clearer to operators. Happy to
switch to `EDQUOT` if preferred.
- Scope is limited to the streaming/append-only write path (the incident
path). The advanced-writes flush path (which already retries) and remote
deletes are untouched.
## Tests
- `error::tests` covers the status -> errno mapping (including
statusless and non-Hub errors staying `EIO`).
- `streaming_worker_surfaces_real_hub_error_on_failed_write` is a
regression test: a write failing with a `413` must surface the real
message, `status() == Some(413)`, and `to_errno() == ENOSPC`.
- Full suite green with `--features fuse,nfs` (349 passed); clippy clean
across all feature combos with `-D warnings`.1 parent 91c48f9 commit 10befd0
3 files changed
Lines changed: 122 additions & 11 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
26 | 49 | | |
27 | 50 | | |
28 | 51 | | |
| |||
63 | 86 | | |
64 | 87 | | |
65 | 88 | | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
66 | 97 | | |
67 | 98 | | |
68 | 99 | | |
| |||
78 | 109 | | |
79 | 110 | | |
80 | 111 | | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1120 | 1120 | | |
1121 | 1121 | | |
1122 | 1122 | | |
1123 | | - | |
| 1123 | + | |
1124 | 1124 | | |
1125 | 1125 | | |
1126 | 1126 | | |
| |||
2316 | 2316 | | |
2317 | 2317 | | |
2318 | 2318 | | |
2319 | | - | |
2320 | | - | |
| 2319 | + | |
| 2320 | + | |
2321 | 2321 | | |
2322 | 2322 | | |
2323 | 2323 | | |
| |||
2362 | 2362 | | |
2363 | 2363 | | |
2364 | 2364 | | |
2365 | | - | |
2366 | | - | |
| 2365 | + | |
| 2366 | + | |
2367 | 2367 | | |
2368 | 2368 | | |
2369 | 2369 | | |
| |||
2629 | 2629 | | |
2630 | 2630 | | |
2631 | 2631 | | |
2632 | | - | |
| 2632 | + | |
2633 | 2633 | | |
2634 | 2634 | | |
2635 | 2635 | | |
| |||
2665 | 2665 | | |
2666 | 2666 | | |
2667 | 2667 | | |
2668 | | - | |
| 2668 | + | |
2669 | 2669 | | |
2670 | 2670 | | |
2671 | 2671 | | |
| |||
3837 | 3837 | | |
3838 | 3838 | | |
3839 | 3839 | | |
3840 | | - | |
| 3840 | + | |
| 3841 | + | |
3841 | 3842 | | |
3842 | 3843 | | |
3843 | 3844 | | |
| |||
3904 | 3905 | | |
3905 | 3906 | | |
3906 | 3907 | | |
3907 | | - | |
| 3908 | + | |
3908 | 3909 | | |
3909 | 3910 | | |
3910 | 3911 | | |
| |||
3914 | 3915 | | |
3915 | 3916 | | |
3916 | 3917 | | |
3917 | | - | |
| 3918 | + | |
3918 | 3919 | | |
3919 | 3920 | | |
3920 | 3921 | | |
3921 | 3922 | | |
3922 | 3923 | | |
3923 | | - | |
| 3924 | + | |
| 3925 | + | |
| 3926 | + | |
| 3927 | + | |
| 3928 | + | |
| 3929 | + | |
| 3930 | + | |
| 3931 | + | |
| 3932 | + | |
3924 | 3933 | | |
3925 | 3934 | | |
3926 | 3935 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5418 | 5418 | | |
5419 | 5419 | | |
5420 | 5420 | | |
| 5421 | + | |
| 5422 | + | |
| 5423 | + | |
| 5424 | + | |
| 5425 | + | |
| 5426 | + | |
| 5427 | + | |
| 5428 | + | |
| 5429 | + | |
| 5430 | + | |
| 5431 | + | |
| 5432 | + | |
| 5433 | + | |
| 5434 | + | |
| 5435 | + | |
| 5436 | + | |
| 5437 | + | |
| 5438 | + | |
| 5439 | + | |
| 5440 | + | |
| 5441 | + | |
| 5442 | + | |
| 5443 | + | |
| 5444 | + | |
| 5445 | + | |
| 5446 | + | |
| 5447 | + | |
| 5448 | + | |
| 5449 | + | |
| 5450 | + | |
| 5451 | + | |
| 5452 | + | |
| 5453 | + | |
| 5454 | + | |
| 5455 | + | |
| 5456 | + | |
| 5457 | + | |
| 5458 | + | |
| 5459 | + | |
| 5460 | + | |
| 5461 | + | |
| 5462 | + | |
| 5463 | + | |
| 5464 | + | |
| 5465 | + | |
| 5466 | + | |
| 5467 | + | |
0 commit comments