From c7c89c903f63c95702cb619a24170e68f7f237ce Mon Sep 17 00:00:00 2001 From: cryptooda Date: Sun, 23 Nov 2025 21:49:23 +0700 Subject: [PATCH] Enhance error handling and logging in BotController, LiveTradingBotGrain, and BotService - Added specific handling for ServiceUnavailableException in BotController to return user-friendly messages. - Improved logging for Orleans exceptions in both BotController and BotService to provide clearer context during errors. - Implemented verification of position closure status in LiveTradingBotGrain, including timeout handling for closing positions. - Enhanced logging for critical and non-critical operations during bot stop processes to ensure better traceability. --- .../Exceptions/OrleansExceptionHelper.cs | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 src/Managing.Core/Exceptions/OrleansExceptionHelper.cs diff --git a/src/Managing.Core/Exceptions/OrleansExceptionHelper.cs b/src/Managing.Core/Exceptions/OrleansExceptionHelper.cs new file mode 100644 index 00000000..1494263a --- /dev/null +++ b/src/Managing.Core/Exceptions/OrleansExceptionHelper.cs @@ -0,0 +1,119 @@ +using Microsoft.Extensions.Logging; + +namespace Managing.Core.Exceptions; + +/// +/// Helper class to detect and handle Orleans-specific exceptions +/// +public static class OrleansExceptionHelper +{ + /// + /// Checks if an exception is an Orleans-specific exception + /// + public static bool IsOrleansException(Exception ex) + { + if (ex == null) return false; + + var exceptionTypeName = ex.GetType().Name; + var exceptionMessage = ex.Message ?? string.Empty; + + // Check for Orleans exception type names + if (exceptionTypeName.Contains("Orleans", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + // Check for Orleans-specific exception messages + if (exceptionMessage.Contains("Orleans", StringComparison.OrdinalIgnoreCase) || + exceptionMessage.Contains("grain", StringComparison.OrdinalIgnoreCase) || + exceptionMessage.Contains("silo", StringComparison.OrdinalIgnoreCase) || + exceptionMessage.Contains("activation", StringComparison.OrdinalIgnoreCase) || + exceptionMessage.Contains("forwarding failed", StringComparison.OrdinalIgnoreCase) || + exceptionMessage.Contains("Unable to create local activation", StringComparison.OrdinalIgnoreCase) || + exceptionMessage.Contains("Request timeout", StringComparison.OrdinalIgnoreCase) || + exceptionMessage.Contains("Deadlock", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + // Check inner exception recursively + if (ex.InnerException != null) + { + return IsOrleansException(ex.InnerException); + } + + return false; + } + + /// + /// Converts an Orleans exception to a user-friendly message + /// + public static string GetUserFriendlyMessage(Exception ex, string operation = "operation") + { + if (!IsOrleansException(ex)) + { + return ex.Message; + } + + var exceptionTypeName = ex.GetType().Name; + var exceptionMessage = ex.Message ?? string.Empty; + + // Handle timeout exceptions + if (ex is TimeoutException || ex is TaskCanceledException || + exceptionMessage.Contains("timeout", StringComparison.OrdinalIgnoreCase) || + exceptionMessage.Contains("timed out", StringComparison.OrdinalIgnoreCase)) + { + return $"The {operation} timed out. This may occur when closing positions takes longer than expected. Please try again in a moment."; + } + + // Handle deadlock exceptions + if (exceptionMessage.Contains("deadlock", StringComparison.OrdinalIgnoreCase)) + { + return $"The {operation} could not complete due to a system conflict. Please try again in a moment."; + } + + // Handle activation/grain errors + if (exceptionMessage.Contains("activation", StringComparison.OrdinalIgnoreCase) || + exceptionMessage.Contains("grain", StringComparison.OrdinalIgnoreCase) || + exceptionMessage.Contains("forwarding failed", StringComparison.OrdinalIgnoreCase)) + { + return $"The {operation} encountered a temporary system issue. Please try again in a moment."; + } + + // Generic Orleans error + return $"The {operation} encountered a temporary system issue. Please try again in a moment. If the problem persists, contact support."; + } + + /// + /// Wraps an async operation with timeout and Orleans exception handling + /// + public static async Task ExecuteWithTimeoutAndOrleansHandling( + Func> operation, + TimeSpan timeout, + string operationName, + ILogger logger = null) + { + try + { + using var cts = new CancellationTokenSource(timeout); + var task = operation(); + var timeoutTask = Task.Delay(timeout, cts.Token); + + var completedTask = await Task.WhenAny(task, timeoutTask); + if (completedTask == timeoutTask) + { + throw new TimeoutException($"The {operationName} timed out after {timeout.TotalSeconds} seconds."); + } + + return await task; + } + catch (Exception ex) when (IsOrleansException(ex) || ex is TimeoutException) + { + logger?.LogError(ex, "Orleans exception or timeout during {OperationName}: {ExceptionType} - {Message}", + operationName, ex.GetType().Name, ex.Message); + var userMessage = GetUserFriendlyMessage(ex, operationName); + throw new ServiceUnavailableException(userMessage); + } + } +} +