Skip to content

Commit c7e83e1

Browse files
Merge pull request #237 from rocky-linux/feature/url-check-timeout-retry
feat: add retry logic with exponential backoff for URL timeout checks
2 parents 8a3065e + ff5d619 commit c7e83e1

File tree

1 file changed

+72
-6
lines changed

1 file changed

+72
-6
lines changed

scripts/check-download-urls.js

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ function extractUrls(obj, path = "") {
4040
return urls;
4141
}
4242

43-
// Check if URL is accessible
44-
function checkUrl(urlInfo) {
43+
// Check if URL is accessible (single attempt)
44+
function checkUrlOnce(urlInfo) {
4545
return new Promise((resolve) => {
4646
const url = new URL(urlInfo.url);
4747
const protocol = url.protocol === "https:" ? https : http;
@@ -76,6 +76,58 @@ function checkUrl(urlInfo) {
7676
});
7777
}
7878

79+
// Retry timed out URLs with exponential backoff
80+
async function retryTimeouts(timedOutResults) {
81+
if (timedOutResults.length === 0) return [];
82+
83+
const maxRetries = 10;
84+
const baseDelay = 1000; // Start with 1 second
85+
let remaining = [...timedOutResults];
86+
const successful = [];
87+
88+
for (
89+
let attempt = 1;
90+
attempt <= maxRetries && remaining.length > 0;
91+
attempt++
92+
) {
93+
// Exponential backoff: 1s, 2s, 4s, 8s, 16s, 32s, 60s (capped), 60s, 60s, 60s
94+
const delay = Math.min(baseDelay * Math.pow(2, attempt - 1), 60000);
95+
96+
console.log(
97+
`\n${colors.yellow}Retry attempt ${attempt}/${maxRetries} for ${remaining.length} timed out URL(s) (waiting ${delay / 1000}s)...${colors.reset}`
98+
);
99+
100+
await new Promise((resolve) => setTimeout(resolve, delay));
101+
102+
const retryResults = await Promise.all(
103+
remaining.map((r) => checkUrlOnce({ url: r.url, path: r.path }))
104+
);
105+
106+
const stillTimedOut = [];
107+
for (const result of retryResults) {
108+
if (result.status === "TIMEOUT") {
109+
stillTimedOut.push(result);
110+
} else {
111+
successful.push(result);
112+
console.log(
113+
` ${colors.green}${colors.reset} ${result.url} - ${result.status === "OK" ? "OK" : result.status}`
114+
);
115+
}
116+
}
117+
118+
remaining = stillTimedOut;
119+
120+
if (remaining.length === 0) {
121+
console.log(
122+
`${colors.green}All timed out URLs recovered after ${attempt} retry attempt(s)!${colors.reset}`
123+
);
124+
}
125+
}
126+
127+
// Return both successful retries and still-failed ones
128+
return [...successful, ...remaining];
129+
}
130+
79131
// Progress bar
80132
function updateProgress(current, total) {
81133
const percentage = Math.round((current / total) * 100);
@@ -106,7 +158,7 @@ async function main() {
106158
// Process in batches to avoid overwhelming servers
107159
for (let i = 0; i < urls.length; i += batchSize) {
108160
const batch = urls.slice(i, i + batchSize);
109-
const batchResults = await Promise.all(batch.map(checkUrl));
161+
const batchResults = await Promise.all(batch.map(checkUrlOnce));
110162
results.push(...batchResults);
111163

112164
processed += batch.length;
@@ -121,16 +173,30 @@ async function main() {
121173
// Clear progress bar
122174
process.stdout.write("\r" + " ".repeat(80) + "\r");
123175

176+
// Separate timeouts from other failures for retry logic
177+
const timedOut = results.filter((r) => r.status === "TIMEOUT");
178+
const otherResults = results.filter((r) => r.status !== "TIMEOUT");
179+
180+
// Retry timed out URLs with exponential backoff
181+
let finalResults = [...otherResults];
182+
if (timedOut.length > 0) {
183+
console.log(
184+
`\n${colors.yellow}${timedOut.length} URL(s) timed out. Retrying with exponential backoff...${colors.reset}`
185+
);
186+
const retryResults = await retryTimeouts(timedOut);
187+
finalResults = [...finalResults, ...retryResults];
188+
}
189+
124190
// Report results
125-
const failed = results.filter((r) => r.status !== "OK");
126-
const successful = results.filter((r) => r.status === "OK");
191+
const failed = finalResults.filter((r) => r.status !== "OK");
192+
const successful = finalResults.filter((r) => r.status === "OK");
127193

128194
console.log("\n" + colors.blue + "=" + "=".repeat(50) + colors.reset);
129195
console.log(`${colors.blue}URL Check Results${colors.reset}`);
130196
console.log(colors.blue + "=" + "=".repeat(50) + colors.reset + "\n");
131197

132198
console.log(
133-
`Total URLs checked: ${colors.yellow}${results.length}${colors.reset}`
199+
`Total URLs checked: ${colors.yellow}${finalResults.length}${colors.reset}`
134200
);
135201
console.log(`Successful: ${colors.green}${successful.length}${colors.reset}`);
136202
console.log(`Failed: ${colors.red}${failed.length}${colors.reset}\n`);

0 commit comments

Comments
 (0)