11

FastAPI uses gunicorn to launch uvicorn workers as described in https://www.uvicorn.org/settings/

However gunicorn does not allow to launch uvicorn with custom settings as also mentiond in https://github.com/encode/uvicorn/issues/343

The issue suggested to override the config_kwargs in the source file like https://github.com/encode/uvicorn/blob/master/uvicorn/workers.py

We tried it but uvicorn is not honoring the setting limit_concurrency in multiple uvicorn files in the source:

https://github.com/encode/uvicorn/blob/master/uvicorn/workers.py

# fail

        config_kwargs = {
            "app": None,
            "log_config": None,
            "timeout_keep_alive": self.cfg.keepalive,
            "timeout_notify": self.timeout,
            "callback_notify": self.callback_notify,
            "limit_max_requests": self.max_requests, "limit_concurrency": 10000,
            "forwarded_allow_ips": self.cfg.forwarded_allow_ips,
        }

https://github.com/encode/uvicorn/blob/master/uvicorn/main.py

# fail

    kwargs = {
        "app": app,
        "host": host,
        "port": port,
        "uds": uds,
        "fd": fd,
        "loop": loop,
        "http": http,
        "ws": ws,
        "lifespan": lifespan,
        "env_file": env_file,
        "log_config": LOGGING_CONFIG if log_config is None else log_config,
        "log_level": log_level,
        "access_log": access_log,
        "interface": interface,
        "debug": debug,
        "reload": reload,
        "reload_dirs": reload_dirs if reload_dirs else None,
        "workers": workers,
        "proxy_headers": proxy_headers,
        "forwarded_allow_ips": forwarded_allow_ips,
        "root_path": root_path,
        "limit_concurrency": 10000,
        "backlog": backlog,
        "limit_max_requests": limit_max_requests,
        "timeout_keep_alive": timeout_keep_alive,
        "ssl_keyfile": ssl_keyfile,
        "ssl_certfile": ssl_certfile,
        "ssl_version": ssl_version,
        "ssl_cert_reqs": ssl_cert_reqs,
        "ssl_ca_certs": ssl_ca_certs,
        "ssl_ciphers": ssl_ciphers,
        "headers": list([header.split(":") for header in headers]),
        "use_colors": use_colors,
    }

How can uvicorn be forced to honor this setting? We are still getting 503 errors from the FastAPI

-------UPDATE----------- the gunicorn setting --worker-connections 1000 still causes 503 when making 100 parallel requests that are distributed to many workers.

However, I believe it is a bit more complicated issue: our API endpoint does a lot of heavy workload, usually takses 5 seconds to complete.

Stress test with 2 cores, 2 workers:

  • A. 100+ concurrent requests, endpoint heavy load --worker-connections 1
  • B. 100+ concurrent requests, endpoint heavy load --worker-connections 1000
  • C. 100+ concurrent requests, endpoint low load --worker-connections 1
  • D. 100+ concurrent requests, endpoint low load --worker-connections 1000

Both experiments A und B yielded 503 responses, so assuming the worker-connections setting does work, too many simulatenous connections seem to not cause our 503 errors.

We are puzzled about this behavior, because we expect gunicorn/uvicorn to queue the work and not throw 503 errors.

user670186
  • 2,588
  • 6
  • 37
  • 55
  • Very interested to know whether you resolved your 503 errors in the end, if so what combination of concurrency and backlog settings did you find worked best? – Ed Randall Feb 25 '22 at 12:07

1 Answers1

9

From the gunicorn doc

worker-connections

The maximum number of simultaneous clients.

and from uvicorn doc

limit-concurrency

Maximum number of concurrent connections or tasks to allow, before issuing HTTP 503 responses.

According to this info, both settings variable doing the same thing. So

uvicorn --limit-concurrency 100 application:demo_app

is almost same as

gunicorn --worker-connections 100 -k uvicorn.workers.UvicornWorker application:demo_app

Note: I haven't done any real test on this, please do correct me if I am wrong.


Also, you can set limit-concurrency (or limit_concurrency) by subclassing the uvicorn.workers.UvicornWorker class

from uvicorn.workers import UvicornWorker


class CustomUvicornWorker(UvicornWorker):
    CONFIG_KWARGS = {
        "loop": "uvloop",
        "http": "httptools",
        "limit_concurrency": 100
    }

and now use this CustomUvicornWorker with gunicorn command as,

gunicorn -k path.to.custom_worker.CustomUvicornWorker application:demo_app

Note: you can inspect self.config.limit_concurrency in CustomUvicornWorker class to ensure the value has been set correctly.

JPG
  • 82,442
  • 19
  • 127
  • 206
  • thanks for this! the gunicorn --worker-connections 1000 did not have any impact, I am still getting the 503 when I make many parallel requests. However our API endpoint does a lot of heavy workload, usually takses 5 seconds to complete. Yet we must do stress tests do see what happens if 100 customers do this request at the same time. This behavior with 503 response happens – user670186 Aug 19 '20 at 09:01
  • I am not sure why that happe d to you, but, you can try three solutions. 1st, 2nd and combination of 1st and 2nd. – JPG Aug 19 '20 at 09:03
  • Yeah, I am not sure, if the 503 is due to concurrent connections, or other resource overload. I will also try the CustomUvicornWorker approach and test it – user670186 Aug 19 '20 at 09:22
  • 3
    did you resolve this? – Crashalot Dec 04 '21 at 01:26
  • For gunicorn with uvicorn class worker-connections has no affect per documentation https://docs.gunicorn.org/en/stable/settings.html#worker-connections The maximum number of simultaneous clients. This setting only affects the gthread, eventlet and gevent worker types. – Gorkem Aug 22 '23 at 19:46