Skip to content

Commit d298fa0

Browse files
Fail closed on undeclared metric dimensions
Fail closed on undeclared metric dimensions
1 parent 205a2ee commit d298fa0

3 files changed

Lines changed: 79 additions & 2 deletions

File tree

app/Support/BoundedMetricPolicy.php

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace App\Support;
44

5+
use InvalidArgumentException;
6+
57
final class BoundedMetricPolicy
68
{
79
/**
@@ -10,10 +12,26 @@ final class BoundedMetricPolicy
1012
*/
1113
public static function labelSet(string $metric, array $runtimeDimensions = []): array
1214
{
13-
$declared = config("dw-bounded-growth.metrics.{$metric}.dimensions", []);
15+
$metrics = config('dw-bounded-growth.metrics', []);
16+
17+
if (! is_array($metrics) || ! array_key_exists($metric, $metrics)) {
18+
throw new InvalidArgumentException("Metric {$metric} is missing from config/dw-bounded-growth.php.");
19+
}
20+
21+
$declared = $metrics[$metric]['dimensions'] ?? null;
1422

1523
if (! is_array($declared)) {
16-
return $runtimeDimensions;
24+
throw new InvalidArgumentException("Metric {$metric} must declare bounded-growth dimensions.");
25+
}
26+
27+
$unknownDimensions = array_diff(array_keys($runtimeDimensions), array_keys($declared));
28+
29+
if ($unknownDimensions !== []) {
30+
throw new InvalidArgumentException(sprintf(
31+
'Metric %s disclosed undeclared runtime dimension(s): %s.',
32+
$metric,
33+
implode(', ', $unknownDimensions),
34+
));
1735
}
1836

1937
$labelSet = [];

docs/bounded-growth.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ added without a TTL, admission, or cardinality contract.
2424
- Prometheus label names emitted by app code or the perf harness must exactly
2525
match the declared metric dimensions, so adding a label requires a reviewed
2626
cardinality policy in the same change.
27+
- Runtime metric cardinality disclosures must fail closed: a metric cannot
28+
expose a label-set policy unless the metric and every disclosed dimension are
29+
declared in `config/dw-bounded-growth.php`.
2730
- Remote-write scrape labels must stay deployment-scoped. Per-run values such
2831
as `GITHUB_RUN_ID` and `RUNNER_NAME` belong in `summary.json` provenance, not
2932
in Prometheus labels that create new series for every soak.
@@ -68,6 +71,8 @@ added without a TTL, admission, or cardinality contract.
6871
covered by a `metrics` entry;
6972
- Prometheus labels emitted by app or perf-harness source must exactly match
7073
the corresponding metric dimensions declared in the policy;
74+
- runtime metric disclosures reject unknown metrics or undeclared dimensions
75+
before they can appear in `/api/system/metrics`;
7176
- perf-harness remote-write target labels must not include per-run or
7277
per-runner dimensions;
7378
- each policy entry must include the required review fields;
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
namespace Tests\Unit;
4+
5+
use App\Support\BoundedMetricPolicy;
6+
use InvalidArgumentException;
7+
use Tests\TestCase;
8+
9+
class BoundedMetricPolicyTest extends TestCase
10+
{
11+
public function test_label_set_rejects_metrics_without_bounded_growth_policy(): void
12+
{
13+
$this->expectException(InvalidArgumentException::class);
14+
$this->expectExceptionMessage('Metric dw_unreviewed_metric is missing from config/dw-bounded-growth.php.');
15+
16+
BoundedMetricPolicy::labelSet('dw_unreviewed_metric');
17+
}
18+
19+
public function test_label_set_rejects_runtime_dimensions_missing_from_policy(): void
20+
{
21+
$this->expectException(InvalidArgumentException::class);
22+
$this->expectExceptionMessage(
23+
'Metric dw_projection_drift_total disclosed undeclared runtime dimension(s): workflow_type.',
24+
);
25+
26+
BoundedMetricPolicy::labelSet('dw_projection_drift_total', [
27+
'table' => [
28+
'values' => ['run_summaries'],
29+
],
30+
'workflow_type' => [
31+
'selection' => 'all',
32+
],
33+
]);
34+
}
35+
36+
public function test_label_set_merges_declared_cardinality_policy_with_runtime_metadata(): void
37+
{
38+
$labelSet = BoundedMetricPolicy::labelSet('dw_projection_drift_total', [
39+
'table' => [
40+
'values' => ['run_summaries', 'run_waits'],
41+
'selection' => 'fixed_projection_table_inventory',
42+
],
43+
]);
44+
45+
$this->assertSame([
46+
'namespace' => 'server_scope_no_label',
47+
'table' => [
48+
'cardinality_class' => 'finite_projection_table_inventory',
49+
'values' => ['run_summaries', 'run_waits'],
50+
'selection' => 'fixed_projection_table_inventory',
51+
],
52+
], $labelSet);
53+
}
54+
}

0 commit comments

Comments
 (0)