From 35928d552801ecd6eaf9e647abc23f89a940f20a Mon Sep 17 00:00:00 2001 From: cryptooda Date: Wed, 7 Jan 2026 17:18:42 +0700 Subject: [PATCH] Enhance SignalR Redis backplane configuration with robust connection options - Added detailed connection options for StackExchange.Redis to improve SignalR backplane reliability. - Implemented retry logic and connection settings to handle temporary Redis unavailability. - Updated logging to provide clearer feedback on configuration success or failure, including stack trace information for error handling. - Ensured fallback to single-instance mode when Redis is not configured, enhancing application resilience. --- src/Managing.Api/Program.cs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/Managing.Api/Program.cs b/src/Managing.Api/Program.cs index 00822dbd..db271243 100644 --- a/src/Managing.Api/Program.cs +++ b/src/Managing.Api/Program.cs @@ -30,6 +30,7 @@ using Serilog.Sinks.Elasticsearch; using OpenApiSecurityRequirement = Microsoft.OpenApi.Models.OpenApiSecurityRequirement; using OpenApiSecurityScheme = NSwag.OpenApiSecurityScheme; using DotNetEnv; +using StackExchange.Redis; // Optionally load .env file if it exists (primarily for Vibe Kanban worktrees) // This is optional - if no .env file exists, the app will use system env vars and appsettings.json @@ -474,18 +475,42 @@ if (!string.IsNullOrWhiteSpace(redisConnectionString)) try { Console.WriteLine($"✅ Configuring SignalR with Redis backplane"); - signalRBuilder.AddStackExchangeRedis(redisConnectionString, options => + + // Parse and configure connection options properly + // This ensures Redis backplane works without sticky sessions by storing connection state in Redis + var connectionOptions = ConfigurationOptions.Parse(redisConnectionString); + + // CRITICAL: These settings allow Redis backplane to work without sticky sessions + connectionOptions.AbortOnConnectFail = false; // Don't fail if Redis is temporarily unavailable + connectionOptions.ConnectTimeout = 10000; // 10 second timeout + connectionOptions.ConnectRetry = 5; // Retry 5 times + connectionOptions.KeepAlive = 60; // Keep connection alive + connectionOptions.SyncTimeout = 5000; + connectionOptions.AsyncTimeout = 5000; + connectionOptions.ReconnectRetryPolicy = new ExponentialRetry(1000, 10000); // Exponential backoff: 1s to 10s + + // Convert ConfigurationOptions back to connection string format with all settings + var configuredConnectionString = connectionOptions.ToString(); + + signalRBuilder.AddStackExchangeRedis(configuredConnectionString, options => { // Configure channel prefix for SignalR messages options.Configuration.ChannelPrefix = "managing-signalr"; }); + + Console.WriteLine($"✅ SignalR Redis backplane configured successfully with retry logic"); } catch (Exception ex) { Console.WriteLine($"⚠️ Failed to configure SignalR Redis backplane: {ex.Message}"); + Console.WriteLine($"⚠️ Stack trace: {ex.StackTrace}"); Console.WriteLine("SignalR will work in single-instance mode only"); } } +else +{ + Console.WriteLine("ℹ️ Redis not configured - SignalR running in single-instance mode"); +} builder.Services.AddScoped();