0

SignalR hub running as an EKS pod handles 700 VUs via ALB but fails at 720+, yet successfully handles 1000 VUs via kubectl port-forward with 85% free CPU and 43% free memory. This confirms the application is healthy. I am new to AWS , I wanted to understand what might be the root cause this issue. I am performing a load test using k6 scripts.

Also, I am connected to Redis cache (AWS Elasticache) instance c6 large as backpane for scalability.

I am trying to understand is it server side resource exhaustion or ALB node scaling issue?

Error Logs-

    INFO[0524] [VU 728] Starting sustained connection test - Target: https://xxxxxxxxxxxxxxxxxxx/hubs/notification source=console
INFO[0524] [VU 728] Will send messages every 15000ms for 20 minutes source=console
WARN[0524] Request Failed error="Post "https://xxxxxxxxxxxxxxxxxxx/hubs/notification/negotiate\": EOF"
ERRO[0524] [VU 728] NEGOTIATION_HTTP_ERROR: HTTP error during negotiation source=console
ERRO[0524] [VU 728] Negotiation failed source=console
WARN[0524] Request Failed error="Post "https://xxxxxxxxxxxxxxxxxxx/hubs/notification/negotiate\": EOF"
ERRO[0524] [VU 728] NEGOTIATION_HTTP_ERROR: HTTP error during negotiation source=console
ERRO[0524] [VU 728] Negotiation failed source=console
WARN[0524] Request Failed error="Post "https://xxxxxxxxxxxxxxxxxxx/hubs/notification/negotiate\": EOF"
ERRO[0524] [VU 728] NEGOTIATION_HTTP_ERROR: HTTP error during negotiation source=console
ERRO[0524] [VU 728] Negotiation failed source=console
WARN[0524] Request Failed error="Post "https://xxxxxxxxxxxxxxxxxxx/hubs/notification/negotiate\": EOF"
ERRO[0524] [VU 728] NEGOTIATION_HTTP_ERROR: HTTP error during negotiation source=console
ERRO[0524] [VU 728] Negotiation failed source=console
WARN[0524] Request Failed error="Post "https://xxxxxxxxxxxxxxxxxxx/hubs/notification/negotiate\": EOF"
ERRO[0524] [VU 728] NEGOTIATION_HTTP_ERROR: HTTP error during negotiation source=console
INFO[0524] [VU 726] WebSocket opened source=console
ERRO[0524] [VU 728] Negotiation failed source=console
WARN[0524] Request Failed error="Post "https://xxxxxxxxxxxxxxxxxxx/hubs/notification/negotiate\": EOF"
ERRO[0524] [VU 728] NEGOTIATION_HTTP_ERROR: HTTP error during negotiation source=console
ERRO[0524] [VU 728] Negotiation failed source=console
WARN[0524] Request Failed error="Post "https://xxxxxxxxxxxxxxxxxxx/hubs/notification/negotiate\": EOF"
ERRO[0524] [VU 728] NEGOTIATION_HTTP_ERROR: HTTP error during negotiation source=console
ERRO[0524] [VU 728] Negotiation failed source=console
WARN[0524] Request Failed error="Post "https://xxxxxxxxxxxxxxxxxxx/hubs/notification/negotiate\": EOF"
ERRO[0524] [VU 728] NEGOTIATION_HTTP_ERROR: HTTP error during negotiation source=console
ERRO[0524] [VU 728] Negotiation failed source=console
INFO[0524] [VU 726] Handshake complete in 1067ms source=console
INFO[0525] [VU 729] Starting sustained connection test - Target: https://xxxxxxxxxxxxxxxxxxx/hubs/notification source=console
INFO[0525] [VU 729] Will send messages every 15000ms for 20 minutes source=console
WARN[0525] Request Failed error="Post "https://xxxxxxxxxxxxxxxxxxx/hubs/notification/negotiate\": EOF"
ERRO[0525] [VU 729] NEGOTIATION_HTTP_ERROR: HTTP error during negotiation source=console
ERRO[0525] [VU 729] Negotiation failed source=console
INFO[0525] [VU 727] WebSocket opened source=console
WARN[0525] Request Failed error="Post "https://xxxxxxxxxxxxxxxxxxx/hubs/notification/negotiate\": EOF"
ERRO[0525] [VU 729] NEGOTIATION_HTTP_ERROR: HTTP error during negotiation source=console
ERRO[0525] [VU 729] Negotiation failed source=console
WARN[0525] Request Failed error="Post "https://xxxxxxxxxxxxxxxxxxx/hubs/notification/negotiate\": EOF"
ERRO[0525] [VU 729] NEGOTIATION_HTTP_ERROR: HTTP error during negotiation source=console
ERRO[0525] [VU 729] Negotiation failed source=console
WARN[0525] Request Failed error="Post "https://xxxxxxxxxxxxxxxxxxx/hubs/notification/negotiate\": EOF"

