more complex route

This commit is contained in:
Anton Martynenko
2025-11-01 17:24:59 +01:00
parent 11395ec913
commit f8798cd3d3
6 changed files with 431 additions and 77 deletions
@@ -52,13 +52,31 @@ public class GoogleMapsDownloaderV2
{
var str = JsonConvert.SerializeObject(new { mapType = "satellite" });
var response = await httpClient.PostAsync(url, new StringContent(str));
if (!response.IsSuccessStatusCode)
{
var errorBody = await response.Content.ReadAsStringAsync();
_logger.LogError("Failed to get session token. Status: {StatusCode}, Response: {Response}",
response.StatusCode, errorBody);
}
response.EnsureSuccessStatusCode();
var sessionResponse = await response.Content.ReadFromJsonAsync<SessionResponse>();
return sessionResponse?.Session;
}
catch (TaskCanceledException ex)
{
_logger.LogError(ex, "Session token request cancelled or timed out");
throw;
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "HTTP request failed while getting session token. StatusCode: {StatusCode}", ex.StatusCode);
throw;
}
catch (Exception e)
{
_logger.LogError(e, "Failed to get session token");
_logger.LogError(e, "Unexpected error getting session token");
throw;
}
}
@@ -92,6 +110,14 @@ public class GoogleMapsDownloaderV2
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(USER_AGENT);
var response = await httpClient.GetAsync(url, token);
if (!response.IsSuccessStatusCode)
{
var errorBody = await response.Content.ReadAsStringAsync(token);
_logger.LogError("Single tile download failed. Tile: ({X}, {Y}), Status: {StatusCode}, Response: {Response}",
tileX, tileY, response.StatusCode, errorBody);
}
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsByteArrayAsync(token);
@@ -127,6 +153,7 @@ public class GoogleMapsDownloaderV2
{
int attempt = 0;
int delay = BASE_RETRY_DELAY_SECONDS;
Exception? lastException = null;
while (attempt < maxRetries)
{
@@ -134,34 +161,68 @@ public class GoogleMapsDownloaderV2
{
return await action();
}
catch (TaskCanceledException ex)
{
_logger.LogError(ex, "Request was cancelled (timeout or explicit cancellation). Attempt {Attempt}/{Max}", attempt + 1, maxRetries);
throw;
}
catch (OperationCanceledException ex)
{
_logger.LogError(ex, "Operation was cancelled. Attempt {Attempt}/{Max}", attempt + 1, maxRetries);
throw;
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.TooManyRequests || ex.StatusCode == (HttpStatusCode)429)
{
attempt++;
lastException = ex;
if (attempt >= maxRetries)
{
_logger.LogError("Rate limit exceeded after {Attempts} attempts", maxRetries);
throw new RateLimitException($"Rate limit exceeded after {maxRetries} attempts");
_logger.LogError(ex, "Rate limit (429) exceeded after {Attempts} attempts. This indicates Google Maps API throttling.", maxRetries);
throw new RateLimitException($"Rate limit exceeded after {maxRetries} attempts. Google Maps API is throttling requests.");
}
delay = Math.Min(delay * 2, MAX_RETRY_DELAY_SECONDS);
_logger.LogWarning("Rate limited. Waiting {Delay}s before retry {Attempt}/{Max}", delay, attempt, maxRetries);
_logger.LogWarning("Rate limited (429 Too Many Requests). Waiting {Delay}s before retry {Attempt}/{Max}", delay, attempt, maxRetries);
await Task.Delay(TimeSpan.FromSeconds(delay), cancellationToken);
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.Forbidden)
{
_logger.LogError(ex, "Access forbidden (403). Check API key validity and permissions.");
throw;
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.Unauthorized)
{
_logger.LogError(ex, "Unauthorized (401). API key is invalid or missing.");
throw;
}
catch (HttpRequestException ex) when (ex.StatusCode >= HttpStatusCode.InternalServerError)
{
attempt++;
lastException = ex;
if (attempt >= maxRetries)
{
_logger.LogError(ex, "Server error after {Attempts} attempts", maxRetries);
_logger.LogError(ex, "Server error ({StatusCode}) after {Attempts} attempts", ex.StatusCode, maxRetries);
throw;
}
delay = Math.Min(delay * 2, MAX_RETRY_DELAY_SECONDS);
_logger.LogWarning("Server error. Waiting {Delay}s before retry {Attempt}/{Max}", delay, attempt, maxRetries);
_logger.LogWarning("Server error ({StatusCode}). Waiting {Delay}s before retry {Attempt}/{Max}", ex.StatusCode, delay, attempt, maxRetries);
await Task.Delay(TimeSpan.FromSeconds(delay), cancellationToken);
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "HTTP request failed with status {StatusCode}. Message: {Message}", ex.StatusCode, ex.Message);
throw;
}
}
if (lastException != null)
{
throw new InvalidOperationException($"Retry logic exhausted after {maxRetries} attempts", lastException);
}
throw new InvalidOperationException("Retry logic failed unexpectedly");
}
@@ -229,6 +290,14 @@ public class GoogleMapsDownloaderV2
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(USER_AGENT);
var response = await httpClient.GetAsync(url, token);
if (!response.IsSuccessStatusCode)
{
var errorBody = await response.Content.ReadAsStringAsync(token);
_logger.LogError("Tile download failed. Tile: ({X}, {Y}), Status: {StatusCode}, URL: {Url}, Response: {Response}",
x, y, response.StatusCode, url, errorBody);
}
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsByteArrayAsync(token);
@@ -242,14 +311,31 @@ public class GoogleMapsDownloaderV2
downloadedTiles.Add(new DownloadedTileInfoV2(
x, y, zoomLevel, tileCenter.Lat, tileCenter.Lon, filePath, tileSizeMeters));
}
catch (TaskCanceledException ex)
{
_logger.LogError(ex, "Tile download cancelled for ({X}, {Y}). This may be due to HttpClient timeout or explicit cancellation.", x, y);
throw;
}
catch (OperationCanceledException ex)
{
_logger.LogError(ex, "Tile download operation cancelled for ({X}, {Y})", x, y);
throw;
}
catch (RateLimitException ex)
{
_logger.LogError(ex, "Rate limit exceeded for tile ({X}, {Y})", x, y);
_logger.LogError(ex, "Rate limit exceeded for tile ({X}, {Y}). Google Maps API is throttling requests. Consider reducing concurrent requests or adding delays.", x, y);
throw;
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "HTTP request failed for tile ({X}, {Y}). StatusCode: {StatusCode}, Message: {Message}",
x, y, ex.StatusCode, ex.Message);
throw;
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to download tile ({X}, {Y})", x, y);
_logger.LogError(ex, "Unexpected error downloading tile ({X}, {Y}). Type: {ExceptionType}, Message: {Message}",
x, y, ex.GetType().Name, ex.Message);
throw;
}
}