Add bundle backtest refact + fix whitelist

This commit is contained in:
2025-10-12 14:40:20 +07:00
parent 4543246871
commit 5acc77650f
21 changed files with 2961 additions and 628 deletions

View File

@@ -0,0 +1,61 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Managing.Infrastructure.Databases.Migrations
{
/// <inheritdoc />
public partial class UpdateBundleBacktestRequestToVariants : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "BacktestRequestsJson",
table: "BundleBacktestRequests",
newName: "UniversalConfigJson");
migrationBuilder.AddColumn<string>(
name: "DateTimeRangesJson",
table: "BundleBacktestRequests",
type: "text",
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "MoneyManagementVariantsJson",
table: "BundleBacktestRequests",
type: "text",
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "TickerVariantsJson",
table: "BundleBacktestRequests",
type: "text",
nullable: false,
defaultValue: "");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "DateTimeRangesJson",
table: "BundleBacktestRequests");
migrationBuilder.DropColumn(
name: "MoneyManagementVariantsJson",
table: "BundleBacktestRequests");
migrationBuilder.DropColumn(
name: "TickerVariantsJson",
table: "BundleBacktestRequests");
migrationBuilder.RenameColumn(
name: "UniversalConfigJson",
table: "BundleBacktestRequests",
newName: "BacktestRequestsJson");
}
}
}

View File

@@ -329,10 +329,6 @@ namespace Managing.Infrastructure.Databases.Migrations
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("BacktestRequestsJson")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime?>("CompletedAt")
.HasColumnType("timestamp with time zone");
@@ -346,6 +342,10 @@ namespace Managing.Infrastructure.Databases.Migrations
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property<string>("DateTimeRangesJson")
.IsRequired()
.HasColumnType("text");
b.Property<string>("ErrorMessage")
.HasColumnType("text");
@@ -355,6 +355,10 @@ namespace Managing.Infrastructure.Databases.Migrations
b.Property<int>("FailedBacktests")
.HasColumnType("integer");
b.Property<string>("MoneyManagementVariantsJson")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(255)
@@ -375,9 +379,17 @@ namespace Managing.Infrastructure.Databases.Migrations
.IsRequired()
.HasColumnType("text");
b.Property<string>("TickerVariantsJson")
.IsRequired()
.HasColumnType("text");
b.Property<int>("TotalBacktests")
.HasColumnType("integer");
b.Property<string>("UniversalConfigJson")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("timestamp with time zone");

View File

@@ -30,7 +30,19 @@ public class BundleBacktestRequestEntity
[Required]
[Column(TypeName = "text")]
public string BacktestRequestsJson { get; set; } = string.Empty;
public string UniversalConfigJson { get; set; } = string.Empty;
[Required]
[Column(TypeName = "text")]
public string DateTimeRangesJson { get; set; } = string.Empty;
[Required]
[Column(TypeName = "text")]
public string MoneyManagementVariantsJson { get; set; } = string.Empty;
[Required]
[Column(TypeName = "text")]
public string TickerVariantsJson { get; set; } = string.Empty;
[Required]
public int TotalBacktests { get; set; }

View File

@@ -190,7 +190,10 @@ public class ManagingDbContext : DbContext
entity.Property(e => e.Status)
.IsRequired()
.HasConversion<string>(); // Store enum as string
entity.Property(e => e.BacktestRequestsJson).HasColumnType("text");
entity.Property(e => e.UniversalConfigJson).HasColumnType("text");
entity.Property(e => e.DateTimeRangesJson).HasColumnType("text");
entity.Property(e => e.MoneyManagementVariantsJson).HasColumnType("text");
entity.Property(e => e.TickerVariantsJson).HasColumnType("text");
entity.Property(e => e.ErrorMessage).HasColumnType("text");
entity.Property(e => e.ProgressInfo).HasColumnType("text");
entity.Property(e => e.CurrentBacktest).HasMaxLength(500);

View File

@@ -367,7 +367,10 @@ public static class PostgreSqlMappers
CreatedAt = entity.CreatedAt,
CompletedAt = entity.CompletedAt,
Status = entity.Status,
BacktestRequestsJson = entity.BacktestRequestsJson,
UniversalConfigJson = entity.UniversalConfigJson,
DateTimeRangesJson = entity.DateTimeRangesJson,
MoneyManagementVariantsJson = entity.MoneyManagementVariantsJson,
TickerVariantsJson = entity.TickerVariantsJson,
TotalBacktests = entity.TotalBacktests,
CompletedBacktests = entity.CompletedBacktests,
FailedBacktests = entity.FailedBacktests,
@@ -406,7 +409,10 @@ public static class PostgreSqlMappers
CreatedAt = bundleRequest.CreatedAt,
CompletedAt = bundleRequest.CompletedAt,
Status = bundleRequest.Status,
BacktestRequestsJson = bundleRequest.BacktestRequestsJson,
UniversalConfigJson = bundleRequest.UniversalConfigJson,
DateTimeRangesJson = bundleRequest.DateTimeRangesJson,
MoneyManagementVariantsJson = bundleRequest.MoneyManagementVariantsJson,
TickerVariantsJson = bundleRequest.TickerVariantsJson,
TotalBacktests = bundleRequest.TotalBacktests,
CompletedBacktests = bundleRequest.CompletedBacktests,
FailedBacktests = bundleRequest.FailedBacktests,

