0

I have a default ASP .NET Core Web API application with a single handler:

[HttpGet("{x}")]
public string Get(string x)
{
    var guid = Guid.NewGuid();
    var start = DateTime.Now;
    Console.WriteLine($"{guid}\t1\tSTRT\t{start}");
    var sb = new StringBuilder();
    using (var conn = new OracleConnection(CONN_STR)) {
        using (var cmd = conn.CreateCommand()) {
            conn.Open();

            Console.WriteLine($"{guid}\t2\tCONN\t{DateTime.Now - start}");

            cmd.CommandText = "select hello4(:x) from dual";

            var nameParam = cmd.CreateParameter();
            nameParam.ParameterName = "x";
            nameParam.Value = x;
            cmd.Parameters.Add(nameParam);

            var ret = cmd.ExecuteScalar();

            if (ret is string xname) {
                sb.Append("{\"x\":");
                sb.Append(x);
                sb.Append("\",\"xname\":\"");
                sb.Append(xname);
                sb.Append("\"}");
            } else {
                sb.Append("{\"error\":\"no data found\"}");
            }
        }
    }
    Console.WriteLine($"{guid}\t3\tDONE\t{DateTime.Now - start}");
    return sb.ToString();
}

I load test it using vegeta: vegeta attack -targets=targets.txt -duration=10s -rate=100 -timeout=0 | vegeta report.

When hello4 is fast, I can see in the stdout that the handler is invoked 100 times per second.

When hello4 contains dbms_lock.sleep(1); to simulate extra processing time, I see that the handler is invoked much fewer times per second, about 20. I actually expected it to still be invoked about 100 times per second, placing extra stress on the DB and exhausting the SGA (my connection pool limit is 1024).

Why doesn't that happen and how can I force it to start handling more incoming connections simultaneously?

Alexey
  • 1,354
  • 13
  • 30
  • FYI, `var ret = await Task.Run((Func)cmd.ExecuteScalar);` didn't help. – Alexey Mar 15 '18 at 08:43
  • `.UseKestrel(options => { options.Limits.MaxConcurrentConnections = null; options.Limits.MaxConcurrentUpgradedConnections = null; })` when building the `IWebHost` didn't help either. – Alexey Mar 15 '18 at 09:06

1 Answers1

1

Running cmd.ExecuteScalar in a Task was the right idea, but it has to be a long running task to avoid blocking all threads in the application pool:

private static TaskFactory<object> tf = new TaskFactory<object>();
//and in the method
await tf.StartNew((Func<object>)cmd.ExecuteScalar, TaskCreationOptions.LongRunning).ConfigureAwait(false);

This allows Kestrel to keep handling incoming connections at the rate that they arrive.

Alexey
  • 1,354
  • 13
  • 30