From 8b916b65f94c942c77af30faeab2850f21f83aef Mon Sep 17 00:00:00 2001 From: Simon Davy Date: Wed, 17 Oct 2018 14:56:32 +0100 Subject: [PATCH] In multiprocess mode, ensure that metrics initialise to the correct file. Without this, when forking, children can initialise metrics to the cached master pid file. This is racy, and can corrupt the master's mmaped file, breaking all subsequent collections. Signed-off-by: Simon Davy --- prometheus_client/core.py | 1 + tests/test_multiprocess.py | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/prometheus_client/core.py b/prometheus_client/core.py index e96a1095..f730e095 100644 --- a/prometheus_client/core.py +++ b/prometheus_client/core.py @@ -666,6 +666,7 @@ class _MmapedValue(object): def __init__(self, typ, metric_name, name, labelnames, labelvalues, multiprocess_mode='', **kwargs): self._params = typ, metric_name, name, labelnames, labelvalues, multiprocess_mode with lock: + self.__check_for_pid_change() self.__reset() values.append(self) diff --git a/tests/test_multiprocess.py b/tests/test_multiprocess.py index c7b412b1..e2745b0b 100644 --- a/tests/test_multiprocess.py +++ b/tests/test_multiprocess.py @@ -141,6 +141,26 @@ def test_counter_across_forks(self): self.assertEqual(3, self.registry.get_sample_value('c_total')) self.assertEqual(1, c1._value.get()) + def test_initialization_detects_pid_change(self): + pid = 0 + core._ValueClass = core._MultiProcessValue(lambda: pid) + + # can not inspect the files cache directly, as it's a closure, so we + # check for the actual files themselves + def files(): + fs = os.listdir(os.environ['prometheus_multiproc_dir']) + fs.sort() + return fs + + c1 = Counter('c1', 'c1', registry=None) + self.assertEqual(files(), ['counter_0.db']) + c2 = Counter('c2', 'c2', registry=None) + self.assertEqual(files(), ['counter_0.db']) + pid = 1 + c3 = Counter('c3', 'c3', registry=None) + self.assertEqual(files(), ['counter_0.db', 'counter_1.db']) + + @unittest.skipIf(sys.version_info < (2, 7), "Test requires Python 2.7+.") def test_collect(self): pid = 0