Skip to content

Commit 24fc182

Browse files
Render matching-role partition and backpressure metadata
Backfill the frozen matching-role partition and lease-based backpressure fields on Waterline's operator metrics surface when older workflow package versions omit them, so the dashboard can render the extra routing details without waiting for a workflow package bump.
1 parent a267878 commit 24fc182

5 files changed

Lines changed: 96 additions & 5 deletions

File tree

app/Repositories/Workflow/Infrastructure/V2WorkflowRepository.php

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ public function dashboardStats(): array
7373
$namespace = $this->namespace();
7474
$now = now();
7575
$summary = app(OperatorObservabilityRepository::class)->dashboardSummary($now, $namespace);
76-
$summary['operator_metrics']['workers'] = $this->scopedWorkerMetrics(
77-
$summary['operator_metrics']['workers'] ?? null,
76+
$summary['operator_metrics'] = $this->annotateOperatorMetrics(
77+
$summary['operator_metrics'] ?? null,
7878
$namespace,
7979
);
8080

@@ -152,7 +152,27 @@ public function operatorMetrics()
152152
{
153153
$namespace = $this->namespace();
154154
$metrics = app(OperatorObservabilityRepository::class)->metrics(null, $namespace);
155+
$metrics = $this->annotateOperatorMetrics($metrics, $namespace);
156+
157+
return $metrics;
158+
}
159+
160+
/**
161+
* Keep Waterline compatible with older workflow alphas that omit
162+
* namespace-scoped worker snapshots or the expanded matching-role
163+
* routing contract on the operator metrics surface.
164+
*
165+
* @param mixed $metrics
166+
* @return mixed
167+
*/
168+
private function annotateOperatorMetrics(mixed $metrics, ?string $namespace): mixed
169+
{
170+
if (! is_array($metrics)) {
171+
return $metrics;
172+
}
173+
155174
$metrics['workers'] = $this->scopedWorkerMetrics($metrics['workers'] ?? null, $namespace);
175+
$metrics['matching_role'] = $this->matchingRoleMetrics($metrics['matching_role'] ?? null);
156176

157177
return $metrics;
158178
}
@@ -209,6 +229,31 @@ private function scopedWorkerMetrics(mixed $workers, ?string $namespace): mixed
209229
return $workers;
210230
}
211231

