|
1 | 1 | from concurrent.futures import ThreadPoolExecutor |
2 | 2 | import os |
| 3 | +from threading import Barrier, Event |
3 | 4 | import time |
4 | 5 | import unittest |
5 | 6 |
|
@@ -621,6 +622,58 @@ def test_child(self): |
621 | 622 | self.two_labels.labels('x', 'y').inc(2) |
622 | 623 | self.assertEqual(2, self.registry.get_sample_value('two_total', {'a': 'x', 'b': 'y'})) |
623 | 624 |
|
| 625 | + def test_existing_child_during_other_child_creation(self): |
| 626 | + started = Event() |
| 627 | + release = Event() |
| 628 | + |
| 629 | + class SlowCounter(Counter): |
| 630 | + def _metric_init(self): |
| 631 | + if self._labelvalues == ('slow',): |
| 632 | + started.set() |
| 633 | + if not release.wait(timeout=10): |
| 634 | + raise RuntimeError('Child creation was not released') |
| 635 | + super()._metric_init() |
| 636 | + |
| 637 | + counter = SlowCounter('slow', 'help', ['l'], registry=self.registry) |
| 638 | + existing = counter.labels('existing') |
| 639 | + with ThreadPoolExecutor(max_workers=2) as pool: |
| 640 | + creating = pool.submit(counter.labels, 'slow') |
| 641 | + try: |
| 642 | + self.assertTrue(started.wait(timeout=5)) |
| 643 | + lookup = pool.submit(counter.labels, 'existing') |
| 644 | + self.assertIs(existing, lookup.result(timeout=5)) |
| 645 | + finally: |
| 646 | + release.set() |
| 647 | + self.assertIs(creating.result(timeout=5), counter.labels('slow')) |
| 648 | + |
| 649 | + def test_concurrent_child_creation_and_updates(self): |
| 650 | + barrier = Barrier(8) |
| 651 | + |
| 652 | + def increment(): |
| 653 | + barrier.wait(timeout=5) |
| 654 | + child = self.counter.labels('shared') |
| 655 | + for _ in range(100): |
| 656 | + self.counter.labels(l='shared').inc() |
| 657 | + return child |
| 658 | + |
| 659 | + with ThreadPoolExecutor(max_workers=8) as pool: |
| 660 | + futures = [pool.submit(increment) for _ in range(8)] |
| 661 | + children = [future.result(timeout=5) for future in futures] |
| 662 | + |
| 663 | + self.assertTrue(all(child is children[0] for child in children)) |
| 664 | + self.assertEqual(800, self.registry.get_sample_value('c_total', {'l': 'shared'})) |
| 665 | + |
| 666 | + def test_recreate_child_after_removal(self): |
| 667 | + for remove in (lambda: self.counter.remove('x'), self.counter.clear, |
| 668 | + lambda: self.counter.remove_by_labels({'l': 'x'})): |
| 669 | + original = self.counter.labels('x') |
| 670 | + original.inc() |
| 671 | + remove() |
| 672 | + replacement = self.counter.labels('x') |
| 673 | + self.assertIsNot(original, replacement) |
| 674 | + self.assertIs(replacement, self.counter.labels(l='x')) |
| 675 | + self.assertEqual(0, self.registry.get_sample_value('c_total', {'l': 'x'})) |
| 676 | + |
624 | 677 | def test_remove(self): |
625 | 678 | self.counter.labels('x').inc() |
626 | 679 | self.counter.labels('y').inc(2) |
|
0 commit comments