Compare commits
No commits in common. "68d91b81513ea5ba37fd5a34a57a1ebdbcbba2a8" and "f16e1e5e5d38ed08450e18b39c7f3fcddc6acb6e" have entirely different histories.
68d91b8151
...
f16e1e5e5d
@ -53,7 +53,8 @@ namespace OpenCand.API
|
|||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
}
|
}
|
||||||
private static void SetupServices(WebApplicationBuilder builder)
|
|
||||||
|
private static void SetupServices(WebApplicationBuilder builder)
|
||||||
{
|
{
|
||||||
builder.Services.Configure<FotosSettings>(builder.Configuration.GetSection("FotosSettings"));
|
builder.Services.Configure<FotosSettings>(builder.Configuration.GetSection("FotosSettings"));
|
||||||
builder.Services.AddMemoryCache();
|
builder.Services.AddMemoryCache();
|
||||||
@ -66,9 +67,6 @@ namespace OpenCand.API
|
|||||||
|
|
||||||
builder.Services.AddScoped<OpenCandService>();
|
builder.Services.AddScoped<OpenCandService>();
|
||||||
builder.Services.AddScoped<EstatisticaService>();
|
builder.Services.AddScoped<EstatisticaService>();
|
||||||
|
|
||||||
// Add cache preload background service
|
|
||||||
builder.Services.AddHostedService<CachePreloadService>();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,7 @@ namespace OpenCand.Repository
|
|||||||
return await connection.QueryFirstOrDefaultAsync<Guid?>(@"
|
return await connection.QueryFirstOrDefaultAsync<Guid?>(@"
|
||||||
SELECT idcandidato
|
SELECT idcandidato
|
||||||
FROM candidato
|
FROM candidato
|
||||||
TABLESAMPLE SYSTEM (0.01)
|
ORDER BY RANDOM()
|
||||||
LIMIT 1;
|
LIMIT 1;
|
||||||
");
|
");
|
||||||
}
|
}
|
||||||
|
@ -65,10 +65,10 @@ namespace OpenCand.API.Repository
|
|||||||
|
|
||||||
using (var connection = new NpgsqlConnection(ConnectionString))
|
using (var connection = new NpgsqlConnection(ConnectionString))
|
||||||
{
|
{
|
||||||
result.Partidos = (await connection.QueryAsync<string>(@"SELECT DISTINCT sigla FROM partido ORDER BY sigla ASC;")).AsList();
|
result.Partidos = (await connection.QueryAsync<string>(@"SELECT DISTINCT sigla FROM partido;")).AsList();
|
||||||
result.SiglasUF = (await connection.QueryAsync<string>(@"SELECT DISTINCT siglauf FROM candidato_mapping ORDER BY siglauf ASC;")).AsList();
|
result.SiglasUF = (await connection.QueryAsync<string>(@"SELECT DISTINCT siglauf FROM candidato_mapping;")).AsList();
|
||||||
result.Anos = (await connection.QueryAsync<int>(@"SELECT DISTINCT ano FROM candidato_mapping ORDER BY ano ASC;")).AsList();
|
result.Anos = (await connection.QueryAsync<int>(@"SELECT DISTINCT ano FROM candidato_mapping;")).AsList();
|
||||||
result.Cargos = (await connection.QueryAsync<string>(@"SELECT DISTINCT cargo FROM candidato_mapping ORDER BY cargo ASC;")).AsList();
|
result.Cargos = (await connection.QueryAsync<string>(@"SELECT DISTINCT cargo FROM candidato_mapping;")).AsList();
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -1,96 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using OpenCand.API.Model;
|
|
||||||
|
|
||||||
namespace OpenCand.API.Services
|
|
||||||
{
|
|
||||||
public class CachePreloadService : BackgroundService
|
|
||||||
{
|
|
||||||
private readonly IServiceProvider serviceProvider;
|
|
||||||
private readonly ILogger<CachePreloadService> logger;
|
|
||||||
|
|
||||||
public CachePreloadService(IServiceProvider serviceProvider, ILogger<CachePreloadService> logger)
|
|
||||||
{
|
|
||||||
this.serviceProvider = serviceProvider;
|
|
||||||
this.logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
|
||||||
{
|
|
||||||
// Wait a bit for the application to fully start up
|
|
||||||
await Task.Delay(5000, stoppingToken);
|
|
||||||
logger.LogInformation("Starting cache preload process...");
|
|
||||||
|
|
||||||
using var scope = serviceProvider.CreateScope();
|
|
||||||
var estatisticaService = scope.ServiceProvider.GetRequiredService<EstatisticaService>();
|
|
||||||
var openCandService = scope.ServiceProvider.GetRequiredService<OpenCandService>();
|
|
||||||
|
|
||||||
// First, preload single-call endpoints
|
|
||||||
await PreloadSingleEndpoints(estatisticaService, openCandService);
|
|
||||||
|
|
||||||
await PerformPreLoadEstatistica(estatisticaService);
|
|
||||||
|
|
||||||
logger.LogInformation("Cache preload process completed.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task PerformPreLoadEstatistica(EstatisticaService estatisticaService)
|
|
||||||
{
|
|
||||||
await PerformPreLoad("GetConfigurationModel", estatisticaService.GetConfigurationModel);
|
|
||||||
|
|
||||||
var types = new[] { "bem", "despesa", "receita" };
|
|
||||||
var groupByValues = new[] { "candidato", "partido", "uf", "cargo" };
|
|
||||||
|
|
||||||
logger.LogInformation($"Preloading cache with GetValueSum requests (3 types * 4 groupBy combinations)");
|
|
||||||
|
|
||||||
foreach (var type in types)
|
|
||||||
{
|
|
||||||
foreach (var groupBy in groupByValues)
|
|
||||||
{
|
|
||||||
var request = new GetValueSumRequest
|
|
||||||
{
|
|
||||||
Type = type,
|
|
||||||
GroupBy = groupBy,
|
|
||||||
Filter = null // No filters as requested
|
|
||||||
};
|
|
||||||
|
|
||||||
logger.LogDebug($"Executing cache preload request: Type={type}, GroupBy={groupBy}");
|
|
||||||
|
|
||||||
await PerformPreLoad("GetValueSum", () => estatisticaService.GetValueSum(request));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task PreloadSingleEndpoints(EstatisticaService estatisticaService, OpenCandService openCandService)
|
|
||||||
{
|
|
||||||
logger.LogInformation("Preloading single-call endpoints...");
|
|
||||||
|
|
||||||
await PerformPreLoad("GetOpenCandStatsAsync", estatisticaService.GetMaioresEnriquecimentos);
|
|
||||||
await PerformPreLoad("GetOpenCandStatsAsync", openCandService.GetOpenCandStatsAsync);
|
|
||||||
await PerformPreLoad("GetDataAvailabilityStatsAsync", openCandService.GetDataAvailabilityStatsAsync);
|
|
||||||
|
|
||||||
logger.LogInformation("Single-call endpoints preload completed");
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task PerformPreLoad(string name, Func<Task> action)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
logger.LogDebug($"Executing cache preload for {name}");
|
|
||||||
|
|
||||||
var startTime = DateTime.UtcNow;
|
|
||||||
await action();
|
|
||||||
var duration = DateTime.UtcNow - startTime;
|
|
||||||
|
|
||||||
logger.LogInformation($"Cache preload completed for {name}: Duration={duration.TotalMilliseconds}ms");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
logger.LogError(ex, $"Failed to perform preload action for {name}");
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
await Task.Delay(100); // Small delay to avoid overwhelming the database
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,7 +2,6 @@ using Microsoft.Extensions.Configuration;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using OpenCand.Config;
|
using OpenCand.Config;
|
||||||
using OpenCand.ETL.Repository;
|
|
||||||
using OpenCand.ETL.Services;
|
using OpenCand.ETL.Services;
|
||||||
using OpenCand.Parser.Models;
|
using OpenCand.Parser.Models;
|
||||||
using OpenCand.Parser.Services;
|
using OpenCand.Parser.Services;
|
||||||
@ -22,8 +21,6 @@ namespace OpenCand.Parser
|
|||||||
|
|
||||||
private readonly DespesaReceitaService despesaReceitaService;
|
private readonly DespesaReceitaService despesaReceitaService;
|
||||||
|
|
||||||
private readonly ViewRepository viewRepository;
|
|
||||||
|
|
||||||
private readonly string BasePath;
|
private readonly string BasePath;
|
||||||
|
|
||||||
public ParserManager(
|
public ParserManager(
|
||||||
@ -35,8 +32,7 @@ namespace OpenCand.Parser
|
|||||||
CsvParserService<RedeSocialCSV> redeSocialParserService,
|
CsvParserService<RedeSocialCSV> redeSocialParserService,
|
||||||
CsvParserService<DespesasCSV> despesaParserService,
|
CsvParserService<DespesasCSV> despesaParserService,
|
||||||
CsvParserService<ReceitasCSV> receitaParserService,
|
CsvParserService<ReceitasCSV> receitaParserService,
|
||||||
DespesaReceitaService despesaReceitaService,
|
DespesaReceitaService despesaReceitaService)
|
||||||
ViewRepository viewRepository)
|
|
||||||
{
|
{
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.csvSettings = csvSettings.Value;
|
this.csvSettings = csvSettings.Value;
|
||||||
@ -47,7 +43,6 @@ namespace OpenCand.Parser
|
|||||||
this.despesaParserService = despesaParserService;
|
this.despesaParserService = despesaParserService;
|
||||||
this.receitaParserService = receitaParserService;
|
this.receitaParserService = receitaParserService;
|
||||||
this.despesaReceitaService = despesaReceitaService;
|
this.despesaReceitaService = despesaReceitaService;
|
||||||
this.viewRepository = viewRepository;
|
|
||||||
|
|
||||||
// Get the base path from either SampleFolder in csvSettings or the BasePath in configuration
|
// Get the base path from either SampleFolder in csvSettings or the BasePath in configuration
|
||||||
BasePath = configuration.GetValue<string>("BasePath") ?? string.Empty;
|
BasePath = configuration.GetValue<string>("BasePath") ?? string.Empty;
|
||||||
@ -78,12 +73,6 @@ namespace OpenCand.Parser
|
|||||||
await ParseFolder(receitasDirectory, receitaParserService);
|
await ParseFolder(receitasDirectory, receitaParserService);
|
||||||
|
|
||||||
logger.LogInformation("ParseFullDataAsync - Full data parsing completed!");
|
logger.LogInformation("ParseFullDataAsync - Full data parsing completed!");
|
||||||
|
|
||||||
logger.LogInformation("ParseFullDataAsync - Will refresh materialized views and re-run the analyzes.");
|
|
||||||
|
|
||||||
await viewRepository.RefreshMaterializedViews();
|
|
||||||
|
|
||||||
logger.LogInformation("ParseFullDataAsync - Materialized views refreshed successfully!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ParseFolder<CsvObj>(string csvDirectory, CsvParserService<CsvObj> csvParserService)
|
private async Task ParseFolder<CsvObj>(string csvDirectory, CsvParserService<CsvObj> csvParserService)
|
||||||
|
@ -79,7 +79,6 @@ namespace OpenCand
|
|||||||
services.AddTransient<RedeSocialRepository>();
|
services.AddTransient<RedeSocialRepository>();
|
||||||
services.AddTransient<PartidoRepository>();
|
services.AddTransient<PartidoRepository>();
|
||||||
services.AddTransient<CsvFixerService>();
|
services.AddTransient<CsvFixerService>();
|
||||||
services.AddTransient<ViewRepository>();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
using Dapper;
|
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Npgsql;
|
|
||||||
using OpenCand.Core.Models;
|
|
||||||
using OpenCand.Repository;
|
|
||||||
|
|
||||||
namespace OpenCand.ETL.Repository
|
|
||||||
{
|
|
||||||
public class ViewRepository : BaseRepository
|
|
||||||
{
|
|
||||||
public ViewRepository(IConfiguration configuration) : base(configuration)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
public async Task RefreshMaterializedViews()
|
|
||||||
{
|
|
||||||
using (var connection = new NpgsqlConnection(ConnectionString))
|
|
||||||
{
|
|
||||||
// Get all materialized view names
|
|
||||||
var materializedViews = await connection.QueryAsync<string>(
|
|
||||||
@"SELECT schemaname || '.' || matviewname as full_name
|
|
||||||
FROM pg_matviews
|
|
||||||
ORDER BY schemaname, matviewname");
|
|
||||||
|
|
||||||
foreach (var viewName in materializedViews)
|
|
||||||
{
|
|
||||||
// Refresh the materialized view
|
|
||||||
await connection.ExecuteAsync($"REFRESH MATERIALIZED VIEW {viewName}");
|
|
||||||
|
|
||||||
// Analyze the materialized view to update statistics
|
|
||||||
await connection.ExecuteAsync($"ANALYZE {viewName}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user