Add media server cards, and stop re-counting whole libraries every minute
The counts need the entire library downloaded to compute six numbers — 16.0 MB from Radarr and 5.5 MB from Sonarr, measured — and they were on the same one-minute clock as queue depth and health, which pulled ~30 GB a day out of the arrs to re-count records that had not changed. They now cache for fifteen minutes against how often the numbers actually move: 7.6s to 3.0s per refresh. Emby and Jellyfin get a card each: version, CPU, memory, uptime, streams, users and transcodes, with update/restart flags. Two halves of one question — a server answering happily at 145% CPU is a different situation from one at 8%, and neither the app nor the container says so alone.
This commit is contained in:
@@ -112,6 +112,60 @@ function vv_arr_http(string $url, string $apiKey, string $path, int $timeout = 4
|
||||
|
||||
// ── Live arr data ─────────────────────────────────────────────────────────────
|
||||
|
||||
// ── Library counts, on their own clock ────────────────────────────────────────
|
||||
// Six numbers — total, monitored, and the file/episode/album count — and the only way to get them
|
||||
// from these APIs is to download the entire library and count it. Measured against this install:
|
||||
// 16.0 MB from Radarr and 5.5 MB from Sonarr, per fetch.
|
||||
//
|
||||
// The rest of this payload refreshes every minute because queue depth and health genuinely change
|
||||
// that fast. These do not: a library gains items a few times a day. Refreshing them on the same
|
||||
// clock pulled ~21.5 MB out of the arrs every sixty seconds — about 30 GB a day — to re-count
|
||||
// records that had not changed. The page cache hid it perfectly, which is why it ran that way for
|
||||
// as long as it did: the cost fell entirely on the background writer and on the arrs themselves.
|
||||
//
|
||||
// Fifteen minutes is chosen against how often the numbers move, not how fresh they could be. A
|
||||
// library count that is ten minutes stale has never been the wrong answer to anything, and the
|
||||
// discovery scripts that add items run far less often than that.
|
||||
//
|
||||
// Keyed per instance URL so two hosts' Sonarrs never share an entry, and only written on success —
|
||||
// caching an empty result would keep an arr looking empty for fifteen minutes after it came back.
|
||||
function vv_arr_library_counts(array $arr): array {
|
||||
$ck = 'arr_counts_' . $arr['type'] . '_' . substr(sha1($arr['url']), 0, 10);
|
||||
$hit = vv_cache_read($ck, 900);
|
||||
if ($hit !== null) return $hit;
|
||||
|
||||
$base = '/api/' . $arr['api'];
|
||||
$out = [];
|
||||
|
||||
if ($arr['type'] === 'sonarr') {
|
||||
$data = vv_arr_http($arr['url'], $arr['key'], "$base/series", 20);
|
||||
if (is_array($data)) {
|
||||
$out['total'] = count($data);
|
||||
$out['monitored'] = count(array_filter($data, fn($x) => !empty($x['monitored'])));
|
||||
$out['episodes'] = array_sum(array_map(
|
||||
fn($x) => $x['statistics']['episodeFileCount'] ?? $x['episodeFileCount'] ?? 0, $data));
|
||||
}
|
||||
} elseif ($arr['type'] === 'radarr') {
|
||||
$data = vv_arr_http($arr['url'], $arr['key'], "$base/movie", 20);
|
||||
if (is_array($data)) {
|
||||
$out['total'] = count($data);
|
||||
$out['monitored'] = count(array_filter($data, fn($x) => !empty($x['monitored'])));
|
||||
$out['files'] = count(array_filter($data, fn($x) => !empty($x['hasFile'])));
|
||||
}
|
||||
} elseif ($arr['type'] === 'lidarr') {
|
||||
$data = vv_arr_http($arr['url'], $arr['key'], "$base/artist", 20);
|
||||
if (is_array($data)) {
|
||||
$out['total'] = count($data);
|
||||
$out['monitored'] = count(array_filter($data, fn($x) => !empty($x['monitored'])));
|
||||
$out['albums'] = array_sum(array_map(
|
||||
fn($a) => $a['statistics']['albumCount'] ?? $a['albumCount'] ?? 0, $data));
|
||||
}
|
||||
}
|
||||
|
||||
if ($out) vv_cache_write($ck, $out);
|
||||
return $out;
|
||||
}
|
||||
|
||||
function vv_fetch_arr_live(array $arr): array {
|
||||
$url = $arr['url'];
|
||||
$key = $arr['key'];
|
||||
@@ -126,30 +180,7 @@ function vv_fetch_arr_live(array $arr): array {
|
||||
$out['online'] = true;
|
||||
$out['version'] = $sys['version'] ?? null;
|
||||
|
||||
if ($type === 'sonarr') {
|
||||
$data = vv_arr_http($url, $key, "$base/series");
|
||||
if (is_array($data)) {
|
||||
$out['total'] = count($data);
|
||||
$out['monitored'] = count(array_filter($data, fn($x) => !empty($x['monitored'])));
|
||||
$out['episodes'] = array_sum(array_map(
|
||||
fn($x) => $x['statistics']['episodeFileCount'] ?? $x['episodeFileCount'] ?? 0, $data));
|
||||
}
|
||||
} elseif ($type === 'radarr') {
|
||||
$data = vv_arr_http($url, $key, "$base/movie");
|
||||
if (is_array($data)) {
|
||||
$out['total'] = count($data);
|
||||
$out['monitored'] = count(array_filter($data, fn($x) => !empty($x['monitored'])));
|
||||
$out['files'] = count(array_filter($data, fn($x) => !empty($x['hasFile'])));
|
||||
}
|
||||
} elseif ($type === 'lidarr') {
|
||||
$data = vv_arr_http($url, $key, "$base/artist");
|
||||
if (is_array($data)) {
|
||||
$out['total'] = count($data);
|
||||
$out['monitored'] = count(array_filter($data, fn($x) => !empty($x['monitored'])));
|
||||
$out['albums'] = array_sum(array_map(
|
||||
fn($a) => $a['statistics']['albumCount'] ?? $a['albumCount'] ?? 0, $data));
|
||||
}
|
||||
}
|
||||
$out += vv_arr_library_counts($arr);
|
||||
|
||||
$q = vv_arr_http($url, $key, "$base/queue?page=1&pageSize=500");
|
||||
if (is_array($q)) {
|
||||
@@ -454,7 +485,8 @@ function vv_arrs_all(): array {
|
||||
// The three media jobs that operate on the same files the arrs manage and had no tab of
|
||||
// their own. Cheap — three run records and three log tails — and it rides this payload
|
||||
// rather than a fourth endpoint because it is shown on this page and cached with it.
|
||||
'media_jobs' => function_exists('vv_media_jobs') ? vv_media_jobs() : [],
|
||||
'media_jobs' => function_exists('vv_media_jobs') ? vv_media_jobs() : [],
|
||||
'media_servers' => function_exists('vv_media_server_stats') ? vv_media_server_stats() : [],
|
||||
'host' => $currentHost,
|
||||
'settings' => [
|
||||
'arr_sync_enabled' => ($vars['ARR_SYNC_ENABLED'] ?? 'true') !== 'false',
|
||||
|
||||
Reference in New Issue
Block a user