79 lines
3.3 KiB
PHP
79 lines
3.3 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Models\StatusDay;
|
|
use App\Services\Status\ServiceHealth;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Carbon;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
/**
|
|
* Writes one sample of every public component into today's row.
|
|
*
|
|
* The ninety-day bar on the status page is the one figure a reader checks
|
|
* against their own memory, so it has to come from something that was actually
|
|
* recorded at the time. There is no way to reconstruct it afterwards: the
|
|
* monitoring table holds the LAST verdict per instance, not a history, and by
|
|
* the time anybody asks about last Tuesday that verdict is about today.
|
|
*
|
|
* Runs on the scheduler beside the monitoring sync it depends on. A missed run
|
|
* costs one sample out of roughly three hundred in a day; a missed DAY leaves
|
|
* no row at all, and the page draws that as "not recorded" rather than
|
|
* inventing a colour for it.
|
|
*/
|
|
class SampleServiceStatus extends Command
|
|
{
|
|
protected $signature = 'clupilot:sample-status';
|
|
|
|
protected $description = 'Record the current state of every public status component into the daily history';
|
|
|
|
public function handle(ServiceHealth $health): int
|
|
{
|
|
// The wall clock, not UTC: the bar is labelled with days a reader
|
|
// recognises, and a sample taken at 01:30 local belongs to the day they
|
|
// would call it (R19).
|
|
$day = Carbon::now()->local()->toDateString();
|
|
|
|
foreach ($health->components() as $component) {
|
|
$state = $component['state'];
|
|
|
|
// One statement, so two schedulers racing on the same minute cannot
|
|
// read-modify-write over each other. The counters are increments,
|
|
// never assignments — a sample is a thing that happened, and a
|
|
// later run must not be able to undo it.
|
|
DB::table('status_days')->upsert(
|
|
[[
|
|
'day' => $day,
|
|
'component' => $component['key'],
|
|
'samples' => 1,
|
|
'operational' => $state === 'operational' ? 1 : 0,
|
|
'degraded' => $state === 'degraded' ? 1 : 0,
|
|
'down' => $state === 'down' ? 1 : 0,
|
|
'unknown' => $state === 'unknown' ? 1 : 0,
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
]],
|
|
['day', 'component'],
|
|
[
|
|
'samples' => DB::raw('samples + 1'),
|
|
'operational' => DB::raw('operational + '.($state === 'operational' ? 1 : 0)),
|
|
'degraded' => DB::raw('degraded + '.($state === 'degraded' ? 1 : 0)),
|
|
'down' => DB::raw('down + '.($state === 'down' ? 1 : 0)),
|
|
'unknown' => DB::raw('unknown + '.($state === 'unknown' ? 1 : 0)),
|
|
'updated_at' => DB::raw('CURRENT_TIMESTAMP'),
|
|
],
|
|
);
|
|
}
|
|
|
|
// Beyond the window the page can draw, the rows are only weight. Kept a
|
|
// fortnight longer than the ninety days shown so a change of window
|
|
// does not immediately hit an empty table.
|
|
StatusDay::query()
|
|
->where('day', '<', Carbon::now()->local()->subDays(104)->toDateString())
|
|
->delete();
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
}
|