parallel processing for routes and regions

This commit is contained in:
Anton Martynenko
2025-11-19 13:01:30 +01:00
parent 7f33567632
commit b66d3a0277
8 changed files with 331 additions and 133 deletions
@@ -97,76 +97,95 @@ public class RouteProcessingService : BackgroundService
using var scope = _serviceProvider.CreateScope();
var regionService = scope.ServiceProvider.GetRequiredService<Common.Interfaces.IRegionService>();
_logger.LogInformation("Route {RouteId}: Starting sequential region processing for {PointCount} points",
_logger.LogInformation("Route {RouteId}: Starting parallel region processing for {PointCount} points",
routeId, routePointsList.Count);
var firstPoint = routePointsList.First();
var regionId = Guid.NewGuid();
var queuedRegionIds = new List<Guid>();
await regionService.RequestRegionAsync(
regionId,
firstPoint.Latitude,
firstPoint.Longitude,
route.RegionSizeMeters,
route.ZoomLevel,
stitchTiles: false);
foreach (var point in routePointsList)
{
var regionId = Guid.NewGuid();
await regionService.RequestRegionAsync(
regionId,
point.Latitude,
point.Longitude,
route.RegionSizeMeters,
route.ZoomLevel,
stitchTiles: false);
await _routeRepository.LinkRouteToRegionAsync(routeId, regionId);
queuedRegionIds.Add(regionId);
}
await _routeRepository.LinkRouteToRegionAsync(routeId, regionId);
_logger.LogInformation("Route {RouteId}: Queued first region {RegionId} (1/{TotalPoints})",
routeId, regionId, routePointsList.Count);
_logger.LogInformation("Route {RouteId}: Queued all {Count} regions for parallel processing. Region IDs: {RegionIds}",
routeId, queuedRegionIds.Count, string.Join(", ", queuedRegionIds.Take(5)) + (queuedRegionIds.Count > 5 ? "..." : ""));
return;
}
var lastRegion = await _regionRepository.GetByIdAsync(regionIdsList.Last());
if (lastRegion == null)
var regions = new List<DataAccess.Models.RegionEntity>();
foreach (var regionId in regionIdsList)
{
return;
var region = await _regionRepository.GetByIdAsync(regionId);
if (region != null)
{
regions.Add(region);
}
}
if (lastRegion.Status == "queued" || lastRegion.Status == "processing")
{
return;
}
var completedRegions = regions.Where(r => r.Status == "completed").ToList();
var failedRegions = regions.Where(r => r.Status == "failed").ToList();
var processingRegions = regions.Where(r => r.Status == "queued" || r.Status == "processing").ToList();
if (lastRegion.Status == "failed")
var hasEnoughCompleted = completedRegions.Count >= routePointsList.Count;
var activeRegions = completedRegions.Count + processingRegions.Count;
var shouldRetryFailed = failedRegions.Count > 0 && !hasEnoughCompleted && activeRegions < routePointsList.Count;
if (hasEnoughCompleted)
{
_logger.LogError("Route {RouteId}: Region {RegionId} failed. Stopping route processing.",
routeId, lastRegion.Id);
_logger.LogInformation("Route {RouteId}: Have {Completed} completed regions (required: {Required}). Generating final maps. Ignoring {Processing} processing and {Failed} failed regions.",
routeId, completedRegions.Count, routePointsList.Count, processingRegions.Count, failedRegions.Count);
route.MapsReady = false;
route.UpdatedAt = DateTime.UtcNow;
await _routeRepository.UpdateRouteAsync(route);
await GenerateRouteMapsAsync(routeId, route, completedRegions.Take(routePointsList.Count).Select(r => r.Id), cancellationToken);
return;
}
if (regionIdsList.Count < routePointsList.Count)
if (shouldRetryFailed)
{
_logger.LogWarning("Route {RouteId}: {FailedCount} region(s) failed: {FailedRegions}. Active regions: {ActiveCount}/{RequiredCount}. Attempting to retry with new regions.",
routeId, failedRegions.Count, string.Join(", ", failedRegions.Select(r => r.Id)), activeRegions, routePointsList.Count);
using var scope = _serviceProvider.CreateScope();
var regionService = scope.ServiceProvider.GetRequiredService<Common.Interfaces.IRegionService>();
var nextPoint = routePointsList[regionIdsList.Count];
var regionId = Guid.NewGuid();
foreach (var failedRegion in failedRegions)
{
var newRegionId = Guid.NewGuid();
_logger.LogInformation("Route {RouteId}: Retrying failed region {OldRegionId} with new region {NewRegionId}",
routeId, failedRegion.Id, newRegionId);
await regionService.RequestRegionAsync(
newRegionId,
failedRegion.Latitude,
failedRegion.Longitude,
failedRegion.SizeMeters,
failedRegion.ZoomLevel,
stitchTiles: false);
await _routeRepository.LinkRouteToRegionAsync(routeId, newRegionId);
}
await regionService.RequestRegionAsync(
regionId,
nextPoint.Latitude,
nextPoint.Longitude,
route.RegionSizeMeters,
route.ZoomLevel,
stitchTiles: false);
await _routeRepository.LinkRouteToRegionAsync(routeId, regionId);
_logger.LogInformation("Route {RouteId}: Queued next region {RegionId} ({CurrentRegion}/{TotalPoints})",
routeId, regionId, regionIdsList.Count + 1, routePointsList.Count);
_logger.LogInformation("Route {RouteId}: Queued {Count} retry regions", routeId, failedRegions.Count);
return;
}
_logger.LogInformation("Route {RouteId}: All {RegionCount} regions completed, generating final maps",
routeId, regionIdsList.Count);
await GenerateRouteMapsAsync(routeId, route, regionIdsList, cancellationToken);
var anyProcessing = processingRegions.Count > 0;
if (anyProcessing)
{
_logger.LogInformation("Route {RouteId}: Progress - {Completed}/{Required} regions completed, {Processing} still processing, {Failed} failed (will retry if needed)",
routeId, completedRegions.Count, routePointsList.Count, processingRegions.Count, failedRegions.Count);
return;
}
}
private async Task GenerateRouteMapsAsync(