fix(delivery): stop the 422 retry loop on failed delivery items
Deploy / deploy (push) Successful in 6m37s
Deploy / deploy (push) Successful in 6m37s
A FAILED delivery item never sets fileCount/totalBytes, and the worker's Dictionary-based JSON payload serializes those as explicit nulls (the WhenWritingNull option only suppresses nulls on POCO properties, not dictionary values). The server's schema allowed the fields to be omitted but not null, so the report was permanently rejected with 422 - and the worker only ever treated 409/404 as terminal, so it retried the same malformed payload forever across restarts.
This commit is contained in:
@@ -128,11 +128,15 @@ public sealed class ApiClient
|
|||||||
};
|
};
|
||||||
var res = await _http.PostAsJsonAsync(path, payload, Json, ct);
|
var res = await _http.PostAsJsonAsync(path, payload, Json, ct);
|
||||||
if (res.IsSuccessStatusCode) return true;
|
if (res.IsSuccessStatusCode) return true;
|
||||||
if (res.StatusCode is HttpStatusCode.Conflict or HttpStatusCode.NotFound)
|
if (res.StatusCode is HttpStatusCode.Conflict or HttpStatusCode.NotFound or HttpStatusCode.UnprocessableEntity)
|
||||||
{
|
{
|
||||||
|
// Conflict/NotFound: contradictory or vanished report — nothing to retry.
|
||||||
|
// UnprocessableEntity: the server rejected the payload shape itself, so
|
||||||
|
// resending the same bytes can only ever produce the same 422 — retrying
|
||||||
|
// just spams the log forever instead of surfacing the real problem.
|
||||||
_log.LogWarning("Server permanently rejected {Kind} for job {JobId}: {Status} {Body}",
|
_log.LogWarning("Server permanently rejected {Kind} for job {JobId}: {Status} {Body}",
|
||||||
kind, jobId, (int)res.StatusCode, await res.Content.ReadAsStringAsync(ct));
|
kind, jobId, (int)res.StatusCode, await res.Content.ReadAsStringAsync(ct));
|
||||||
return true; // don't retry contradictory/vanished reports
|
return true;
|
||||||
}
|
}
|
||||||
_log.LogWarning("Lifecycle report {Kind} for {JobId} failed with {Status}; will retry", kind, jobId, (int)res.StatusCode);
|
_log.LogWarning("Lifecycle report {Kind} for {JobId} failed with {Status}; will retry", kind, jobId, (int)res.StatusCode);
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -437,8 +437,8 @@ export const deliveryReportSchema = z.object({
|
|||||||
status: z.enum(["COPIED", "VERIFIED", "FAILED"]),
|
status: z.enum(["COPIED", "VERIFIED", "FAILED"]),
|
||||||
exrPath: z.string().max(1000).optional().nullable(),
|
exrPath: z.string().max(1000).optional().nullable(),
|
||||||
movPath: z.string().max(1000).optional().nullable(),
|
movPath: z.string().max(1000).optional().nullable(),
|
||||||
fileCount: z.number().int().nonnegative().optional(),
|
fileCount: z.number().int().nonnegative().optional().nullable(),
|
||||||
totalBytes: z.number().nonnegative().optional(),
|
totalBytes: z.number().nonnegative().optional().nullable(),
|
||||||
checksum: z.string().max(200).optional().nullable(),
|
checksum: z.string().max(200).optional().nullable(),
|
||||||
linkMode: z.enum(["hardlink", "copy"]).optional().nullable(),
|
linkMode: z.enum(["hardlink", "copy"]).optional().nullable(),
|
||||||
errorMessage: z.string().max(2000).optional().nullable(),
|
errorMessage: z.string().max(2000).optional().nullable(),
|
||||||
|
|||||||
Reference in New Issue
Block a user