diff --git a/Elecciones-Web/src/Elecciones.Worker/Worker.cs b/Elecciones-Web/src/Elecciones.Worker/Worker.cs index db4ddb3..3f58e47 100644 --- a/Elecciones-Web/src/Elecciones.Worker/Worker.cs +++ b/Elecciones-Web/src/Elecciones.Worker/Worker.cs @@ -578,6 +578,9 @@ public class Worker : BackgroundService _logger.LogInformation("Iniciando sondeo de Telegramas para {count} combinaciones... con paralelismo de {degree}", combinaciones.Count(), GRADO_DE_PARALELISMO); + // Usaremos un ConcurrentBag para recolectar de forma segura los telegramas nuevos desde múltiples hilos. + var telegramasNuevosParaGuardar = new ConcurrentBag(); + var tareas = combinaciones.Select(async item => { await semaforo.WaitAsync(stoppingToken); @@ -590,8 +593,6 @@ public class Worker : BackgroundService using var innerScope = _serviceProvider.CreateScope(); var innerDbContext = innerScope.ServiceProvider.GetRequiredService(); - // --- CORRECCIÓN CLAVE --- - // 'idsDeApi' ya es una List, no necesitamos hacer .Select(t => t[0]) var idsYaEnDb = await innerDbContext.Telegramas .Where(t => idsDeApi.Contains(t.Id)) .Select(t => t.Id) @@ -599,17 +600,13 @@ public class Worker : BackgroundService var nuevosTelegramasIds = idsDeApi.Except(idsYaEnDb).ToList(); - if (!nuevosTelegramasIds.Any()) - { - return; - } + if (!nuevosTelegramasIds.Any()) return; - _logger.LogInformation("Se encontraron {count} telegramas nuevos en '{partido}' para '{cat}'. Descargando...", nuevosTelegramasIds.Count, item.partido.Nombre, item.categoria.Nombre); + _logger.LogInformation("Se encontraron {count} telegramas nuevos en '{partido}' para '{cat}'. Descargando en paralelo...", nuevosTelegramasIds.Count, item.partido.Nombre, item.categoria.Nombre); - // Iteramos y descargamos cada nuevo telegrama. - foreach (var mesaId in nuevosTelegramasIds) + // --- NUEVA OPTIMIZACIÓN: Paralelizar la descarga de los archivos --- + await Task.WhenAll(nuevosTelegramasIds.Select(async mesaId => { - if (stoppingToken.IsCancellationRequested) break; var telegramaFile = await _apiService.GetTelegramaFileAsync(authToken, mesaId); if (telegramaFile != null) { @@ -621,23 +618,39 @@ public class Worker : BackgroundService FechaEscaneo = DateTime.Parse(telegramaFile.FechaEscaneo).ToUniversalTime(), FechaTotalizacion = DateTime.Parse(telegramaFile.FechaTotalizacion).ToUniversalTime() }; - await innerDbContext.Telegramas.AddAsync(nuevoTelegrama, stoppingToken); + telegramasNuevosParaGuardar.Add(nuevoTelegrama); } - } - // Guardamos los cambios de ESTA tarea específica en la BD. - await innerDbContext.SaveChangesAsync(stoppingToken); + // Si telegramaFile es null (por 403, 200 vacío, etc.), simplemente no hacemos nada. + })); } } + // --- NUEVA ROBUSTEZ: Capturar errores por tarea --- + catch (Exception ex) + { + // Si una combinación entera falla (ej. getTelegramasTotalizados da 500), lo logueamos + // pero NO relanzamos la excepción, para no cancelar el Task.WhenAll principal. + _logger.LogError(ex, "Falló el sondeo de telegramas para el partido '{partido}' y categoría '{cat}'", item.partido.Nombre, item.categoria.Nombre); + } finally { - // Liberamos el semáforo para que otra tarea pueda comenzar. semaforo.Release(); } }); - // Ejecutamos todas las tareas en paralelo y esperamos a que finalicen. + // Esperamos a que todas las tareas de sondeo y descarga terminen. await Task.WhenAll(tareas); + // --- Guardado Masivo Final --- + // Después de que todo el paralelismo ha terminado, hacemos una única operación de escritura en la BD. + if (!telegramasNuevosParaGuardar.IsEmpty) + { + _logger.LogInformation("Guardando un total de {count} telegramas nuevos en la base de datos...", telegramasNuevosParaGuardar.Count); + using var finalScope = _serviceProvider.CreateScope(); + var finalDbContext = finalScope.ServiceProvider.GetRequiredService(); + await finalDbContext.Telegramas.AddRangeAsync(telegramasNuevosParaGuardar, stoppingToken); + await finalDbContext.SaveChangesAsync(stoppingToken); + } + _logger.LogInformation("Sondeo de Telegramas completado."); } catch (Exception ex)