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:
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.
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.