SignalR configuration:-

public static IServiceCollection AddSignalRServices(this IServiceCollection services, IConfiguration configuration, IWebHostEnvironment environment)
{
    var signalRBuilder = services.AddSignalR(options =>
    {
                    
        options.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
           
        options.KeepAliveInterval = TimeSpan.FromSeconds(
            configuration.GetValue<int>("SignalR_KeepAliveSeconds", 30));

        options.HandshakeTimeout = TimeSpan.FromSeconds(
            configuration.GetValue<int>("SignalR_HandshakeTimeoutSeconds", 15));

        options.MaximumParallelInvocationsPerClient =
            configuration.GetValue<int>("SignalR_MaxParallelInvocations", 1);

        options.MaximumReceiveMessageSize =
            configuration.GetValue<long?>("SignalR_MaxMessageSize", 32 * 1024);

        options.StreamBufferCapacity =
            configuration.GetValue<int>("SignalR_StreamBufferCapacity", 10);


        // Adjusted settings for Debugging and performance tuning
        options.MaximumReceiveMessageSize = 32768; 
        options.StreamBufferCapacity = 5; // Reduced from 10

        // Timeouts - balance connection count vs responsiveness
        options.KeepAliveInterval = TimeSpan.FromSeconds(20); 
       
        options.HandshakeTimeout = TimeSpan.FromSeconds(20); 

        // Limit parallel invocations to reduce CPU load
        options.MaximumParallelInvocationsPerClient = 1;

        options.EnableDetailedErrors = false; // Disable in production




    });

    signalRBuilder.AddMessagePackProtocol();

    services.AddRedisBackplane(signalRBuilder, configuration, environment);

    return services;
}
 private static void AddRedisBackplane(
        this IServiceCollection services,
        ISignalRServerBuilder signalRBuilder,
        IConfiguration configuration,
        IWebHostEnvironment environment)
    {
             

        var redisConfig = configuration.GetRedisConfiguration();
        var redisHost = redisConfig.Host;
        var redisPort = redisConfig.Port;
        var redisPassword = redisConfig.Password;

        if (string.IsNullOrEmpty(redisHost))
        {
            Console.WriteLine("Redis backplane is enabled but Redis host is not configured.");
            return;
        }
        var useSSL = configuration.GetValue<bool>("RedisConfig_UseSsl", true);
        var redisConnectionString = BuildRedisConnectionString(redisHost, redisPort, redisPassword, useSSL);

        signalRBuilder.AddStackExchangeRedis(redisConnectionString, options =>
        {
            options.Configuration.ChannelPrefix = StackExchange.Redis.RedisChannel.Literal("SignalR");
            options.Configuration.AbortOnConnectFail = false;
            options.Configuration.ConnectTimeout = 15000;
            options.Configuration.SyncTimeout = 30000;
            options.Configuration.AsyncTimeout = 30000;               
            options.Configuration.KeepAlive = 60;
            options.Configuration.ConnectRetry = 3;
            options.Configuration.ReconnectRetryPolicy = new StackExchange.Redis.ExponentialRetry(5000);
            
            options.Configuration.DefaultDatabase = 0;
            options.Configuration.Proxy = StackExchange.Redis.Proxy.None;
            
           
        });

            }

Kestrel Configuration:-

 ThreadPool.SetMinThreads(200, 200);
        Console.WriteLine("[ThreadPool] Min threads set to 200 for high concurrency (SignalR 1000+ connections)");


        builder.WebHost.ConfigureKestrel(options => 
        {
          
            options.Limits.KeepAliveTimeout = TimeSpan.FromSeconds(280);
            
            options.Limits.RequestHeadersTimeout = TimeSpan.FromSeconds(60);
            options.Limits.Http2.KeepAlivePingDelay = TimeSpan.FromSeconds(10);

            options.Limits.MaxConcurrentConnections = 5100;  
            options.Limits.MaxConcurrentUpgradedConnections = 5100;
          
            options.Limits.MinRequestBodyDataRate = null;
            options.Limits.MinResponseDataRate = null;
           
            options.Limits.RequestHeadersTimeout = TimeSpan.FromSeconds(30);
            options.Limits.MinRequestBodyDataRate = null;
            options.Limits.MinResponseDataRate = null;

            options.Limits.MaxRequestBodySize = 10 * 1024;
            options.Limits.MaxRequestBufferSize = 1024 * 1024; 
            options.Limits.MaxRequestHeadersTotalSize = 32 * 1024; 

  
        });

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.