Unit 02.01: Running it, and what the reload flag costs
How you run it differs between development and production, and one of the differences has a design consequence.
Reload, workers, and shared state
Three commands with what each is for.
The code lists them.
COMMANDS = [
("uvicorn app.main:app --reload",
"development", "restarts on file change; do not use in production"),
("uvicorn app.main:app --host 0.0.0.0 --port 8000",
"production-ish", "one worker, no reload"),
("uvicorn app.main:app --workers 4",
"production", "four processes; no shared in-memory state"),
]
print(f"{'command':52} {'for':16} note")
for command, use, note in COMMANDS:
print(f"{command:52} {use:16} {note}")
print("""
`--reload` watches the filesystem and restarts the process. That costs
noticeable CPU and, more importantly, it restarts on any change -- including
a half-saved file, which produces confusing errors.
`--workers 4` is the one with a design consequence: four processes means any
in-memory cache, counter or rate limiter exists four times.
""")
--reload watches the filesystem and restarts on any change - including a half-saved file, which produces errors that look like bugs and are not. It costs noticeable CPU and has no place in production.
--workers 4 is the one that changes your design. Four processes means any in-memory cache, counter or rate limiter exists four times, and a restart forgets all four.
The mistake this prevents
The mistake is testing with one worker and deploying with several. Every in-process state bug is invisible until then, and the symptom - a rate limit that allows four times what it should - looks like a logic error rather than a deployment one.
Takeaway
Never use --reload in production, and remember that multiple workers mean multiple copies of anything held in memory.
