3498 Commits

Author SHA1 Message Date
Benoit Chesneau
32e46a58ce
Merge pull request #3522 from bysiber/fix-sd-notify-unbound-error
fix: prevent UnboundLocalError in sd_notify when socket creation fails
2026-02-20 13:42:51 +01:00
Benoit Chesneau
cef6b337d8
Merge pull request #3519 from benoitc/fix/gthread-slow-client-resilience
fix(gthread): prevent thread pool exhaustion from slow clients
2026-02-20 10:18:08 +01:00
Benoit Chesneau
eb6b69377c
Merge pull request #3513 from benoitc/feature/asgi-performance-optimizations
perf(asgi): optimize HTTP parser for improved performance
2026-02-20 10:17:38 +01:00
Kadir Can Ozden
6ada1ce03a fix: ensure pidfile fd is closed on rename failure 2026-02-20 06:17:29 +03:00
Kadir Can Ozden
41c6bf8e3e fix: prevent UnboundLocalError in sd_notify when socket creation fails 2026-02-20 06:16:54 +03:00
Benoit Chesneau
d3f80e8cfd fix(ctl): prevent fork deadlock and file watcher triggers
Fix two issues with the control socket feature introduced in 25.1.0:

1. Fork deadlock (#3509): The control server runs asyncio in a background
   thread. When fork() is called to spawn workers, locks held by asyncio
   can remain locked in the child process, causing deadlocks. Fix by
   stopping the control server before fork and restarting after fork in
   the parent.

2. File watcher triggers (#3508): The default socket path `gunicorn.ctl`
   was created in the app directory, triggering file watchers in dev
   servers (e.g., GAE dev_appserver). Fix by changing the default path
   to `/tmp/gunicorn.ctl`.

Fixes #3508
Fixes #3509
2026-02-19 15:57:41 +01:00
Benoit Chesneau
b5f127e99b fix(gthread): prevent thread pool exhaustion from slow clients
Add a timeout when waiting for initial request data in worker threads.
If no data arrives within 5 seconds, the connection is deferred back
to the main poller instead of blocking the thread indefinitely.

This fixes a regression from v24 where connections were submitted
directly to the thread pool after accept(). In v23, connections were
registered with the poller first and only submitted when data arrived.

The new hybrid approach maintains the performance benefits for fast
clients (immediate processing) while protecting against slow-client
scenarios that can exhaust the thread pool and cause health check
timeouts.

Changes:
- Add _DEFER sentinel and DEFAULT_WORKER_DATA_TIMEOUT constant
- Add TConn.wait_for_data() method using selectors
- Add TConn.data_ready flag to track data availability
- Add pending_conns deque for deferred connections
- Add on_pending_socket_readable() callback
- Add murder_pending() to clean up timed-out pending connections
- Modify handle() to wait for data with timeout before processing
- Modify finish_request() to handle _DEFER and register with poller

Fixes #3518
2026-02-19 15:21:01 +01:00
Benoit Chesneau
3f0c1f9ce5 fix(lint): remove unused io import 2026-02-19 01:02:14 +01:00
lukqw
aa29987be5 chore(logging): enrich request handling log line with request method 2026-02-18 10:12:14 +01:00
Benoit Chesneau
b3b98b1322 perf(asgi): optimize HTTP parser for improved performance
- Read chunk size lines and trailers in 64-byte blocks instead of 1 byte
  at a time, pushing back excess data to the unreader buffer
- Reuse BytesIO buffers with truncate/seek instead of creating new
  objects to reduce GC pressure in AsyncUnreader
- Use bytearray.find() directly instead of converting to bytes first
  in header parsing loop
- Use index-based iteration for header parsing instead of list.pop(0)
  which is O(n) per pop vs O(1) for index access

Add tests for the optimized parsing code paths.
2026-02-18 10:00:46 +01:00
Benoit Chesneau
2d4310116d docs: merge gunicornc into 25.1.0 release 2026-02-13 12:03:48 +01:00
Benoit Chesneau
bf4ad8d610 docs: update 25.1.0 release date to 2026-02-13 2026-02-13 11:59:42 +01:00
Benoit Chesneau
730350eb9f
Merge pull request #3505 from benoitc/feature/gunicornc-control-interface
feat(ctl): add gunicornc control interface
2026-02-13 11:31:45 +01:00
Benoit Chesneau
63df19bd5c fix(tests): use process groups for reliable signal handling in PyPy
- Use preexec_fn=os.setsid to create new process group
- Send signals to process group with os.killpg() instead of single process
- Add explicit timeout and graceful-timeout to gunicorn command
- Fixes test failures on PyPy 3.10 where signals weren't propagating properly
2026-02-13 11:02:10 +01:00
Benoit Chesneau
cd77bcc941 fix(tests): increase wait time for all server tests 2026-02-13 10:48:41 +01:00
Benoit Chesneau
02ea9855c1 fix(tests): improve server test reliability on FreeBSD 2026-02-13 10:37:22 +01:00
Benoit Chesneau
6d81c9ebcd fix: resolve pylint warnings 2026-02-13 02:40:01 +01:00
Benoit Chesneau
7486baa0ad fix: remove unused imports 2026-02-13 02:35:02 +01:00
Eng Zer Jun
54eb411d34
Enable hard_linebreaks
Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2026-02-13 09:31:17 +08:00
Benoit Chesneau
3e60d2942d docs: add gunicornc control interface guide
- Add guides/gunicornc.md with usage examples and command reference
- Update mkdocs.yml navigation to include Control Interface guide
- Update 2026-news.md and news.md changelog with 25.2.0 release
- Regenerate reference/settings.md with control socket settings
2026-02-13 02:29:44 +01:00
Benoit Chesneau
e05e40d19b feat(ctl): add message-based dirty worker management
Replace signal-based dirty add/remove with protocol messages:
- Add MSG_TYPE_MANAGE to dirty protocol for worker management
- Add MANAGE_OP_ADD and MANAGE_OP_REMOVE operation codes
- Add handle_manage_request() in DirtyArbiter
- Update handlers to send messages instead of SIGTTIN/SIGTTOU signals

New workers only load apps that haven't reached their worker limits.
When all apps are at their limits, returns reason in response.
Only increment num_workers when a worker is actually spawned.
2026-02-13 02:25:37 +01:00
Benoit Chesneau
7df260930c fix(ctl): use 'result' key from dirty protocol response
The dirty protocol response uses 'result' not 'data' for the payload.
2026-02-13 02:01:38 +01:00
Benoit Chesneau
be27ed4036 fix(ctl): properly encode/decode MSG_TYPE_STATUS for dirty worker query
- Add encode_status method to BinaryProtocol
- Use arbiter.dirty_arbiter.socket_path instead of env var when available
2026-02-13 01:56:39 +01:00
Benoit Chesneau
3e6d6b94c5 feat(ctl): query dirty arbiter for worker info in 'show all'
Add MSG_TYPE_STATUS to dirty protocol to allow querying the dirty
arbiter for its workers. The control socket now connects to the
dirty arbiter socket to retrieve worker information.
2026-02-13 01:52:43 +01:00
Benoit Chesneau
9f7000ff63 feat(ctl): add 'show all' command for process overview
Displays complete hierarchy: arbiter PID, web workers with their
PIDs/status, dirty arbiter PID, and dirty workers with their apps.
2026-02-13 01:47:45 +01:00
Benoit Chesneau
3963e850ea fix(dirty): reduce log verbosity for worker spawn skip
Change 'No apps need more workers' from warning to debug level
to avoid log spam when all apps have sufficient workers.
2026-02-13 01:44:35 +01:00
Benoit Chesneau
a57507c4e5 feat(ctl): add gunicornc control interface
Add a control socket server and CLI client for runtime management
of Gunicorn instances, similar to birdc for BIRD routing daemon.

Features:
- Control socket server running in arbiter process (asyncio/threaded)
- gunicornc CLI with interactive and single-command modes
- JSON protocol with length-prefixed framing
- Commands: show workers/stats/config/listeners/dirty, worker add/remove/kill,
  dirty add/remove, reload, reopen, shutdown
- Stats tracking (uptime, workers spawned/killed, reloads)
- Configurable socket path and permissions

New config options:
- control_socket: Unix socket path (default: gunicorn.ctl)
- control_socket_mode: Socket permissions (default: 0o600)
- --no-control-socket: Disable control socket
2026-02-13 01:38:17 +01:00
Benoit Chesneau
3cba17b84a
Merge pull request #3504 from benoitc/feature/dirty-ttin-ttou
feat(dirty): add TTIN/TTOU signal support for dynamic worker scaling
2026-02-13 00:17:31 +01:00
Benoit Chesneau
0077b05916 docs: add TTIN/TTOU to changelog 2026-02-12 23:59:20 +01:00
Benoit Chesneau
2639215aa3 feat(dirty): add TTIN/TTOU signal support for dynamic worker scaling
Add support for SIGTTIN and SIGTTOU signals to the dirty arbiter,
allowing dynamic scaling of dirty workers at runtime without restarting
gunicorn.

Changes:
- Add TTIN/TTOU to DirtyArbiter.SIGNALS
- Add num_workers instance variable for dynamic count
- Add _get_minimum_workers() to enforce app worker constraints
- Add signal handlers for TTIN (increase) and TTOU (decrease)
- Update manage_workers() to use dynamic count
- Add documentation for dynamic scaling
- Add unit tests for signal handling
- Add Docker integration tests

The minimum worker constraint ensures TTOU cannot reduce workers below
what apps require (e.g., if an app has workers=3, minimum is 3).

Closes #3489
2026-02-12 23:52:12 +01:00
Benoit Chesneau
ac00c862d7 docs: mark ASGI as stable, Dirty Arbiters as beta
- Remove beta warning from ASGI worker documentation
- Add beta warning to Dirty Arbiters documentation
- Update README feature list accordingly
- Update changelog to reflect stability changes
2026-02-12 22:09:14 +01:00
Benoit Chesneau
7f6cf908e5 release: 25.1.0
New Features:
- Dirty Stash: global shared state between workers (#3503)
- Dirty Binary Protocol: TLV encoding for efficient IPC (#3500)

Documentation:
- Fix Markdown formatting in /configure
2026-02-12 22:06:44 +01:00
Benoit Chesneau
709a6ad159
feat(dirty): add stash - global shared state between workers (#3503)
* feat(dirty): add stash - global shared state between workers

Add a simple key-value store (stash) that allows dirty workers to share
state through the arbiter. Tables are stored directly in arbiter memory
for fast access and simplicity.

Features:
- Auto-create tables on first access
- Dict-like interface via stash.table()
- Pattern matching for keys (glob patterns)
- Module-level API: stash.put(), stash.get(), stash.delete(), etc.

Usage:
    from gunicorn.dirty import stash

    stash.put("sessions", "user:1", {"name": "Alice"})
    user = stash.get("sessions", "user:1")

    # Or dict-like
    sessions = stash.table("sessions")
    sessions["user:1"] = {"name": "Alice"}

New files:
- gunicorn/dirty/stash.py - Client API and StashTable class
- Protocol additions for MSG_TYPE_STASH and STASH_OP_* codes

Note: Tables are ephemeral - lost if arbiter restarts.

* test(dirty): add tests for stash protocol and encoding

Test coverage for:
- Stash message creation and encoding
- Protocol constants (MSG_TYPE_STASH, STASH_OP_*)
- Error classes (StashError, StashTableNotFoundError, StashKeyNotFoundError)
- StashTable dict-like interface
- Edge cases: unicode, complex values, special patterns

* example(dirty): add stash usage example and integration tests

- Add SessionApp to dirty_app.py demonstrating stash usage
- Add /session/* endpoints to wsgi_app.py
- Add test_stash_integration.py with Docker tests
- Update docker-compose.yml with stash-test service
- Fix: Set GUNICORN_DIRTY_SOCKET in dirty arbiter for worker access

* docs(dirty): add stash documentation
2026-02-12 21:45:49 +01:00
Benoit Chesneau
236c9371d0
Merge pull request #3502 from Juneezee/docs/issues-3498
docs: fix Markdown formatting in /configure
2026-02-12 17:52:07 +01:00
Benoit Chesneau
4b90e4ba16
Merge pull request #3500 from benoitc/feature/binary-dirty-protocol
feat(dirty): implement binary protocol for dirty worker IPC
2026-02-12 17:26:10 +01:00
Eng Zer Jun
1cd4db8ddf
docs: fix Markdown formatting in /configure
Fixes https://github.com/benoitc/gunicorn/issues/3498.

Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2026-02-12 21:12:24 +08:00
Benoit Chesneau
415aa77343 fix: revert embedding_service port to 8000 for CI 2026-02-11 23:54:36 +01:00
Benoit Chesneau
f4e219716f fix(dirty): disable pylint too-many-return-statements in TLV 2026-02-11 23:48:07 +01:00
Benoit Chesneau
6d691b30e1 chore: use different ports in example docker-compose files
Avoid port conflicts when running multiple examples:
- dirty_example: 8001
- embedding_service: 8002
- celery_alternative: 8003
2026-02-11 23:40:10 +01:00
Benoit Chesneau
68ce658f5d fix(dirty): convert dict int keys to strings in TLV encoder
JSON serializes all dict keys as strings, so for compatibility the TLV
encoder should do the same. This fixes an error when tasks return dicts
with integer keys (e.g., aggregation results grouped by numeric ID).
2026-02-11 23:39:53 +01:00
Benoit Chesneau
c0cc8c0de0 test(dirty): add Docker setup for dirty example integration tests
- Add Dockerfile and docker-compose.yml for running examples in containers
- Add test_integration.py for HTTP-level integration testing
- Update test_worker_integration.py to use MockWriter for handle_request
- Use integer request IDs for binary protocol compatibility
- Add GUNICORN_BIND env var support in gunicorn_conf.py for Docker
2026-02-11 23:30:48 +01:00
Benoit Chesneau
00da70292f docs(dirty): update examples and docs for binary protocol
- Update test_protocol.py example to use binary protocol
- Add test_binary_data_handling example showing raw bytes transfer
- Update dirty.md to document binary TLV protocol format
- Replace JSON references with binary protocol
- Add Binary Protocol section with header and TLV encoding details
2026-02-11 23:15:14 +01:00
Benoit Chesneau
477b7479cc feat(dirty): update client for binary protocol
Update client and streaming tests to work with the binary protocol:
- Update MockStreamWriter/MockStreamReader to use BinaryProtocol
- Replace string request IDs with integers
- Update test assertions to decode binary protocol messages
- Use HEADER_SIZE and decode_header/decode_message instead of old API
2026-02-11 23:12:44 +01:00
Benoit Chesneau
98b1b649c2 feat(dirty): update arbiter for binary protocol
Update arbiter tests to work with the binary protocol:
- Update MockStreamWriter to decode binary messages
- Import binary protocol constants from module level
2026-02-11 23:03:40 +01:00
Benoit Chesneau
6d2139bb6c feat(dirty): update worker for binary protocol
Update worker tests to work with the binary protocol:
- Use integer request IDs instead of strings
- Update MockStreamWriter to decode binary messages
- Import binary protocol constants from module level
2026-02-11 23:01:21 +01:00
Benoit Chesneau
1665857c0e feat(dirty): implement binary protocol
Replace JSON-based protocol with binary format using 16-byte header:
- Magic bytes (GD), version, message type, payload length, request ID
- TLV-encoded payloads for efficient binary data transfer
- No base64 encoding needed for binary data
- Backwards compatible API (DirtyProtocol alias, dict-based interface)

Header format inspired by OpenBSD msgctl/msgsnd.
2026-02-11 22:58:43 +01:00
Benoit Chesneau
0e0dc669c8 feat(dirty): add TLV binary encoder/decoder
Implement TLV (Type-Length-Value) serialization layer for the binary
dirty worker protocol. This enables efficient binary data transfer
without base64 encoding overhead.

Supported types:
- None, bool, int64, float64
- bytes (raw binary, no encoding needed)
- string (UTF-8)
- list, dict (nested structures)

Inspired by OpenBSD msgctl/msgsnd message format.
2026-02-11 22:55:03 +01:00
Eng Zer Jun
c0959609ef
Use markdown-grid-tables for multiline table
pycodestyle enforces 120-character line lengtn limit.

Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2026-02-12 00:25:29 +08:00
Eng Zer Jun
cc974da87e
Update config.py
Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2026-02-11 05:40:34 +08:00
Eng Zer Jun
fb698ef0f7
docs: update forwarded_allow_ips to Markdown
Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2026-02-10 12:32:23 +08:00