From 317ee2cb287dfeb059a9b4c6f84f081d2f372ab2 Mon Sep 17 00:00:00 2001 From: Josh Hoffer Date: Wed, 21 Jun 2023 16:47:26 -0600 Subject: [PATCH] APCng storage - avoid worst-case memory usage in buildPermutationTree --- src/Prometheus/Storage/APCng.php | 38 ++++++++++++-------------------- 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/src/Prometheus/Storage/APCng.php b/src/Prometheus/Storage/APCng.php index 6628ef97..ad58da46 100644 --- a/src/Prometheus/Storage/APCng.php +++ b/src/Prometheus/Storage/APCng.php @@ -414,31 +414,22 @@ private function metaData(array $data): array * [15] => ['/metrics', 'get', 'fail'], [16] => ['/metrics', 'post', 'success'], [17] => ['/metrics', 'post', 'fail'] * @param array $labelNames * @param array $labelValues - * @return array + * @return Generator */ - private function buildPermutationTree(array $labelNames, array $labelValues): array /** @phpstan-ignore-line */ - { - $treeRowCount = count(array_keys($labelNames)); - $numElements = 1; - $treeInfo = []; - for ($i = $treeRowCount - 1; $i >= 0; $i--) { - $treeInfo[$i]['numInRow'] = count($labelValues[$i]); - $numElements *= $treeInfo[$i]['numInRow']; - $treeInfo[$i]['numInTree'] = $numElements; - } - - $map = array_fill(0, $numElements, []); - for ($row = 0; $row < $treeRowCount; $row++) { - $col = $i = 0; - while ($i < $numElements) { - $val = $labelValues[$row][$col]; - $map[$i] = array_merge($map[$i], array($val)); - if (++$i % ($treeInfo[$row]['numInTree'] / $treeInfo[$row]['numInRow']) == 0) { - $col = ++$col % $treeInfo[$row]['numInRow']; - } + private function buildPermutationTree(array $labelValues): \Generator /** @phpstan-ignore-line */ + { + if($labelValues){ + $lastIndex = array_key_last($labelValues); + if($currentValue = array_pop($labelValues)){ + foreach($this->buildPermutationTree($labelValues) as $prefix){ + foreach($currentValue as $value){ + yield $prefix + [$lastIndex => $value]; + } } + } + } else { + yield []; } - return $map; } /** @@ -539,10 +530,9 @@ private function getValues(string $type, array $metaData): array /** @phpstan-ig if (isset($metaData['buckets'])) { $metaData['buckets'][] = 'sum'; $labels[] = $metaData['buckets']; - $metaData['labelNames'][] = '__histogram_buckets'; } - $labelValuesList = $this->buildPermutationTree($metaData['labelNames'], $labels); + $labelValuesList = $this->buildPermutationTree($labels); unset($labels); $histogramBucket = ''; foreach ($labelValuesList as $labelValues) {