The binary make install builds retries a refused or unresolvable connection (closes #206)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
Bun's `fetch`, which `~/bin/quak` runs under, rejects with a plain `Error` that has no `cause` and a code of bun's own. Running the built binary against local listeners gave `ConnectionRefused` for a refused connection and for a failed TLS handshake, and `ConnectionRefused` or `FailedToOpenSocket` for a name that does not resolve. Both codes join `TRANSPORT_CODES` and `CONNECT_CODES` in `src/retry.ts`, so they are retried, replayed, and called transient by `classify`. A reset already came as `ECONNRESET` and a deadline as a `TimeoutError`; the tests pin every shape found. Judgement call: a failed TLS handshake is replayed, as no request byte was sent. Not changed: a certificate bun rejects is not retried. Model: opus-5-5
This commit is contained in:
@@ -724,6 +724,50 @@ describe("quak backup", () => {
|
||||
},
|
||||
);
|
||||
|
||||
// The download of toast.mp4 fails as bun's `fetch` reports a failure, in
|
||||
// the binary `make install` builds: a plain `Error` with bun's own code
|
||||
// and message, and no cause. These are what bun gave for a refused
|
||||
// connection, a name that did not resolve and a reset connection. The
|
||||
// file is tried as often as under Node, and goes into `failures.json`
|
||||
// with that message, as a transient failure.
|
||||
it.each([
|
||||
[
|
||||
"ConnectionRefused",
|
||||
"Unable to connect. Is the computer able to access the url?",
|
||||
],
|
||||
["FailedToOpenSocket", "Was there a typo in the url or port?"],
|
||||
[
|
||||
"ECONNRESET",
|
||||
"The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()",
|
||||
],
|
||||
])(
|
||||
"records a download that failed with bun's %s in failures.json, as transient",
|
||||
async (bunCode, message) => {
|
||||
let attempts = 0;
|
||||
const fetch: typeof globalThis.fetch = async (input, init) => {
|
||||
if (downloadOf(input) === 200) {
|
||||
attempts++;
|
||||
throw Object.assign(new Error(message), {
|
||||
code: bunCode,
|
||||
errno: 0,
|
||||
});
|
||||
}
|
||||
return server(input, init);
|
||||
};
|
||||
const dir = mkdtempSync(join(root, "backup-"));
|
||||
|
||||
const { code } = await backup(dir, {}, fetch);
|
||||
|
||||
expect(code).toBe(1);
|
||||
expect(attempts).toBe(UNATTENDED_RETRY_OPTIONS.attempts);
|
||||
const failures = readJSON(join(dir, "failures.json"));
|
||||
expect(failures.files["200"]).toMatchObject({
|
||||
classification: "transient",
|
||||
error: message,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// The name of Ente's API server does not resolve when quak fetches the ML
|
||||
// data, after every original has been backed up. No file has ML data
|
||||
// cached yet, so each goes into `failures.json` with the reason, as a
|
||||
|
||||
Reference in New Issue
Block a user