Skip to content

Guidance to avoid duplicate metrics in multiprocess #651

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 14, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ implement a proper `describe`, or if that's not practical have `describe`
return an empty list.


## Multiprocess Mode (Gunicorn)
## Multiprocess Mode (E.g. Gunicorn)

Prometheus client libraries presume a threaded model, where metrics are shared
across workers. This doesn't work so well for languages such as Python where
Expand All @@ -504,30 +504,38 @@ To handle this the client library can be put in multiprocess mode.
This comes with a number of limitations:

- Registries can not be used as normal, all instantiated metrics are exported
- Registering metrics to a registry later used by a `MultiProcessCollector`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is under a section called (Gunicorn) however this seems more generic and not tied to Gunicorn, right? I hit this issue with UWSGI.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this also begs the question of, what is the case someone does want to register a metric to a registry?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Gunicorn specification is a good point, perhaps (E.g. Gunicorn) instead to make it clear that is an example?

There are lots of valid use cases to register a metric when in non-multiprocess mode, it just doesn't work for multi-process right now. For example, when building a custom exporter you might have one registry for internal metrics, and a second registry for the metrics coming from something being probed.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

roger that

may cause duplicate metrics to be exported
- Custom collectors do not work (e.g. cpu and memory metrics)
- Info and Enum metrics do not work
- The pushgateway cannot be used
- Gauges cannot use the `pid` label

There's several steps to getting this working:

**1. Gunicorn deployment**:
**1. Deployment**:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


The `prometheus_multiproc_dir` environment variable must be set to a directory
that the client library can use for metrics. This directory must be wiped
between Gunicorn runs (before startup is recommended).
between process/Gunicorn runs (before startup is recommended).

This environment variable should be set from a start-up shell script,
and not directly from Python (otherwise it may not propagate to child processes).

**2. Metrics collector**:

The application must initialize a new `CollectorRegistry`,
and store the multi-process collector inside.
The application must initialize a new `CollectorRegistry`, and store the
multi-process collector inside. It is a best practice to create this registry
inside the context of a request to avoid metrics registering themselves to a
collector used by a `MultiProcessCollector`. If a registry with metrics
registered is used by a `MultiProcessCollector` duplicate metrics may be
exported, one for multiprocess, and one for the process serving the request.

```python
from prometheus_client import multiprocess
from prometheus_client import generate_latest, CollectorRegistry, CONTENT_TYPE_LATEST
from prometheus_client import generate_latest, CollectorRegistry, CONTENT_TYPE_LATEST, Counter

MY_COUNTER = Counter('my_counter', 'Description of my counter')

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


# Expose metrics.
def app(environ, start_response):
Expand Down