Update jobs

This commit is contained in:
2025-11-09 04:48:15 +07:00
parent 7e08e63dd1
commit 747bda2700
20 changed files with 2281 additions and 217 deletions

View File

@@ -0,0 +1,295 @@
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";
// Log environment detection before host creation
var dotnetEnv = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
var aspnetcoreEnv = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
Console.WriteLine("═══════════════════════════════════════════════════════════");
Console.WriteLine("🔧 Environment Configuration");
Console.WriteLine("═══════════════════════════════════════════════════════════");
Console.WriteLine($" DOTNET_ENVIRONMENT: {dotnetEnv ?? "(not set)"}");
Console.WriteLine($" ASPNETCORE_ENVIRONMENT: {aspnetcoreEnv ?? "(not set)"}");
Console.WriteLine($" Selected Environment: {environment}");
Console.WriteLine("═══════════════════════════════════════════════════════════");
var host = Host.CreateDefaultBuilder(args)
.UseEnvironment(environment) // Explicitly set the environment
.ConfigureAppConfiguration((hostingContext, config) =>
{
var detectedEnv = hostingContext.HostingEnvironment.EnvironmentName;
Console.WriteLine($" Detected Environment: {detectedEnv}");
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);
Console.WriteLine(" ✓ Loaded: appsettings.json");
// 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);
Console.WriteLine($" ✓ Loaded: {envFile} (optional)");
}
// 3. Environment variables and user secrets (highest priority)
config.AddEnvironmentVariables()
.AddUserSecrets<Program>();
Console.WriteLine("═══════════════════════════════════════════════════════════");
})
.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"];
// Log database connection details (mask password for security)
if (!string.IsNullOrEmpty(postgreSqlConnectionString))
{
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 host = connectionParts.GetValueOrDefault("Host", "unknown");
var port = connectionParts.GetValueOrDefault("Port", "unknown");
var database = connectionParts.GetValueOrDefault("Database", "unknown");
var username = connectionParts.GetValueOrDefault("Username", "unknown");
var maskedConnectionString = postgreSqlConnectionString
.Replace(connectionParts.GetValueOrDefault("Password", ""), "***", StringComparison.OrdinalIgnoreCase);
Console.WriteLine("═══════════════════════════════════════════════════════════");
Console.WriteLine("📊 PostgreSQL Database Configuration");
Console.WriteLine("═══════════════════════════════════════════════════════════");
Console.WriteLine($" Host: {host}");
Console.WriteLine($" Port: {port}");
Console.WriteLine($" Database: {database}");
Console.WriteLine($" Username: {username}");
Console.WriteLine($" Connection String: {maskedConnectionString}");
Console.WriteLine("═══════════════════════════════════════════════════════════");
}
else
{
Console.WriteLine("⚠️ WARNING: PostgreSQL connection string is empty or not configured!");
}
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"));
})
.Build();
// Log worker status
var logger = host.Services.GetRequiredService<ILogger<Program>>();
var config = host.Services.GetRequiredService<IConfiguration>();
// Log PostgreSQL connection details
var postgreSqlConnectionString = config.GetSection(Constants.Databases.PostgreSql)["ConnectionString"];
if (!string.IsNullOrEmpty(postgreSqlConnectionString))
{
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 hostName = connectionParts.GetValueOrDefault("Host", "unknown");
var port = connectionParts.GetValueOrDefault("Port", "unknown");
var database = connectionParts.GetValueOrDefault("Database", "unknown");
var username = connectionParts.GetValueOrDefault("Username", "unknown");
logger.LogInformation("═══════════════════════════════════════════════════════════");
logger.LogInformation("📊 PostgreSQL Database Configuration");
logger.LogInformation("═══════════════════════════════════════════════════════════");
logger.LogInformation(" Host: {Host}", hostName);
logger.LogInformation(" Port: {Port}", port);
logger.LogInformation(" Database: {Database}", database);
logger.LogInformation(" Username: {Username}", username);
logger.LogInformation("═══════════════════════════════════════════════════════════");
// Test database connection
try
{
using var scope = host.Services.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<ManagingDbContext>();
var canConnect = await dbContext.Database.CanConnectAsync();
if (canConnect)
{
logger.LogInformation("✅ Database connection test: SUCCESS");
}
else
{
logger.LogWarning("⚠️ Database connection test: FAILED - Cannot connect to database");
}
}
catch (Exception ex)
{
logger.LogError(ex, "❌ Database connection test: FAILED - {Error}", ex.Message);
}
}
else
{
logger.LogWarning("⚠️ WARNING: PostgreSQL connection string is empty or not configured!");
}
var isBacktestWorkerEnabled = config.GetValue<bool>("WorkerBacktestCompute", false);
var isGeneticWorkerEnabled = config.GetValue<bool>("WorkerGeneticCompute", false);
if (isBacktestWorkerEnabled)
{
var taskSlot = Environment.GetEnvironmentVariable("TASK_SLOT") ??
Environment.GetEnvironmentVariable("CAPROVER_TASK_SLOT") ??
"0";
var backtestWorkerId = Environment.GetEnvironmentVariable("WORKER_ID") ??
config["BacktestComputeWorker:WorkerId"] ??
$"{Environment.MachineName}-{taskSlot}";
logger.LogInformation("BacktestComputeWorker is enabled and will be started.");
logger.LogInformation("Backtest Worker ID: {WorkerId} (Task Slot: {TaskSlot})", backtestWorkerId, taskSlot);
}
else
{
logger.LogWarning("BacktestComputeWorker is disabled via configuration. No backtest jobs will be processed.");
}
if (isGeneticWorkerEnabled)
{
var taskSlot = Environment.GetEnvironmentVariable("TASK_SLOT") ??
Environment.GetEnvironmentVariable("CAPROVER_TASK_SLOT") ??
"0";
var geneticWorkerId = Environment.GetEnvironmentVariable("GENETIC_WORKER_ID") ??
config["GeneticComputeWorker:WorkerId"] ??
$"{Environment.MachineName}-genetic-{taskSlot}";
logger.LogInformation("GeneticComputeWorker is enabled and will be started.");
logger.LogInformation("Genetic Worker ID: {WorkerId} (Task Slot: {TaskSlot})", geneticWorkerId, taskSlot);
}
else
{
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();
}