Skip to content

Commit cc07773

Browse files
authored
🐌 Handle MyPurdue request throttling (#78)
MyPurdue has recently started throttling incoming requests, preventing Purdue.io CatalogSync from successfully syncing: > We are sorry, but the site has received too many requests. The exact behavior of the throttle logic is unclear, but brief experimentation has revealed that after being throttled, you must wait a minimum of 60 seconds from your last request to avoid being throttled again. This change implements an exponential delay with a 60-second base time when the throttling message is received to allow the sync to complete.
1 parent 3d41d75 commit cc07773

1 file changed

Lines changed: 20 additions & 3 deletions

File tree

src/Scraper/Connections/MyPurdueConnection.cs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@ private enum HttpMethod { GET, POST };
2929
private readonly HttpClient httpClient;
3030

3131
// How many request attempts should be made before failure
32-
private const int MAX_RETRIES = 5;
32+
private const int MAX_RETRIES = 6;
33+
34+
// MyPurdue now throttles our requests, and only seems to let us through
35+
// after waiting a minimum of one minute.
36+
private const int THROTTLE_DELAY_BASE_MS = 60000;
3337

3438
public MyPurdueConnection(ILogger<MyPurdueConnection> logger)
3539
{
@@ -146,11 +150,19 @@ public async Task<string> GetSectionListPageAsync(string termCode, string subjec
146150
{
147151
this.logger.LogError("Received non-success status code '{}' on " +
148152
"GetSectionListPageAsync.", request.StatusCode);
153+
continue;
149154
}
150-
else
155+
156+
var content = await request.Content.ReadAsStringAsync();
157+
if (content.Contains("We are sorry, but the site has received too many requests."))
151158
{
152-
return await request.Content.ReadAsStringAsync();
159+
var retryDelayMs = THROTTLE_DELAY_BASE_MS + ExponentialRetryDelayMs(attempts);
160+
this.logger.LogError("MyPurdue is throttling requests. Retrying in {}ms...",
161+
retryDelayMs);
162+
await Task.Delay(retryDelayMs);
163+
continue;
153164
}
165+
return content;
154166
}
155167
throw new ApplicationException(
156168
"Exceeded retries attempting to query MyPurdue section list");
@@ -315,5 +327,10 @@ private async Task<HttpResponseMessage> Request(HttpMethod method, string url,
315327
}
316328
return result;
317329
}
330+
331+
private static int ExponentialRetryDelayMs(int retryNumber)
332+
{
333+
return (int)(Math.Pow(2, retryNumber) * 1000);
334+
}
318335
}
319336
}

0 commit comments

Comments
 (0)