mirror of
https://github.com/MichMich/MagicMirror.git
synced 2026-06-09 03:34:51 +00:00
fix(http-fetcher): fall back to reloadInterval after retries exhausted (#4113)
As reported in #4109, the weather module retries much more frequently than expected after network errors. #4092 already fixed the main cause (duplicate fetchers), but the backoff logic in `HTTPFetcher` still has a gap: once retries are exhausted, `calculateBackoffDelay` keeps returning a short fixed delay (60s) instead of falling back to `reloadInterval`. The same problem existed for 5xx errors, where the delay grew to 8× the configured interval. Inspired by #4110 (thanks @CodeLine9), this PR makes both error paths fall back to `reloadInterval` after retries are exhausted. I also simplified the catch block, extracted a `#shortenUrl()` helper for log messages, and added tests for the backoff progression.
This commit is contained in:
committed by
GitHub
parent
3f2a0302eb
commit
7e1286257c
@@ -469,3 +469,83 @@ describe("selfSignedCert dispatcher", () => {
|
||||
expect(options.dispatcher).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Retry exhaustion fallback", () => {
|
||||
it("should fall back to reloadInterval after network retries exhausted", async () => {
|
||||
server.use(
|
||||
http.get(TEST_URL, () => {
|
||||
return HttpResponse.error();
|
||||
})
|
||||
);
|
||||
|
||||
fetcher = new HTTPFetcher(TEST_URL, { reloadInterval: 300000, maxRetries: 3 });
|
||||
|
||||
const errors = [];
|
||||
fetcher.on("error", (errorInfo) => errors.push(errorInfo));
|
||||
|
||||
// Trigger maxRetries + 1 fetches to reach exhaustion
|
||||
for (let i = 0; i < 4; i++) {
|
||||
await fetcher.fetch();
|
||||
}
|
||||
|
||||
// First retries should use backoff (< reloadInterval)
|
||||
expect(errors[0].retryAfter).toBe(15000);
|
||||
expect(errors[1].retryAfter).toBe(30000);
|
||||
// Third retry hits maxRetries, should fall back to reloadInterval
|
||||
expect(errors[2].retryAfter).toBe(300000);
|
||||
// Subsequent errors stay at reloadInterval
|
||||
expect(errors[3].retryAfter).toBe(300000);
|
||||
});
|
||||
|
||||
it("should fall back to reloadInterval after server error retries exhausted", async () => {
|
||||
server.use(
|
||||
http.get(TEST_URL, () => {
|
||||
return new HttpResponse(null, { status: 503 });
|
||||
})
|
||||
);
|
||||
|
||||
fetcher = new HTTPFetcher(TEST_URL, { reloadInterval: 300000, maxRetries: 3 });
|
||||
|
||||
const errors = [];
|
||||
fetcher.on("error", (errorInfo) => errors.push(errorInfo));
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
await fetcher.fetch();
|
||||
}
|
||||
|
||||
// First retries should use backoff (< reloadInterval)
|
||||
expect(errors[0].retryAfter).toBe(15000);
|
||||
expect(errors[1].retryAfter).toBe(30000);
|
||||
// Third retry hits maxRetries, should fall back to reloadInterval
|
||||
expect(errors[2].retryAfter).toBe(300000);
|
||||
// Subsequent errors stay at reloadInterval
|
||||
expect(errors[3].retryAfter).toBe(300000);
|
||||
});
|
||||
|
||||
it("should reset network error count on success", async () => {
|
||||
let requestCount = 0;
|
||||
server.use(
|
||||
http.get(TEST_URL, () => {
|
||||
requestCount++;
|
||||
if (requestCount <= 2) return HttpResponse.error();
|
||||
return HttpResponse.text("ok");
|
||||
})
|
||||
);
|
||||
|
||||
fetcher = new HTTPFetcher(TEST_URL, { reloadInterval: 300000, maxRetries: 3 });
|
||||
|
||||
const errors = [];
|
||||
fetcher.on("error", (errorInfo) => errors.push(errorInfo));
|
||||
|
||||
// Two failures with backoff
|
||||
await fetcher.fetch();
|
||||
await fetcher.fetch();
|
||||
expect(errors).toHaveLength(2);
|
||||
expect(errors[0].retryAfter).toBe(15000);
|
||||
expect(errors[1].retryAfter).toBe(30000);
|
||||
|
||||
// Success resets counter
|
||||
await fetcher.fetch();
|
||||
expect(fetcher.networkErrorCount).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user