Skip to content

Commit 482bb17

Browse files
Render retrying-activity and claim-failed ages on the Waterline dashboard
Surface activities.max_retrying_age_ms / oldest_retrying_started_at on the Activity attempts panel and tasks.max_claim_failed_age_ms / oldest_claim_failed_at on the Claim failed runs card so operators can read transport-failure age and retrying-activity age directly from the dashboard alongside the existing lease-expired, ready-due, dispatch- overdue, and compatibility-blocked age indicators.
1 parent dfa1fed commit 482bb17

2 files changed

Lines changed: 106 additions & 0 deletions

File tree

resources/js/screens/dashboard.vue

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,12 @@
331331
<div class="wl-operator-metric">
332332
<div class="wl-operator-metric__label">Claim failed runs</div>
333333
<div class="wl-operator-metric__value">{{ operatorMetricLabel('backlog', 'claim_failed_runs') }}</div>
334+
<div v-if="operatorClaimFailedAgeAvailable()" class="wl-operator-metric__meta">
335+
oldest claim {{ operatorDurationMetricLabel('tasks', 'max_claim_failed_age_ms') }} failed
336+
<template v-if="operatorClaimFailedOldestAt()">
337+
(since {{ operatorClaimFailedOldestAt() }})
338+
</template>
339+
</div>
334340
</div>
335341
<div class="wl-operator-metric">
336342
<div class="wl-operator-metric__label">Compatibility blocked</div>
@@ -383,6 +389,12 @@
383389
{{ operatorMetricLabel('activities', 'failed_attempts') }} failed attempts,
384390
max {{ operatorMetricLabel('activities', 'max_attempt_count') }} attempts.
385391
</p>
392+
<p v-if="operatorRetryingActivityAgeAvailable()">
393+
Oldest retrying activity {{ operatorDurationMetricLabel('activities', 'max_retrying_age_ms') }} behind
394+
<template v-if="operatorRetryingActivityOldestStartedAt()">
395+
(since {{ operatorRetryingActivityOldestStartedAt() }})
396+
</template>.
397+
</p>
386398
</section>
387399

388400
<section class="wl-operator-section">
@@ -1238,6 +1250,42 @@ export default {
12381250
return runs.oldest_wait_started_at || null;
12391251
},
12401252
1253+
operatorRetryingActivityAgeAvailable() {
1254+
const activities = (this.operatorMetrics && this.operatorMetrics.activities) || {};
1255+
1256+
if (activities.max_retrying_age_ms === undefined
1257+
|| activities.max_retrying_age_ms === null) {
1258+
return false;
1259+
}
1260+
1261+
return Number(activities.retrying || 0) > 0
1262+
|| Number(activities.max_retrying_age_ms || 0) > 0;
1263+
},
1264+
1265+
operatorRetryingActivityOldestStartedAt() {
1266+
const activities = (this.operatorMetrics && this.operatorMetrics.activities) || {};
1267+
1268+
return activities.oldest_retrying_started_at || null;
1269+
},
1270+
1271+
operatorClaimFailedAgeAvailable() {
1272+
const tasks = (this.operatorMetrics && this.operatorMetrics.tasks) || {};
1273+
1274+
if (tasks.max_claim_failed_age_ms === undefined
1275+
|| tasks.max_claim_failed_age_ms === null) {
1276+
return false;
1277+
}
1278+
1279+
return Number(tasks.claim_failed || 0) > 0
1280+
|| Number(tasks.max_claim_failed_age_ms || 0) > 0;
1281+
},
1282+
1283+
operatorClaimFailedOldestAt() {
1284+
const tasks = (this.operatorMetrics && this.operatorMetrics.tasks) || {};
1285+
1286+
return tasks.oldest_claim_failed_at || null;
1287+
},
1288+
12411289
operatorSchedulesAvailable() {
12421290
const schedules = this.operatorMetrics && this.operatorMetrics.schedules;
12431291

tests/Feature/V2DashboardStatsControllerTest.php

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,64 @@ public function testIndexExposesRunWaitAge(): void
831831
);
832832
}
833833

834+
public function testIndexExposesRetryingActivityAge(): void
835+
{
836+
config()->set('waterline.engine_source', 'v2');
837+
838+
$response = $this->get('/waterline/api/stats')->assertStatus(200);
839+
840+
$activities = $response->json('operator_metrics.activities');
841+
842+
if (! is_array($activities)
843+
|| ! array_key_exists('max_retrying_age_ms', $activities)
844+
|| ! array_key_exists('oldest_retrying_started_at', $activities)) {
845+
$this->markTestSkipped(
846+
'Vendored workflow package predates the retrying-activity age rollout-safety '
847+
. 'contract (operator_metrics.activities.{oldest_retrying_started_at,max_retrying_age_ms}).',
848+
);
849+
}
850+
851+
$this->assertTrue(
852+
$activities['oldest_retrying_started_at'] === null
853+
|| is_string($activities['oldest_retrying_started_at']),
854+
'operator_metrics.activities.oldest_retrying_started_at must be null or ISO-8601 string',
855+
);
856+
$this->assertTrue(
857+
$activities['max_retrying_age_ms'] === null
858+
|| is_int($activities['max_retrying_age_ms']),
859+
'operator_metrics.activities.max_retrying_age_ms must be null or integer milliseconds',
860+
);
861+
}
862+
863+
public function testIndexExposesClaimFailedAge(): void
864+
{
865+
config()->set('waterline.engine_source', 'v2');
866+
867+
$response = $this->get('/waterline/api/stats')->assertStatus(200);
868+
869+
$tasks = $response->json('operator_metrics.tasks');
870+
871+
if (! is_array($tasks)
872+
|| ! array_key_exists('max_claim_failed_age_ms', $tasks)
873+
|| ! array_key_exists('oldest_claim_failed_at', $tasks)) {
874+
$this->markTestSkipped(
875+
'Vendored workflow package predates the claim-failed age rollout-safety '
876+
. 'contract (operator_metrics.tasks.{oldest_claim_failed_at,max_claim_failed_age_ms}).',
877+
);
878+
}
879+
880+
$this->assertTrue(
881+
$tasks['oldest_claim_failed_at'] === null
882+
|| is_string($tasks['oldest_claim_failed_at']),
883+
'operator_metrics.tasks.oldest_claim_failed_at must be null or ISO-8601 string',
884+
);
885+
$this->assertTrue(
886+
$tasks['max_claim_failed_age_ms'] === null
887+
|| is_int($tasks['max_claim_failed_age_ms']),
888+
'operator_metrics.tasks.max_claim_failed_age_ms must be null or integer milliseconds',
889+
);
890+
}
891+
834892
public function testIndexExposesMatchingRole(): void
835893
{
836894
config()->set('waterline.engine_source', 'v2');

0 commit comments

Comments
 (0)