Fix reminding for livetradingbot

This commit is contained in:
2025-09-18 12:19:52 +07:00
parent f1bb40fb75
commit ffe69356d8
8 changed files with 199 additions and 10 deletions

View File

@@ -1,5 +0,0 @@
namespace Managing.Application.Bots;
public class BotReminderInitializer
{
}

View File

@@ -0,0 +1,129 @@
using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Grains;
using Managing.Core;
using Managing.Domain.Bots;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using static Managing.Common.Enums;
namespace Managing.Application.Bots.Grains;
/// <summary>
/// Grain that initializes bot reminders on application startup and periodically checks for running bots.
/// Fetches all running bots and pings them to ensure their reminders are properly registered.
/// This grain ensures that only one instance runs the initialization process
/// even in multi-silo environments.
/// </summary>
public class BotReminderInitializerGrain : Grain, IBotReminderInitializerGrain, IRemindable
{
private readonly IServiceScopeFactory _scopeFactory;
private readonly ILogger<BotReminderInitializerGrain> _logger;
private const string CheckRunningBotsReminderName = "CheckRunningBotsReminder";
public BotReminderInitializerGrain(
IServiceScopeFactory scopeFactory,
ILogger<BotReminderInitializerGrain> logger)
{
_scopeFactory = scopeFactory;
_logger = logger;
}
/// <summary>
/// Initializes bot reminders by fetching all running bots and pinging them
/// to ensure their reminders are properly registered.
/// </summary>
public async Task InitializeBotRemindersAsync()
{
try
{
_logger.LogInformation("BotReminderInitializerGrain starting - fetching running bots to reactivate reminders");
// Get all running bots from the database
var runningBots = await GetRunningBotsAsync();
if (!runningBots.Any())
{
_logger.LogInformation("No running bots found to reactivate");
return;
}
_logger.LogInformation("Found {Count} running bots to reactivate", runningBots.Count());
// Ping each running bot to reactivate it and ensure reminders are registered
var tasks = runningBots.Select(async bot =>
{
try
{
_logger.LogDebug("Pinging bot {BotId} ({BotName}) to reactivate", bot.Identifier, bot.Name);
var grain = GrainFactory.GetGrain<ILiveTradingBotGrain>(bot.Identifier);
var success = await grain.PingAsync();
if (success)
{
_logger.LogDebug("Successfully pinged bot {BotId} ({BotName})", bot.Identifier, bot.Name);
}
else
{
_logger.LogWarning("Ping failed for bot {BotId} ({BotName})", bot.Identifier, bot.Name);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to ping bot {BotId} ({BotName})", bot.Identifier, bot.Name);
SentrySdk.CaptureException(ex);
}
});
// Wait for all pings to complete
await Task.WhenAll(tasks);
// Register a reminder to check running bots every hour
// Start immediately and repeat every hour
await this.RegisterOrUpdateReminder(
CheckRunningBotsReminderName,
TimeSpan.FromHours(1), // Start in 1 hour
TimeSpan.FromHours(1)); // Repeat every hour
_logger.LogInformation("BotReminderInitializerGrain completed - processed {Count} running bots", runningBots.Count());
}
catch (Exception ex)
{
_logger.LogError(ex, "Error during BotReminderInitializerGrain initialization");
SentrySdk.CaptureException(ex);
}
}
/// <summary>
/// Handles reminder callbacks for periodic bot checking
/// </summary>
public Task ReceiveReminder(string reminderName, TickStatus status)
{
if (reminderName == CheckRunningBotsReminderName)
{
_logger.LogDebug("Received hourly reminder to check running bots");
return InitializeBotRemindersAsync();
}
return Task.CompletedTask;
}
/// <summary>
/// Fetches all bots with Running status from the database
/// </summary>
private async Task<IEnumerable<Bot>> GetRunningBotsAsync()
{
try
{
return await ServiceScopeHelpers.WithScopedService<IBotService, IEnumerable<Bot>>(
_scopeFactory,
async botService => await botService.GetBotsByStatusAsync(BotStatus.Running));
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to fetch running bots from database");
return Enumerable.Empty<Bot>();
}
}
}

View File

@@ -43,8 +43,8 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
public override async Task OnActivateAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("LiveTradingBotGrain {GrainId} activated", this.GetPrimaryKey());
await base.OnActivateAsync(cancellationToken);
await ResumeBotIfRequiredAsync();
await base.OnActivateAsync(cancellationToken);
}
public override async Task OnDeactivateAsync(DeactivationReason reason, CancellationToken cancellationToken)
@@ -795,4 +795,27 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
_logger.LogError(ex, "Failed to send swap notification for bot {BotId}", _tradingBot?.Identifier);
}
}
/// <summary>
/// Pings the bot to reactivate it and ensure reminders are registered
/// Used during startup to reactivate bots that may have lost their reminders
/// The grain activation will automatically handle reminder registration
/// </summary>
public Task<bool> PingAsync()
{
try
{
_logger.LogInformation("Ping received for LiveTradingBotGrain {GrainId}", this.GetPrimaryKey());
// The grain activation (OnActivateAsync) will automatically call ResumeBotIfRequiredAsync()
// which handles checking the registry status and re-registering reminders if needed
// So we just need to return true to indicate the ping was received
return Task.FromResult(true);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error during ping for LiveTradingBotGrain {GrainId}", this.GetPrimaryKey());
return Task.FromResult(false);
}
}
}

View File

@@ -4,17 +4,35 @@ using static Managing.Common.Enums;
namespace Managing.Application.Grains;
public class PriceFetcherInitializer : IHostedService
public class GrainInitializer : IHostedService
{
private readonly IGrainFactory _grainFactory;
public PriceFetcherInitializer(IClusterClient grainFactory)
public GrainInitializer(IClusterClient grainFactory)
{
_grainFactory = grainFactory;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
await InitializePriceFetcherAsync();
await InitializeBotRemindersAsync();
}
public async Task InitializeBotRemindersAsync()
{
if (Environment.GetEnvironmentVariable("TASK_SLOT") != "1")
return;
var grain = _grainFactory.GetGrain<IBotReminderInitializerGrain>(0);
await grain.InitializeBotRemindersAsync();
}
public async Task InitializePriceFetcherAsync()
{
if (Environment.GetEnvironmentVariable("TASK_SLOT") != "1")
return;
// Initialize grains for different timeframes
var timeframes = new[]
{