Gunicorn: Does 1 web worker with Gunicorn always mean that there is only 1 process running the app code?

Created on 31 Jul 2019  路  11Comments  路  Source: benoitc/gunicorn

This is probably a fairly basic question. I'm learning about the basics of web app development and in this regard about concurrency and parallelism.

If I start my Flask web app with Gunicorn set to 1 worker:

gunicorn -w 1 server:app

Will this always mean that there's only one process running my app? (So I won't have to worry about writing process-safe code, because it's never multiple processes running the app, but always only one.) Or does it depend on other factors?

(Also, I have read the docs and especially the part about Gunicorn's design http://docs.gunicorn.org/en/stable/design.html and I think, the additional master process doesn't count because it's not running the app code, right?)

Most helpful comment

Will this always mean that there's only one process running my app?

Yes. However, you will see two processes running. One is the Gunicorn master process, which controls the worker processes. With the number of workers set to 1 you'll have only one worker process.

Multiple workers means my app has to be designed for multiprocessing, right? Simple example, if a global variable is being changed to a timestamp of the most recent request, its value will be different depending on which worker handled the request.

Correct. Gunicorn workers share nothing. They are separate processes and Gunicorn does not use the Python multiprocessing model. There is no way to share state between Gunicorn workers except using an external storage or message system to communicate between them. For example, each worker will have a separate copy of any global variable and it will not be shared between workers.

Some of the worker types use threads or coroutines to provide concurrency within the same system process. The threaded worker uses multiple system threads within the same process. The gevent and eventlet workers use the gevent and eventlet libraries to provide concurrency with coroutines. The asyncio worker uses the built-in asyncio module from Python to provide concurrency with coroutines, as well. All of these worker types will allow multiple requests to be handled concurrently and any shared, global state within the worker process will be shared.

All 11 comments

The amount of workers should not effect how you write your code, if you _do_ consider that in your implementation - I'd maybe re-evaluate your design.

To answer your question: there is one sync worker running your code, so yes one _worker_ process. There is also the arbiter (master) process, but it sounds like you don't want to count that.

Thanks for your reply :)

The amount of workers should not effect how you write your code, if you do consider that in your implementation - I'd maybe re-evaluate your design.

Why is that? With regard to scalability, I guess? To find more information about this, is there maybe term for the underlying principle I can search for?

I'd maybe ask the question back, why _would_ you consider them in your
design? Maybe your deployment architecture considers it, but your actual
Python app has no reason to, and should not.

There's no underlying term, it may make sense to design around the worker
types since sync workers can get blocked with CPU heavy tasks. So in that
case you'd want to limit how long a worker can stay running.

@cbrintnall Hey, thanks :)

I'd maybe ask the question back, why _would_ you consider them in your design

Multiple workers means my app has to be designed for multiprocessing, right? Simple example, if a global variable is being changed to a timestamp of the most recent request, its value will be different depending on which worker handled the request. So I can't use global variables to store that information. In addition to my pure Python web app script, I'll need to use some kind of server to store data, like Redis etc. This increases the complexity of the app for such a simple task. Maybe I'm missing something fundamental?

Will this always mean that there's only one process running my app?

Yes. However, you will see two processes running. One is the Gunicorn master process, which controls the worker processes. With the number of workers set to 1 you'll have only one worker process.

Multiple workers means my app has to be designed for multiprocessing, right? Simple example, if a global variable is being changed to a timestamp of the most recent request, its value will be different depending on which worker handled the request.

Correct. Gunicorn workers share nothing. They are separate processes and Gunicorn does not use the Python multiprocessing model. There is no way to share state between Gunicorn workers except using an external storage or message system to communicate between them. For example, each worker will have a separate copy of any global variable and it will not be shared between workers.

Some of the worker types use threads or coroutines to provide concurrency within the same system process. The threaded worker uses multiple system threads within the same process. The gevent and eventlet workers use the gevent and eventlet libraries to provide concurrency with coroutines. The asyncio worker uses the built-in asyncio module from Python to provide concurrency with coroutines, as well. All of these worker types will allow multiple requests to be handled concurrently and any shared, global state within the worker process will be shared.

@tilgovi Thanks for the reply :)

There is no way to share state between Gunicorn workers except using an external storage or message system to communicate between them.

I read through the Gunicorn docs and unfortunately there was nothing (like a "hint" or "warning") about shared state or concurrency in there. Should there be? I assume this is basic web dev knowledge and I just should have known this, right? It totally makes sense, but I only noticed this once my code didn't work because I'm not used to writing code designed for multiprocessing.

@finefoot I would gladly accept a pull request to clarify anything like that! It's always hard to strike a balance between explaining everything and assuming too much in documentation

Maybe somewhere on this page? http://docs.gunicorn.org/en/latest/design.html

The source is in docs/source/design.rst.

It's always hard to strike a balance between explaining everything and assuming too much in documentation

Absolutely, I agree. Didn't mean to make any accusations. :) Like I said, I'm still really new to web dev so unfortunately, I often just have to assume that I'm overlooking some basic stuff.

Maybe somewhere on this page? http://docs.gunicorn.org/en/latest/design.html

Yes, I think one can stumble upon this issue regarding concurrency/parallelism in two ways:

  1. Using asynchronous worker(s) (For example, as far as I understand, currently, multiprocessing is not compatible with the gevent worker class. https://github.com/gevent/gevent/issues/1443)

    Section "Choosing a Worker Type", after the enumeration "Some examples of behavior requiring asynchronous workers", how about inserting "Note: You will have to make sure that your code is compatible and safe to run with asynchronous workers."

  2. Using multiple workers

    Section "How Many Workers", how about inserting "Note: There is no way to share state between Gunicorn workers except using an external storage or message system to communicate between them." (Just quoted you from your comment above.)

Is this what you had in mind? I can submit a pull request, if you like.

It's always hard to strike a balance between explaining everything and assuming too much in documentation

Didn't mean to make any accusations.

Not at all. I said that to intentionally making it blameless. The documentation isn't wrong, nor are you overlooking basic stuff. Nothing about any of this is basic.

I think I'll reword my own quote and try to make a PR. I'd love to tag you to review it, if that's alright.

I think I'll reword my own quote and try to make a PR. I'd love to tag you to review it, if that's alright.

Sure, sounds great. :) Sorry for taking so long to respond. I couldn't find any recent and related pull request, though. I hope you didn't wait for my response?

Was this page helpful?
0 / 5 - 0 ratings