mirror of
https://github.com/langflow-ai/langflow.git
synced 2026-07-24 00:03:32 +08:00
feat: Enhance config loading by applying GUNICORN_CMD_ARGS before programmatic options (#12313)
* feat: Enhance config loading by applying GUNICORN_CMD_ARGS before programmatic options
* docs: Add Gunicorn configuration details to environment variables documentation
* Update src/backend/base/langflow/server.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update src/backend/base/langflow/server.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* add unit test
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@logspace.ai>
Co-authored-by: Jordan Frazier <122494242+jordanrfrazier@users.noreply.github.com>
Co-authored-by: Jordan Frazier <jordan.frazier@datastax.com>
(cherry picked from commit 9d14d3b34e)
This commit is contained in:
committed by
Eric Hare
parent
5d6a7a0f31
commit
89d1c60bed
@ -417,6 +417,20 @@ The following environment variables set base Langflow server configuration, such
|
||||
| `LANGFLOW_CELERY_ENABLED` | Boolean | `False` | Enable Celery for distributed task processing. |
|
||||
| `LANGFLOW_ALEMBIC_LOG_TO_STDOUT` | Boolean | `False` | Whether to log Alembic database migration output to stdout instead of a log file. If `true`, Alembic logs to `stdout` and the default log file is ignored. |
|
||||
|
||||
#### Gunicorn configuration
|
||||
|
||||
When running Langflow in a production environment using Gunicorn, you can configure Gunicorn workers using the `GUNICORN_CMD_ARGS` environment variable.
|
||||
|
||||
This is particularly useful to manage worker memory and prevent potential memory leaks over time. You can add it to your `.env` file like this:
|
||||
|
||||
```text
|
||||
GUNICORN_CMD_ARGS="--max-requests 100 --max-requests-jitter 20"
|
||||
```
|
||||
|
||||
Here is what these arguments do:
|
||||
- `--max-requests 100`: Automatically restarts a worker after it has processed 100 requests. This helps prevent memory leaks from accumulating over time.
|
||||
- `--max-requests-jitter 20`: Adds a random jitter of up to 20 requests to the `--max-requests` value. This ensures that all workers don't restart simultaneously, which could cause a brief downtime or spikes in latency.
|
||||
|
||||
For more information about deploying Langflow servers, see [Langflow deployment overview](/deployment-overview).
|
||||
|
||||
### Storage
|
||||
|
||||
@ -71,6 +71,18 @@ class LangflowApplication(BaseApplication):
|
||||
super().__init__()
|
||||
|
||||
def load_config(self) -> None:
|
||||
# Apply options from GUNICORN_CMD_ARGS env var before programmatic options
|
||||
parser = self.cfg.parser()
|
||||
cmd_args = self.cfg.get_cmd_args_from_env()
|
||||
if cmd_args:
|
||||
env_args = parser.parse_args(cmd_args)
|
||||
for k, v in vars(env_args).items():
|
||||
# Skip unset/positional args and only apply known settings
|
||||
if v is None or k == "args" or k not in self.cfg.settings:
|
||||
continue
|
||||
self.cfg.set(k.lower(), v)
|
||||
|
||||
# Programmatic options override env args
|
||||
config = {key: value for key, value in self.options.items() if key in self.cfg.settings and value is not None}
|
||||
for key, value in config.items():
|
||||
self.cfg.set(key.lower(), value)
|
||||
|
||||
55
src/backend/tests/unit/base/test_server.py
Normal file
55
src/backend/tests/unit/base/test_server.py
Normal file
@ -0,0 +1,55 @@
|
||||
import pytest
|
||||
|
||||
from langflow.server import LangflowApplication
|
||||
|
||||
|
||||
def _make_app(options=None, env_args=None, monkeypatch=None):
|
||||
"""Create a LangflowApplication with a dummy WSGI app.
|
||||
|
||||
Args:
|
||||
options: Programmatic options passed to LangflowApplication.
|
||||
env_args: If provided, set GUNICORN_CMD_ARGS env var before construction.
|
||||
monkeypatch: pytest monkeypatch fixture for env manipulation.
|
||||
"""
|
||||
if env_args is not None and monkeypatch is not None:
|
||||
monkeypatch.setenv("GUNICORN_CMD_ARGS", env_args)
|
||||
|
||||
def dummy_app(environ, start_response):
|
||||
pass
|
||||
|
||||
return LangflowApplication(dummy_app, options=options)
|
||||
|
||||
|
||||
class TestGunicornEnvArgs:
|
||||
def test_env_args_applied(self, monkeypatch):
|
||||
"""GUNICORN_CMD_ARGS values should be reflected in the config."""
|
||||
app = _make_app(env_args="--max-requests 100 --max-requests-jitter 20", monkeypatch=monkeypatch)
|
||||
|
||||
assert app.cfg.settings["max_requests"].get() == 100
|
||||
assert app.cfg.settings["max_requests_jitter"].get() == 20
|
||||
|
||||
def test_programmatic_options_override_env(self, monkeypatch):
|
||||
"""Programmatic options must take precedence over GUNICORN_CMD_ARGS."""
|
||||
app = _make_app(
|
||||
options={"workers": 2},
|
||||
env_args="--workers 8",
|
||||
monkeypatch=monkeypatch,
|
||||
)
|
||||
|
||||
assert app.cfg.settings["workers"].get() == 2
|
||||
|
||||
def test_no_env_var_uses_defaults(self):
|
||||
"""Without GUNICORN_CMD_ARGS, Gunicorn defaults should remain intact."""
|
||||
app = _make_app()
|
||||
|
||||
# Gunicorn default for max_requests is 0 (disabled)
|
||||
assert app.cfg.settings["max_requests"].get() == 0
|
||||
|
||||
def test_env_does_not_override_worker_class(self, monkeypatch):
|
||||
"""worker_class is always set programmatically and must not be overridden by env."""
|
||||
app = _make_app(
|
||||
env_args="--worker-class sync",
|
||||
monkeypatch=monkeypatch,
|
||||
)
|
||||
|
||||
assert app.cfg.settings["worker_class"].get() == "langflow.server.LangflowUvicornWorker"
|
||||
Reference in New Issue
Block a user