mirror of
https://github.com/frappe/gunicorn.git
synced 2026-07-01 18:21:30 +08:00
Add comprehensive Docker integration tests verifying dirty arbiter lifecycle under realistic conditions: - Parent death detection via ppid monitoring - Orphan cleanup on restart - Dirty arbiter respawning after crash - Graceful shutdown with SIGTERM Also fix race condition in manage_workers() by checking self.alive before spawning new workers during shutdown.
3.7 KiB
3.7 KiB
Docker-Based Dirty Arbiter Integration Tests
This directory contains Docker-based integration tests that verify the dirty arbiter process lifecycle under realistic conditions.
Overview
These tests verify:
- Parent Death Detection: Dirty arbiter self-terminates when main arbiter dies unexpectedly (SIGKILL)
- Orphan Cleanup: Old dirty arbiter processes are cleaned up on restart
- Respawning: Main arbiter respawns dirty arbiter when it crashes
- Graceful Shutdown: Both arbiters exit cleanly on SIGTERM
Prerequisites
- Docker
- Python 3.10+
- pytest
Quick Start
# Build the Docker image
docker compose build
# Run all tests
pytest test_parent_death.py -v
# Run specific test
pytest test_parent_death.py::TestParentDeath::test_dirty_arbiter_exits_on_parent_sigkill -v
Manual Verification
You can manually verify the behavior:
# Start the container
docker compose up -d
# Check running processes
docker exec dirty_arbiter-gunicorn-1 ps aux | grep gunicorn
# SIGKILL the master and watch dirty arbiter exit
MASTER_PID=$(docker exec dirty_arbiter-gunicorn-1 pgrep -f "gunicorn: master")
docker exec dirty_arbiter-gunicorn-1 kill -9 $MASTER_PID
# After ~2 seconds, check that all gunicorn processes exited
docker exec dirty_arbiter-gunicorn-1 ps aux | grep gunicorn
# View logs
docker logs dirty_arbiter-gunicorn-1
# Cleanup
docker compose down
Test Scenarios
Scenario 1: Parent SIGKILL
Tests that the dirty arbiter detects parent death via ppid check:
- Start gunicorn with dirty workers
- SIGKILL the main arbiter (bypasses graceful shutdown)
- Verify dirty arbiter detects ppid change within ~2 seconds
- Verify no orphan processes remain
Scenario 2: Orphan Cleanup
Tests the _cleanup_orphaned_dirty_arbiter() mechanism:
- Start gunicorn, note dirty arbiter PID
- SIGKILL main arbiter (dirty arbiter becomes orphan)
- Restart gunicorn
- Verify old dirty arbiter was cleaned up
- Verify new dirty arbiter spawned
Scenario 3: Dirty Arbiter Respawn
Tests that main arbiter respawns a dead dirty arbiter:
- Start gunicorn
- SIGKILL the dirty arbiter
- Wait for respawn (~1-2 seconds)
- Verify new dirty arbiter is running
Scenario 4: Graceful Shutdown
Tests clean shutdown via SIGTERM:
- Start gunicorn with dirty workers
- SIGTERM the main arbiter
- Verify both arbiters exit cleanly within graceful_timeout
- Verify clean exit logs
Files
| File | Description |
|---|---|
Dockerfile |
Container build configuration |
docker-compose.yml |
Container orchestration |
app.py |
Simple WSGI app with TestDirtyApp |
gunicorn_conf.py |
Gunicorn configuration |
test_parent_death.py |
pytest integration tests |
README.md |
This file |
Configuration
The gunicorn_conf.py uses:
- 1 sync worker
- 1 dirty worker
- 5 second graceful timeout (for faster tests)
- Debug logging
Expected Log Messages
When verifying behavior, look for these log messages:
| Message | Meaning |
|---|---|
Parent changed, shutting down dirty arbiter |
ppid detection triggered |
Killing orphaned dirty arbiter |
Orphan cleanup activated |
Spawning dirty arbiter |
New dirty arbiter being created |
Dirty arbiter exiting |
Clean shutdown |
Troubleshooting
Tests time out waiting for container:
- Check Docker is running
- Check no port conflicts on 8000
- Try
docker compose downand rebuild
Dirty arbiter doesn't exit after parent death:
- Check ppid detection is working (logs should show check)
- The check runs every 1 second, so allow 2-3 seconds
Container logs not showing expected messages:
- Verify loglevel is set to "debug" in gunicorn_conf.py
- Check
docker logs <container>for full output