232+
/**
233+
* The task-matching contract freezes the partition primitives and the
234+
* lease-based backpressure model even though older workflow alphas did
235+
* not yet expose them on OperatorMetrics::snapshot().
236+
*
237+
* @param mixed $matchingRole
238+
* @return mixed
239+
*/
240+
private function matchingRoleMetrics(mixed $matchingRole): mixed
241+
{
242+
if (! is_array($matchingRole)) {
243+
return $matchingRole;
244+
}
245+
246+
if (! array_key_exists('partition_primitives', $matchingRole)) {
247+
$matchingRole['partition_primitives'] = ['connection', 'queue', 'compatibility', 'namespace'];
248+
}
249+
250+
if (! array_key_exists('backpressure_model', $matchingRole)) {
251+
$matchingRole['backpressure_model'] = 'lease_ownership';
252+
}
253+
254+
return $matchingRole;
255+
}
256+
212257
/**
213258
* @param array<string, mixed> $snapshot
214259
* @return array<string, mixed>

public/app.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/mix-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"/app.js": "/app.js?id=af2b8443ef291871597df9e83087c328",
2+
"/app.js": "/app.js?id=ed9bd12b08ad1f7f9c27a64da04c4222",
33
"/app-dark.css": "/app-dark.css?id=8b1b08a71c8e9860d0a9030d902c30d0",
44
"/app.css": "/app.css?id=4d346e04fa466f5227cee3c313fbea25",
55
"/img/favicon.png": "/img/favicon.png?id=7c006241b093796d6abfa3049df93a59",

resources/js/screens/dashboard.vue

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,10 @@
524524
queue-wake {{ operatorMatchingRoleQueueWakeEnabled() ? 'enabled' : 'disabled' }},
525525
task dispatch <code>{{ operatorMatchingRoleTaskDispatchMode() }}</code>.
526526
</p>
527+
<p v-if="operatorMatchingRoleContractAvailable()">
528+
Partitions by <code>{{ operatorMatchingRolePartitionPrimitivesLabel() }}</code>,
529+
backpressure <code>{{ operatorMatchingRoleBackpressureModel() }}</code>.
530+
</p>
527531
<p class="text-muted">
528532
Single-process scope &mdash; read one snapshot per node to see the full deployment.
529533
</p>
@@ -1462,6 +1466,36 @@ export default {
14621466
: 'unknown';
14631467
},
14641468
1469+
operatorMatchingRoleContractAvailable() {
1470+
const matchingRole = (this.operatorMetrics && this.operatorMetrics.matching_role) || {};
1471+
1472+
return this.operatorMatchingRolePartitionPrimitives(matchingRole).length > 0
1473+
|| (typeof matchingRole.backpressure_model === 'string' && matchingRole.backpressure_model !== '');
1474+
},
1475+
1476+
operatorMatchingRolePartitionPrimitives(matchingRole = null) {
1477+
const snapshot = matchingRole || ((this.operatorMetrics && this.operatorMetrics.matching_role) || {});
1478+
const primitives = Array.isArray(snapshot.partition_primitives) ? snapshot.partition_primitives : [];
1479+
1480+
return primitives.filter((primitive) => typeof primitive === 'string' && primitive !== '');
1481+
},
1482+
1483+
operatorMatchingRolePartitionPrimitivesLabel() {
1484+
const primitives = this.operatorMatchingRolePartitionPrimitives();
1485+
1486+
return primitives.length > 0
1487+
? primitives.join(' / ')
1488+
: 'unknown';
1489+
},
1490+
1491+
operatorMatchingRoleBackpressureModel() {
1492+
const matchingRole = (this.operatorMetrics && this.operatorMetrics.matching_role) || {};
1493+
1494+
return typeof matchingRole.backpressure_model === 'string' && matchingRole.backpressure_model !== ''
1495+
? matchingRole.backpressure_model
1496+
: 'unknown';
1497+
},
1498+
14651499
operatorScheduleOldestOverdueAt() {
14661500
const schedules = (this.operatorMetrics && this.operatorMetrics.schedules) || {};
14671501
@@ -1836,4 +1870,4 @@ export default {
18361870
grid-template-columns: minmax(0, 1fr);
18371871
}
18381872
}
1839-
</style>
1873+
</style>

tests/Feature/V2DashboardStatsControllerTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,8 @@ public function testIndexExposesMatchingRole(): void
968968
$this->assertArrayHasKey('queue_wake_enabled', $matchingRole);
969969
$this->assertArrayHasKey('shape', $matchingRole);
970970
$this->assertArrayHasKey('task_dispatch_mode', $matchingRole);
971+
$this->assertArrayHasKey('partition_primitives', $matchingRole);
972+
$this->assertArrayHasKey('backpressure_model', $matchingRole);
971973

972974
$this->assertIsBool($matchingRole['queue_wake_enabled']);
973975
$this->assertContains(
@@ -980,6 +982,16 @@ public function testIndexExposesMatchingRole(): void
980982
['queue', 'poll'],
981983
'operator_metrics.matching_role.task_dispatch_mode must be queue or poll',
982984
);
985+
$this->assertSame(
986+
['connection', 'queue', 'compatibility', 'namespace'],
987+
$matchingRole['partition_primitives'],
988+
'operator_metrics.matching_role.partition_primitives must preserve the frozen queue routing order',
989+
);
990+
$this->assertSame(
991+
'lease_ownership',
992+
$matchingRole['backpressure_model'],
993+
'operator_metrics.matching_role.backpressure_model must preserve the frozen matching-role backpressure contract',
994+
);
983995
}
984996

985997
public function testIndexExposesUnhealthyAgeRollup(): void

0 commit comments

Comments
 (0)