233 lines
8.8 KiB
C#
233 lines
8.8 KiB
C#
using Managing.Application.Workers;
|
|
using Managing.Bootstrap;
|
|
using Managing.Common;
|
|
using Managing.Infrastructure.Databases.InfluxDb.Models;
|
|
using Managing.Infrastructure.Databases.PostgreSql;
|
|
using Managing.Infrastructure.Databases.PostgreSql.Configurations;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
// Explicitly set the environment before creating the host builder
|
|
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")
|
|
?? Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT")
|
|
?? "Development";
|
|
|
|
var hostBuilder = new HostBuilder()
|
|
.UseEnvironment(environment)
|
|
.UseContentRoot(AppContext.BaseDirectory);
|
|
|
|
var host = hostBuilder
|
|
.ConfigureAppConfiguration((hostingContext, config) =>
|
|
{
|
|
var detectedEnv = hostingContext.HostingEnvironment.EnvironmentName;
|
|
|
|
if (detectedEnv != environment)
|
|
{
|
|
Console.WriteLine($"⚠️ WARNING: Environment mismatch! Expected: {environment}, Got: {detectedEnv}");
|
|
}
|
|
|
|
config.SetBasePath(AppContext.BaseDirectory);
|
|
|
|
// Load configuration files in order (later files override earlier ones)
|
|
// 1. Base appsettings.json (always loaded)
|
|
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
|
|
|
|
// 2. Load ONLY the environment-specific file (not other environments)
|
|
if (!string.IsNullOrEmpty(detectedEnv))
|
|
{
|
|
var envFile = $"appsettings.{detectedEnv}.json";
|
|
config.AddJsonFile(envFile, optional: true, reloadOnChange: true);
|
|
}
|
|
|
|
// 3. Environment variables (highest priority)
|
|
config.AddEnvironmentVariables();
|
|
|
|
// User secrets only in development (requires ASP.NET Core, so we skip in production)
|
|
if (detectedEnv == "Development")
|
|
{
|
|
try
|
|
{
|
|
config.AddUserSecrets<Program>();
|
|
}
|
|
catch
|
|
{
|
|
// User secrets not available, skip silently
|
|
}
|
|
}
|
|
})
|
|
.ConfigureServices((hostContext, services) =>
|
|
{
|
|
var configuration = hostContext.Configuration;
|
|
|
|
// Initialize Sentry
|
|
SentrySdk.Init(options =>
|
|
{
|
|
options.Dsn = configuration["Sentry:Dsn"];
|
|
options.Debug = false;
|
|
options.SendDefaultPii = true;
|
|
options.AutoSessionTracking = true;
|
|
options.IsGlobalModeEnabled = false;
|
|
options.TracesSampleRate = 0.1;
|
|
options.Environment = hostContext.HostingEnvironment.EnvironmentName;
|
|
});
|
|
|
|
// Configure database
|
|
var postgreSqlConnectionString = configuration.GetSection(Constants.Databases.PostgreSql)["ConnectionString"];
|
|
|
|
services.Configure<PostgreSqlSettings>(configuration.GetSection(Constants.Databases.PostgreSql));
|
|
services.Configure<InfluxDbSettings>(configuration.GetSection(Constants.Databases.InfluxDb));
|
|
|
|
// Add DbContext
|
|
services.AddDbContext<ManagingDbContext>((serviceProvider, options) =>
|
|
{
|
|
options.UseNpgsql(postgreSqlConnectionString, npgsqlOptions =>
|
|
{
|
|
npgsqlOptions.CommandTimeout(60);
|
|
npgsqlOptions.EnableRetryOnFailure(maxRetryCount: 5, maxRetryDelay: TimeSpan.FromSeconds(10), errorCodesToAdd: null);
|
|
});
|
|
|
|
if (hostContext.HostingEnvironment.IsDevelopment())
|
|
{
|
|
options.EnableDetailedErrors();
|
|
options.EnableSensitiveDataLogging();
|
|
// SQL logging disabled - uncomment below line if needed for debugging
|
|
// options.LogTo(Console.WriteLine, LogLevel.Information);
|
|
}
|
|
|
|
options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
|
|
options.EnableServiceProviderCaching();
|
|
}, ServiceLifetime.Scoped);
|
|
|
|
// Register compute dependencies (no Orleans)
|
|
services.RegisterComputeDependencies(configuration);
|
|
|
|
// Configure BacktestComputeWorker options
|
|
services.Configure<BacktestComputeWorkerOptions>(
|
|
configuration.GetSection(BacktestComputeWorkerOptions.SectionName));
|
|
|
|
// Get task slot from CapRover ({{.Task.Slot}}) or environment variable
|
|
// This identifies which instance of the worker is running
|
|
var taskSlot = Environment.GetEnvironmentVariable("TASK_SLOT") ??
|
|
Environment.GetEnvironmentVariable("CAPROVER_TASK_SLOT") ??
|
|
"0";
|
|
|
|
// Override WorkerId from environment variable if provided, otherwise use task slot
|
|
var workerId = Environment.GetEnvironmentVariable("WORKER_ID") ??
|
|
configuration["BacktestComputeWorker:WorkerId"] ??
|
|
$"{Environment.MachineName}-{taskSlot}";
|
|
services.Configure<BacktestComputeWorkerOptions>(options =>
|
|
{
|
|
options.WorkerId = workerId;
|
|
});
|
|
|
|
// Configure GeneticComputeWorker options
|
|
services.Configure<GeneticComputeWorkerOptions>(
|
|
configuration.GetSection(GeneticComputeWorkerOptions.SectionName));
|
|
|
|
// Override Genetic WorkerId from environment variable if provided, otherwise use task slot
|
|
var geneticWorkerId = Environment.GetEnvironmentVariable("GENETIC_WORKER_ID") ??
|
|
configuration["GeneticComputeWorker:WorkerId"] ??
|
|
$"{Environment.MachineName}-genetic-{taskSlot}";
|
|
services.Configure<GeneticComputeWorkerOptions>(options =>
|
|
{
|
|
options.WorkerId = geneticWorkerId;
|
|
});
|
|
|
|
// Register the backtest compute worker if enabled
|
|
var isBacktestWorkerEnabled = configuration.GetValue<bool>("WorkerBacktestCompute", false);
|
|
if (isBacktestWorkerEnabled)
|
|
{
|
|
services.AddHostedService<BacktestComputeWorker>();
|
|
}
|
|
|
|
// Register the genetic compute worker if enabled
|
|
var isGeneticWorkerEnabled = configuration.GetValue<bool>("WorkerGeneticCompute", false);
|
|
if (isGeneticWorkerEnabled)
|
|
{
|
|
services.AddHostedService<GeneticComputeWorker>();
|
|
}
|
|
})
|
|
.ConfigureLogging((hostingContext, logging) =>
|
|
{
|
|
logging.ClearProviders();
|
|
logging.AddConsole();
|
|
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
|
|
|
|
// Filter out EF Core database command logs (SQL queries)
|
|
logging.AddFilter("Microsoft.EntityFrameworkCore.Database.Command", LogLevel.Warning);
|
|
})
|
|
.Build();
|
|
|
|
var logger = host.Services.GetRequiredService<ILogger<Program>>();
|
|
var config = host.Services.GetRequiredService<IConfiguration>();
|
|
|
|
// Test database connection at startup
|
|
var postgreSqlConnectionString = config.GetSection(Constants.Databases.PostgreSql)["ConnectionString"];
|
|
if (string.IsNullOrEmpty(postgreSqlConnectionString))
|
|
{
|
|
logger.LogWarning("⚠️ Database connection string is empty or not configured!");
|
|
}
|
|
else
|
|
{
|
|
// Parse and log database host name
|
|
try
|
|
{
|
|
var connectionParts = postgreSqlConnectionString.Split(';')
|
|
.Where(p => !string.IsNullOrWhiteSpace(p))
|
|
.Select(p => p.Trim())
|
|
.ToDictionary(
|
|
p => p.Split('=')[0].Trim(),
|
|
p => p.Contains('=') ? p.Substring(p.IndexOf('=') + 1).Trim() : string.Empty,
|
|
StringComparer.OrdinalIgnoreCase);
|
|
|
|
var dbHost = connectionParts.GetValueOrDefault("Host", "unknown");
|
|
logger.LogWarning("📊 Database Host: {Host}", dbHost);
|
|
}
|
|
catch
|
|
{
|
|
// Failed to parse connection string, continue anyway
|
|
}
|
|
|
|
try
|
|
{
|
|
using var scope = host.Services.CreateScope();
|
|
var dbContext = scope.ServiceProvider.GetRequiredService<ManagingDbContext>();
|
|
var canConnect = await dbContext.Database.CanConnectAsync();
|
|
if (!canConnect)
|
|
{
|
|
logger.LogWarning("⚠️ Database connection test failed - Cannot connect to database");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "❌ Database connection test failed - {Error}", ex.Message);
|
|
}
|
|
}
|
|
|
|
var isBacktestWorkerEnabled = config.GetValue<bool>("WorkerBacktestCompute", false);
|
|
var isGeneticWorkerEnabled = config.GetValue<bool>("WorkerGeneticCompute", false);
|
|
|
|
if (!isBacktestWorkerEnabled)
|
|
{
|
|
logger.LogWarning("BacktestComputeWorker is disabled via configuration. No backtest jobs will be processed.");
|
|
}
|
|
|
|
if (!isGeneticWorkerEnabled)
|
|
{
|
|
logger.LogWarning("GeneticComputeWorker is disabled via configuration. No genetic jobs will be processed.");
|
|
}
|
|
|
|
try
|
|
{
|
|
await host.RunAsync();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogCritical(ex, "Application terminated unexpectedly");
|
|
SentrySdk.CaptureException(ex);
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
SentrySdk.FlushAsync(TimeSpan.FromSeconds(2)).Wait();
|
|
}
|