View File

@@ -12,8 +12,8 @@ public class PostgreSqlUserRepository : BaseRepositoryWithLogging, IUserReposito
{
private readonly ICacheService _cacheService;
public PostgreSqlUserRepository(ManagingDbContext context, ILogger<SqlQueryLogger> logger,
SentrySqlMonitoringService sentryMonitoringService, ICacheService cacheService)
public PostgreSqlUserRepository(ManagingDbContext context, ILogger<SqlQueryLogger> logger,
SentrySqlMonitoringService sentryMonitoringService, ICacheService cacheService)
: base(context, logger, sentryMonitoringService)
{
_cacheService = cacheService;
@@ -54,10 +54,10 @@ public class PostgreSqlUserRepository : BaseRepositoryWithLogging, IUserReposito
return null;
var user = PostgreSqlMappers.Map(userEntity);
// Cache user for 5 minutes since user data doesn't change frequently
_cacheService.SaveValue(cacheKey, user, TimeSpan.FromMinutes(5));
return user;
}
finally
@@ -71,20 +71,12 @@ public class PostgreSqlUserRepository : BaseRepositoryWithLogging, IUserReposito
{
return await ExecuteWithLoggingAsync(async () =>
{
// Check cache first for frequently accessed users
var cacheKey = fetchAccounts ? $"user_name_with_accounts_{name}" : $"user_name_{name}";
var cachedUser = _cacheService.GetValue<User>(cacheKey);
if (cachedUser != null)
{
return cachedUser;
}
try
{
await PostgreSqlConnectionHelper.EnsureConnectionOpenAsync(_context);
User user;
if (fetchAccounts)
{
// Fetch user with accounts in a single query
@@ -99,7 +91,7 @@ public class PostgreSqlUserRepository : BaseRepositoryWithLogging, IUserReposito
throw new InvalidOperationException($"User with name '{name}' not found");
user = PostgreSqlMappers.Map(userEntity);
// Map accounts using the existing mapper
if (userEntity.Accounts != null)
{
@@ -133,12 +125,7 @@ public class PostgreSqlUserRepository : BaseRepositoryWithLogging, IUserReposito
user = PostgreSqlMappers.Map(userEntity);
user.Accounts = new List<Account>(); // Initialize empty list
}
// Cache user for 5 minutes since user data doesn't change frequently
// Use shorter cache time when including accounts since accounts change more frequently
var cacheTime = fetchAccounts ? TimeSpan.FromMinutes(2) : TimeSpan.FromMinutes(5);
_cacheService.SaveValue(cacheKey, user, cacheTime);
return user;
}
finally
@@ -179,10 +166,10 @@ public class PostgreSqlUserRepository : BaseRepositoryWithLogging, IUserReposito
.ConfigureAwait(false);
var users = userEntities.Select(PostgreSqlMappers.Map).ToList();
// Cache all users for 10 minutes since this data changes infrequently
_cacheService.SaveValue(cacheKey, users, TimeSpan.FromMinutes(10));
return users;
}
finally
@@ -209,7 +196,7 @@ public class PostgreSqlUserRepository : BaseRepositoryWithLogging, IUserReposito
{
// Capture old AgentName before updating for cache invalidation
oldAgentName = existingUser.AgentName;
// Update existing user
existingUser.AgentName = user.AgentName;
existingUser.AvatarUrl = user.AvatarUrl;
@@ -229,7 +216,7 @@ public class PostgreSqlUserRepository : BaseRepositoryWithLogging, IUserReposito
// Update the user object with the database-generated ID after save
await _context.SaveChangesAsync().ConfigureAwait(false);
user.Id = userEntity.Id;
// Cache the new user
var newUserNameCacheKey = $"user_name_{user.Name}";
var newUserAgentCacheKey = $"user_agent_{user.AgentName}";
@@ -238,34 +225,34 @@ public class PostgreSqlUserRepository : BaseRepositoryWithLogging, IUserReposito
{
_cacheService.SaveValue(newUserAgentCacheKey, user, TimeSpan.FromMinutes(5));
}
// Invalidate all users cache since we added a new user
_cacheService.RemoveValue("all_users");
return; // Exit early since we already saved
}
await _context.SaveChangesAsync().ConfigureAwait(false);
// Invalidate cache for updated user - handle both old and new AgentName
var nameCacheKey = $"user_name_{user.Name}";
var nameWithAccountsCacheKey = $"user_name_with_accounts_{user.Name}";
_cacheService.RemoveValue(nameCacheKey);
_cacheService.RemoveValue(nameWithAccountsCacheKey);
// Invalidate old AgentName cache if it existed
if (!string.IsNullOrEmpty(oldAgentName))
{
var oldAgentCacheKey = $"user_agent_{oldAgentName}";
_cacheService.RemoveValue(oldAgentCacheKey);
}
// Invalidate new AgentName cache if it exists
if (!string.IsNullOrEmpty(user.AgentName))
{
var newAgentCacheKey = $"user_agent_{user.AgentName}";
_cacheService.RemoveValue(newAgentCacheKey);
}
// Invalidate all users cache since we updated a user
_cacheService.RemoveValue("all_users");
}