Fix genetic db connection pool
This commit is contained in:
@@ -2,9 +2,11 @@ using System.Text.Json;
|
||||
using GeneticSharp;
|
||||
using Managing.Application.Abstractions.Repositories;
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Application.Backtests;
|
||||
using Managing.Core;
|
||||
using Managing.Domain.Backtests;
|
||||
using Managing.Domain.Bots;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.MoneyManagements;
|
||||
using Managing.Domain.Risk;
|
||||
using Managing.Domain.Scenarios;
|
||||
@@ -346,10 +348,50 @@ public class GeneticService : IGeneticService
|
||||
_logger.LogInformation("Starting fresh genetic algorithm for request {RequestId}", request.RequestId);
|
||||
}
|
||||
|
||||
// Create fitness function first
|
||||
var fitness = new TradingBotFitness(_serviceScopeFactory, request, _logger);
|
||||
// Load candles once at the beginning to avoid repeated database queries
|
||||
// This significantly reduces database connections during genetic algorithm execution
|
||||
_logger.LogInformation("Loading candles for genetic algorithm {RequestId}: {Ticker} on {Timeframe} from {StartDate} to {EndDate}",
|
||||
request.RequestId, request.Ticker, request.Timeframe, request.StartDate, request.EndDate);
|
||||
|
||||
HashSet<Candle> candles;
|
||||
try
|
||||
{
|
||||
candles = await ServiceScopeHelpers.WithScopedService<ICandleRepository, HashSet<Candle>>(
|
||||
_serviceScopeFactory,
|
||||
async candleRepository => await candleRepository.GetCandles(
|
||||
TradingExchanges.Evm, // Default exchange for genetic algorithms
|
||||
request.Ticker,
|
||||
request.Timeframe,
|
||||
request.StartDate,
|
||||
request.EndDate
|
||||
)
|
||||
);
|
||||
|
||||
if (candles == null || candles.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"No candles found for {request.Ticker} on {request.Timeframe} from {request.StartDate} to {request.EndDate}");
|
||||
}
|
||||
|
||||
_logger.LogInformation("Loaded {CandleCount} candles for genetic algorithm {RequestId}",
|
||||
candles.Count, request.RequestId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to load candles for genetic algorithm {RequestId}", request.RequestId);
|
||||
throw;
|
||||
}
|
||||
|
||||
// Create fitness function with pre-loaded candles to avoid database queries during evaluation
|
||||
var fitness = new TradingBotFitness(_serviceScopeFactory, request, candles, _logger);
|
||||
|
||||
// Create genetic algorithm with better configuration
|
||||
// Limit parallelism to prevent database connection pool exhaustion
|
||||
// Each fitness evaluation creates a database connection, so we need to limit
|
||||
// the number of parallel evaluations to stay within connection pool limits
|
||||
// Default PostgreSQL pool is 20 connections, so we limit to 4 threads per genetic job
|
||||
// This leaves room for other operations and multiple concurrent genetic jobs
|
||||
var maxParallelThreads = Math.Min(4, Environment.ProcessorCount);
|
||||
var ga = new GeneticAlgorithm(
|
||||
population,
|
||||
fitness,
|
||||
@@ -366,7 +408,7 @@ public class GeneticService : IGeneticService
|
||||
TaskExecutor = new ParallelTaskExecutor
|
||||
{
|
||||
MinThreads = 2,
|
||||
MaxThreads = Environment.ProcessorCount
|
||||
MaxThreads = maxParallelThreads
|
||||
}
|
||||
};
|
||||
|
||||
@@ -963,12 +1005,18 @@ public class TradingBotFitness : IFitness
|
||||
private readonly GeneticRequest _request;
|
||||
private GeneticAlgorithm _geneticAlgorithm;
|
||||
private readonly ILogger<GeneticService> _logger;
|
||||
private readonly HashSet<Candle> _candles;
|
||||
private static readonly SemaphoreSlim _dbSemaphore = new SemaphoreSlim(4, 4); // Limit concurrent DB operations
|
||||
|
||||
public TradingBotFitness(IServiceScopeFactory serviceScopeFactory, GeneticRequest request,
|
||||
public TradingBotFitness(
|
||||
IServiceScopeFactory serviceScopeFactory,
|
||||
GeneticRequest request,
|
||||
HashSet<Candle> candles,
|
||||
ILogger<GeneticService> logger)
|
||||
{
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
_request = request;
|
||||
_candles = candles;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -987,23 +1035,30 @@ public class TradingBotFitness : IFitness
|
||||
|
||||
var config = tradingBotChromosome.GetTradingBotConfig(_request);
|
||||
|
||||
// Get current generation number (default to 0 if not available)
|
||||
var currentGeneration = _geneticAlgorithm?.GenerationsNumber ?? 0;
|
||||
|
||||
// Run backtest using scoped service to avoid DbContext concurrency issues
|
||||
var lightBacktest = ServiceScopeHelpers.WithScopedService<IBacktester, LightBacktest>(
|
||||
_serviceScopeFactory,
|
||||
async backtester => await backtester.RunTradingBotBacktest(
|
||||
config,
|
||||
_request.StartDate,
|
||||
_request.EndDate,
|
||||
_request.User,
|
||||
true,
|
||||
false, // Don't include candles
|
||||
_request.RequestId,
|
||||
new GeneticBacktestMetadata(currentGeneration, _request.RequestId)
|
||||
)
|
||||
).GetAwaiter().GetResult();
|
||||
// Run backtest synchronously using BacktestExecutor (no job creation, no DB writes)
|
||||
// Use semaphore to limit concurrent database operations (for indicator calculations)
|
||||
// This prevents connection pool exhaustion when running large populations
|
||||
_dbSemaphore.Wait();
|
||||
LightBacktest lightBacktest;
|
||||
try
|
||||
{
|
||||
lightBacktest = ServiceScopeHelpers.WithScopedService<BacktestExecutor, LightBacktest>(
|
||||
_serviceScopeFactory,
|
||||
async executor => await executor.ExecuteAsync(
|
||||
config,
|
||||
_candles,
|
||||
_request.User,
|
||||
save: false, // Don't save backtest results for genetic algorithm
|
||||
withCandles: false,
|
||||
requestId: _request.RequestId,
|
||||
metadata: new GeneticBacktestMetadata(_geneticAlgorithm?.GenerationsNumber ?? 0, _request.RequestId)
|
||||
)
|
||||
).GetAwaiter().GetResult();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_dbSemaphore.Release();
|
||||
}
|
||||
|
||||
// Calculate multi-objective fitness based on backtest results
|
||||
var fitness = CalculateFitness(lightBacktest, config);
|
||||
|
||||
Reference in New Issue
Block a user