OpenAPITools / openapi-generator

OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec (v2, v3)
https://openapi-generator.tech
Apache License 2.0
21.77k stars 6.57k forks source link

slow response time with generated csharp (c# .net) #19278

Open millennIumAMbiguity opened 3 months ago

millennIumAMbiguity commented 3 months ago

Bug Report Checklist

Description

Sending my simple get request with Postman takes max 430ms on the first request, and 40-30ms for repeated. But using the generated c# classes, it takes 40000-50000ms in both async and non-async operations for the same request. repeated takes 71-74ms, which is faster but double than Postman. after repeating the same request 5 times, I sent a different request that took 120+ms and 90+ms on repeat.

Trying the same thing with NSwag, I get the same initial delay, but repeated request takes 22-25ms.

openapi-generator version

7.0.1

OpenAPI declaration file content or url

note that the yaml was simplified and unused components and paths were removed.

openapi: "3.0.3"
info:
  title: Client API
  version: "2.0"
security:
  - ProjectId: []
components:
  securitySchemes:
    ProjectId:
      description: >
        The ID of the project.
      type: apiKey
      in: header
      name: project-id
  schemas:
    error:
      type: object
      properties:
        message:
          type: string
    uuid:
      type: string
      format: uuid
    project:
      type: object
      required: [id]
      properties:
        id:
          $ref: "#/components/schemas/uuid"
  responses:
    badRequest:
      description: Invalid request
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/error"
    unauthenticated:
      description: Authentication failure
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/error"
    serverError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/error"
paths:
  /project:
    get:
      operationId: getProject
      summary: Get public project info
      responses:
        200:
          description: The project object.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/project"
        "4XX":
          $ref: "#/components/responses/badRequest"
        500:
          $ref: "#/components/responses/serverError"
Generation Details

This is the code gen command:

java -jar openapi-generator-cli-7.0.1.jar generate -i openapi.yaml -g csharp -o ./GeneratedClient --additional-properties targetFramework=net47,library=restsharp
Steps to reproduce
  1. Create and run a server that accepts your guid header.
  2. Generate using the info in the Generation Details
  3. run C# code:
    var client = new Org.OpenAPITools.Api.DefaultApi("https://client.api.myserver.com/");
    client.Configuration.DefaultHeaders.Add("project-id", "00000000-0000-0000-0000-000000000000");
    Console.WriteLine(client.GetProject().ToString());

    Note that I have replaced address and guid with generic values. The timing logs are also removed to make it easier to read.

Related issues/PRs

https://stackoverflow.com/questions/72814123/openapi-generator-c-net6-extremely-slow-http-requests-under-certain-condi https://stackoverflow.com/questions/754333/why-is-this-webrequest-code-slow

Suggest a fix

I believe that the issue of the initial reqest is that the request is configuring or auto-detecting proxies. This issue is also in NSwag.

request.Proxy = null;
millennIumAMbiguity commented 3 months ago

I have tried using Generichost but it is still slow to respond.

By tracing, I found that the delay is likely caused by _workItemsWaiting.WaitOne(); In AsyncHelpers.cs -> in public void Run() on line 98

while (!_done) {
    if (_items.TryDequeue(out var task)) {
        task.Item1(task.Item2);
        if (_caughtException == null) {
            continue;
        }
        _caughtException.Throw();
    }
    else {
        _workItemsWaiting.WaitOne();
    }
}

WaitOne() returns a true after delay. (Returns: true if the current instance receives a signal. If the current instance is never signaled, WaitOne(int, bool) never returns.) Its description reads as follows: Blocks the current thread until the current WaitHandle receives a signal.

devhl-labs commented 3 months ago

WaitOne does not appear anywhere in the code base.

millennIumAMbiguity commented 3 months ago

WaitOne does not appear anywhere in the code base.

_workItemsWaiting is part of System.Net.Http I belive