mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-28 17:01:16 +00:00
Update main monitor pane
This commit is contained in:
@@ -349,6 +349,9 @@ TINYMCE_API_KEY=
|
||||
TMUX_USE_POWERLINE=true
|
||||
TMUX_USE_NERD_FONTS=true
|
||||
TMUX_TERMINAL=tmux-256color
|
||||
# Main monitor loop delay and slow-statistics refresh interval, in seconds.
|
||||
TMUX_MONITOR_DELAY=10
|
||||
TMUX_REFRESH_INTERVAL=60
|
||||
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
# Docker / Sail
|
||||
|
||||
@@ -97,7 +97,7 @@ class TmuxMonitor extends Command
|
||||
|
||||
// Increment iteration and sleep
|
||||
$this->monitor->incrementIteration();
|
||||
sleep(10);
|
||||
sleep(max(1, (int) config('tmux.monitor.delay', 10)));
|
||||
}
|
||||
|
||||
$this->info('🛑 Monitor stopped by exit flag');
|
||||
|
||||
@@ -235,14 +235,25 @@ final class AdditionalCandidateQuery
|
||||
*/
|
||||
public static function backlogCounts(): array
|
||||
{
|
||||
$counts = ['total' => 0, 'available' => 0];
|
||||
$query = self::baseBuilder(includeClaimed: true)
|
||||
->selectRaw('COUNT(*) AS total_count');
|
||||
|
||||
foreach (self::bucketBacklog() as $backlog) {
|
||||
$counts['total'] += $backlog['total'];
|
||||
$counts['available'] += $backlog['available'];
|
||||
if (self::supportsClaims()) {
|
||||
$query->selectRaw(
|
||||
'SUM(CASE WHEN r.'.self::CLAIMED_AT_COLUMN.' IS NULL OR r.'.self::CLAIMED_AT_COLUMN.' < ? THEN 1 ELSE 0 END) AS available_count',
|
||||
[self::claimStaleBefore()],
|
||||
);
|
||||
} else {
|
||||
$query->selectRaw('COUNT(*) AS available_count');
|
||||
}
|
||||
|
||||
return $counts;
|
||||
/** @var object{total_count: int|string|null, available_count: int|string|null}|null $counts */
|
||||
$counts = $query->toBase()->first();
|
||||
|
||||
return [
|
||||
'total' => (int) ($counts->total_count ?? 0),
|
||||
'available' => (int) ($counts->available_count ?? 0),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+42
-48
@@ -49,53 +49,51 @@ class Tmux
|
||||
return $runVar['connections'];
|
||||
}
|
||||
|
||||
public function getUSPConnections(string $which, mixed $connections): mixed
|
||||
/**
|
||||
* @param array<string, mixed> $connections
|
||||
* @return array<string, array{active: int, total: int}>
|
||||
*/
|
||||
public function getUSPConnections(string $which, array $connections, ?string $socketSnapshot = null): array
|
||||
{
|
||||
switch ($which) {
|
||||
case 'alternate':
|
||||
$ip = 'ip_a';
|
||||
$port = 'port_a';
|
||||
break;
|
||||
case 'primary':
|
||||
default:
|
||||
$ip = 'ip';
|
||||
$port = 'port';
|
||||
break;
|
||||
[$ipKey, $portKey] = $which === 'alternate'
|
||||
? ['ip_a', 'port_a']
|
||||
: ['ip', 'port'];
|
||||
|
||||
$ip = (string) ($connections[$ipKey] ?? '');
|
||||
$port = (string) ($connections[$portKey] ?? '');
|
||||
$needles = array_values(array_filter([
|
||||
$ip !== '' && $port !== '' ? $ip.':'.$port : null,
|
||||
$ip !== '' ? $ip.':https' : null,
|
||||
$port !== '' ? $port : null,
|
||||
$ip !== '' ? $ip : null,
|
||||
]));
|
||||
$lines = preg_split('/\R/', $socketSnapshot ?? $this->getSocketSnapshot()) ?: [];
|
||||
|
||||
foreach ($needles as $needle) {
|
||||
$matchingLines = array_filter(
|
||||
$lines,
|
||||
static fn (string $line): bool => str_contains($line, $needle),
|
||||
);
|
||||
|
||||
if ($matchingLines !== []) {
|
||||
return [
|
||||
$which => [
|
||||
'active' => count(array_filter(
|
||||
$matchingLines,
|
||||
static fn (string $line): bool => str_contains($line, 'ESTAB'),
|
||||
)),
|
||||
'total' => count($matchingLines),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize result structure
|
||||
$runVar['conncounts'][$which]['active'] = '0';
|
||||
$runVar['conncounts'][$which]['total'] = '0';
|
||||
return [$which => ['active' => 0, 'total' => 0]];
|
||||
}
|
||||
|
||||
// 1) Try exact host:port
|
||||
if (! empty($connections[$ip]) && ! empty($connections[$port])) {
|
||||
$needle = escapeshellarg($connections[$ip].':'.$connections[$port]);
|
||||
$runVar['conncounts'][$which]['active'] = str_replace("\n", '', shell_exec("ss -n 2>/dev/null | grep -- $needle | grep -c -- ESTAB")) ?: '0';
|
||||
$runVar['conncounts'][$which]['total'] = str_replace("\n", '', shell_exec("ss -n 2>/dev/null | grep -c -- $needle")) ?: '0';
|
||||
}
|
||||
|
||||
// 2) Fallback to host:https
|
||||
if ((int) $runVar['conncounts'][$which]['active'] === 0 && (int) $runVar['conncounts'][$which]['total'] === 0 && ! empty($connections[$ip])) {
|
||||
$needleHttps = escapeshellarg($connections[$ip].':https');
|
||||
$runVar['conncounts'][$which]['active'] = str_replace("\n", '', shell_exec("ss -n 2>/dev/null | grep -- $needleHttps | grep -c -- ESTAB")) ?: '0';
|
||||
$runVar['conncounts'][$which]['total'] = str_replace("\n", '', shell_exec("ss -n 2>/dev/null | grep -c -- $needleHttps")) ?: '0';
|
||||
}
|
||||
|
||||
// 3) Fallback to port only
|
||||
if ((int) $runVar['conncounts'][$which]['active'] === 0 && (int) $runVar['conncounts'][$which]['total'] === 0 && ! empty($connections[$port])) {
|
||||
$needlePort = escapeshellarg((string) $connections[$port]);
|
||||
$runVar['conncounts'][$which]['active'] = str_replace("\n", '', shell_exec("ss -n 2>/dev/null | grep -- $needlePort | grep -c -- ESTAB")) ?: '0';
|
||||
$runVar['conncounts'][$which]['total'] = str_replace("\n", '', shell_exec("ss -n 2>/dev/null | grep -c -- $needlePort")) ?: '0';
|
||||
}
|
||||
|
||||
// 4) Fallback to host only
|
||||
if ((int) $runVar['conncounts'][$which]['active'] === 0 && (int) $runVar['conncounts'][$which]['total'] === 0 && ! empty($connections[$ip])) {
|
||||
$needleIp = escapeshellarg($connections[$ip]);
|
||||
$runVar['conncounts'][$which]['active'] = str_replace("\n", '', shell_exec("ss -n 2>/dev/null | grep -- $needleIp | grep -c -- ESTAB")) ?: '0';
|
||||
$runVar['conncounts'][$which]['total'] = str_replace("\n", '', shell_exec("ss -n 2>/dev/null | grep -c -- $needleIp")) ?: '0';
|
||||
}
|
||||
|
||||
return $runVar['conncounts'];
|
||||
public function getSocketSnapshot(): string
|
||||
{
|
||||
return (string) shell_exec('ss -nH 2>/dev/null');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -377,10 +375,6 @@ class Tmux
|
||||
"
|
||||
SELECT
|
||||
(SELECT TABLE_ROWS FROM information_schema.TABLES WHERE table_name = 'predb' AND TABLE_SCHEMA = %1\$s) AS predb,
|
||||
(SELECT TABLE_ROWS FROM information_schema.TABLES WHERE table_name = 'missed_parts' AND TABLE_SCHEMA = %1\$s) AS missed_parts_table,
|
||||
(SELECT TABLE_ROWS FROM information_schema.TABLES WHERE table_name = 'parts' AND TABLE_SCHEMA = %1\$s) AS parts_table,
|
||||
(SELECT TABLE_ROWS FROM information_schema.TABLES WHERE table_name = 'binaries' AND TABLE_SCHEMA = %1\$s) AS binaries_table,
|
||||
(SELECT TABLE_ROWS FROM information_schema.TABLES WHERE table_name = 'collections' AND TABLE_SCHEMA = %1\$s) AS releases,
|
||||
(SELECT COUNT(id) FROM usenet_groups WHERE first_record IS NOT NULL AND backfill = 1
|
||||
AND (now() - INTERVAL backfill_target DAY) < first_record_postdate
|
||||
) AS backfill_groups_days,
|
||||
@@ -449,7 +443,7 @@ class Tmux
|
||||
{
|
||||
return DB::select(
|
||||
"
|
||||
SELECT TABLE_NAME AS name
|
||||
SELECT TABLE_NAME AS name, TABLE_ROWS AS row_count
|
||||
FROM information_schema.TABLES
|
||||
WHERE TABLE_SCHEMA = (SELECT DATABASE())
|
||||
AND TABLE_NAME REGEXP {escapeString('^(multigroup_)?(collections|binaries|parts|missed_parts)(_[0-9]+)?$')}
|
||||
|
||||
@@ -27,6 +27,10 @@ class TmuxMonitorService
|
||||
|
||||
protected bool $shouldContinue = true;
|
||||
|
||||
protected float $lastOperationalRefreshAt = 0.0;
|
||||
|
||||
protected float $lastSlowRefreshAt = 0.0;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->tmux = new Tmux;
|
||||
@@ -124,46 +128,52 @@ class TmuxMonitorService
|
||||
*/
|
||||
public function collectStatistics(): array
|
||||
{
|
||||
// Refresh settings periodically
|
||||
$monitorDelay = (int) ($this->runVar['settings']['monitor'] ?? 60);
|
||||
$timeSinceLastRefresh = time() - ($this->runVar['timers']['timer2'] ?? 0);
|
||||
$now = microtime(true);
|
||||
$monitorDelay = max(1, (int) ($this->runVar['settings']['monitor'] ?? 60));
|
||||
$slowRefreshDelay = max($monitorDelay, (int) config('tmux.monitor.refresh_interval', 60));
|
||||
$statisticsChanged = false;
|
||||
|
||||
if ($this->iterations === 1 || $timeSinceLastRefresh >= $monitorDelay) {
|
||||
$this->refreshStatistics();
|
||||
$this->runVar['timers']['timer2'] = time();
|
||||
if ($this->refreshIsDue($this->lastOperationalRefreshAt, $monitorDelay, $now)) {
|
||||
$this->refreshOperationalStatistics();
|
||||
$this->lastOperationalRefreshAt = $now;
|
||||
$this->runVar['timers']['timer2'] = (int) $now;
|
||||
$statisticsChanged = true;
|
||||
}
|
||||
|
||||
// Update connection counts
|
||||
$this->updateConnectionCounts();
|
||||
if ($this->refreshIsDue($this->lastSlowRefreshAt, $slowRefreshDelay, $now)) {
|
||||
$this->refreshSlowStatistics();
|
||||
$this->lastSlowRefreshAt = $now;
|
||||
$statisticsChanged = true;
|
||||
}
|
||||
|
||||
// Set killswitches
|
||||
if ($statisticsChanged) {
|
||||
$this->calculateStatistics();
|
||||
}
|
||||
|
||||
$this->updateConnectionCounts();
|
||||
$this->setKillswitches();
|
||||
|
||||
return $this->runVar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh all statistics from database
|
||||
*/
|
||||
protected function refreshStatistics(): void
|
||||
protected function refreshOperationalStatistics(): void
|
||||
{
|
||||
$timer = time();
|
||||
|
||||
// Refresh settings
|
||||
$timer = microtime(true);
|
||||
$this->runVar['settings'] = $this->tmux->getMonitorSettings();
|
||||
$this->runVar['timers']['query']['tmux_time'] = time() - $timer;
|
||||
$this->runVar['timers']['query']['tmux_time'] = microtime(true) - $timer;
|
||||
|
||||
// Get category counts
|
||||
$this->getCategoryCounts();
|
||||
|
||||
// Get process counts
|
||||
$this->getProcessCounts();
|
||||
}
|
||||
|
||||
// Get table counts
|
||||
protected function refreshSlowStatistics(): void
|
||||
{
|
||||
$this->getCategoryCounts();
|
||||
$this->getTableCounts();
|
||||
}
|
||||
|
||||
// Calculate diffs and percentages
|
||||
$this->calculateStatistics();
|
||||
protected function refreshIsDue(float $lastRefreshAt, int $interval, float $now): bool
|
||||
{
|
||||
return $lastRefreshAt === 0.0 || ($now - $lastRefreshAt) >= $interval;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,41 +181,46 @@ class TmuxMonitorService
|
||||
*/
|
||||
protected function getCategoryCounts(): void
|
||||
{
|
||||
$timer = time();
|
||||
$timer = microtime(true);
|
||||
$bindings = [];
|
||||
$aggregates = [];
|
||||
|
||||
$this->runVar['counts']['now']['tv'] = Release::query()
|
||||
->whereBetween('categories_id', [Category::TV_ROOT, Category::TV_OTHER])
|
||||
->count('id');
|
||||
foreach ($this->categoryRanges() as $name => [$minimum, $maximum]) {
|
||||
$aggregates[] = "SUM(CASE WHEN categories_id BETWEEN ? AND ? THEN 1 ELSE 0 END) AS {$name}";
|
||||
$bindings[] = $minimum;
|
||||
$bindings[] = $maximum;
|
||||
}
|
||||
|
||||
$this->runVar['counts']['now']['movies'] = Release::query()
|
||||
->whereBetween('categories_id', [Category::MOVIE_ROOT, Category::MOVIE_OTHER])
|
||||
->count('id');
|
||||
try {
|
||||
$counts = Release::query()->selectRaw(implode(', ', $aggregates), $bindings)->first();
|
||||
|
||||
$this->runVar['counts']['now']['audio'] = Release::query()
|
||||
->whereBetween('categories_id', [Category::MUSIC_ROOT, Category::MUSIC_OTHER])
|
||||
->count('id');
|
||||
if ($counts !== null) {
|
||||
foreach (array_keys($this->categoryRanges()) as $name) {
|
||||
$this->runVar['counts']['now'][$name] = (int) $counts->getAttribute($name);
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
logger()->error('Error collecting category counts: '.$e->getMessage());
|
||||
}
|
||||
|
||||
$this->runVar['counts']['now']['books'] = Release::query()
|
||||
->whereBetween('categories_id', [Category::BOOKS_ROOT, Category::BOOKS_UNKNOWN])
|
||||
->count('id');
|
||||
$this->runVar['timers']['query']['init_time'] = microtime(true) - $timer;
|
||||
}
|
||||
|
||||
$this->runVar['counts']['now']['console'] = Release::query()
|
||||
->whereBetween('categories_id', [Category::GAME_ROOT, Category::GAME_OTHER])
|
||||
->count('id');
|
||||
|
||||
$this->runVar['counts']['now']['pc'] = Release::query()
|
||||
->whereBetween('categories_id', [Category::PC_ROOT, Category::PC_PHONE_ANDROID])
|
||||
->count('id');
|
||||
|
||||
$this->runVar['counts']['now']['xxx'] = Release::query()
|
||||
->whereBetween('categories_id', [Category::XXX_ROOT, Category::XXX_OTHER])
|
||||
->count('id');
|
||||
|
||||
$this->runVar['counts']['now']['misc'] = Release::query()
|
||||
->whereBetween('categories_id', [Category::OTHER_ROOT, Category::OTHER_HASHED])
|
||||
->count('id');
|
||||
|
||||
$this->runVar['timers']['query']['init_time'] = time() - $timer;
|
||||
/**
|
||||
* @return array<string, array{int, int}>
|
||||
*/
|
||||
protected function categoryRanges(): array
|
||||
{
|
||||
return [
|
||||
'tv' => [Category::TV_ROOT, Category::TV_OTHER],
|
||||
'movies' => [Category::MOVIE_ROOT, Category::MOVIE_OTHER],
|
||||
'audio' => [Category::MUSIC_ROOT, Category::MUSIC_OTHER],
|
||||
'books' => [Category::BOOKS_ROOT, Category::BOOKS_UNKNOWN],
|
||||
'console' => [Category::GAME_ROOT, Category::GAME_OTHER],
|
||||
'pc' => [Category::PC_ROOT, Category::PC_PHONE_ANDROID],
|
||||
'xxx' => [Category::XXX_ROOT, Category::XXX_OTHER],
|
||||
'misc' => [Category::OTHER_ROOT, Category::OTHER_HASHED],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,7 +228,7 @@ class TmuxMonitorService
|
||||
*/
|
||||
protected function getProcessCounts(): void
|
||||
{
|
||||
$timer = time();
|
||||
$timer = microtime(true);
|
||||
$this->runVar['counts']['now']['work'] = $this->runVar['counts']['now']['work'] ?? 0;
|
||||
$this->runVar['counts']['now']['work_available'] = $this->runVar['counts']['now']['work_available'] ?? 0;
|
||||
|
||||
@@ -229,10 +244,10 @@ class TmuxMonitorService
|
||||
}
|
||||
}
|
||||
|
||||
$this->runVar['timers']['query']['proc1_time'] = time() - $timer;
|
||||
$this->runVar['timers']['query']['proc1_time'] = microtime(true) - $timer;
|
||||
|
||||
// Process 2
|
||||
$timer2 = time();
|
||||
$timer2 = microtime(true);
|
||||
$maxSize = $this->runVar['settings']['maxsize_pp'] ?? '';
|
||||
$minSize = $this->runVar['settings']['minsize_pp'] ?? '';
|
||||
|
||||
@@ -253,7 +268,7 @@ class TmuxMonitorService
|
||||
$this->runVar['counts']['now']['work'] = $additionalBacklog['total'];
|
||||
$this->runVar['counts']['now']['work_available'] = $additionalBacklog['available'];
|
||||
|
||||
$this->runVar['timers']['query']['proc2_time'] = time() - $timer2;
|
||||
$this->runVar['timers']['query']['proc2_time'] = microtime(true) - $timer2;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
logger()->error('Error collecting process counts: '.$e->getMessage());
|
||||
@@ -265,77 +280,65 @@ class TmuxMonitorService
|
||||
*/
|
||||
protected function getTableCounts(): void
|
||||
{
|
||||
$timer = time();
|
||||
$timer = microtime(true);
|
||||
|
||||
try {
|
||||
$this->runVar['counts']['now']['collections_table'] = Collection::query()->count();
|
||||
$this->runVar['counts']['now']['releases'] = Release::query()->count();
|
||||
|
||||
// Get binaries/parts counts from information_schema or use approximation
|
||||
$dbName = config('nntmux.db_name');
|
||||
$tables = $this->tmux->cbpmTableQuery();
|
||||
|
||||
$this->runVar['counts']['now']['binaries_table'] = 0;
|
||||
$this->runVar['counts']['now']['parts_table'] = 0;
|
||||
$this->runVar['counts']['now']['missed_parts_table'] = 0;
|
||||
|
||||
foreach ($tables as $table) {
|
||||
$tableName = $table->name;
|
||||
$count = $this->getTableRowCount($tableName);
|
||||
|
||||
if (str_contains($tableName, 'binaries')) {
|
||||
$this->runVar['counts']['now']['binaries_table'] += $count;
|
||||
} elseif (str_contains($tableName, 'missed_parts')) {
|
||||
$this->runVar['counts']['now']['missed_parts_table'] += $count;
|
||||
} elseif (str_contains($tableName, 'parts')) {
|
||||
$this->runVar['counts']['now']['parts_table'] += $count;
|
||||
}
|
||||
foreach ($this->aggregateTableRowEstimates($this->tmux->cbpmTableQuery()) as $key => $count) {
|
||||
$this->runVar['counts']['now'][$key] = $count;
|
||||
}
|
||||
|
||||
$this->runVar['timers']['query']['tpg_time'] = time() - $timer;
|
||||
$this->runVar['timers']['query']['tpg_time'] = microtime(true) - $timer;
|
||||
|
||||
// Get additional table counts (query 4)
|
||||
$timer4 = time();
|
||||
$proc4Query = $this->tmux->proc_query(4, $dbName, '');
|
||||
$proc4Result = DB::selectOne($proc4Query);
|
||||
foreach ([4, 6] as $queryNumber) {
|
||||
$result = DB::selectOne($this->tmux->proc_query($queryNumber, (string) config('nntmux.db_name'), ''));
|
||||
if ($result === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($proc4Result) {
|
||||
foreach ((array) $proc4Result as $key => $value) {
|
||||
$this->runVar['counts']['now'][$key] = $value;
|
||||
$target = $queryNumber === 4 ? 'counts' : 'timers';
|
||||
$section = $queryNumber === 4 ? 'now' : 'newOld';
|
||||
foreach ((array) $result as $key => $value) {
|
||||
$this->runVar[$target][$section][$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Get newest/oldest data (query 6)
|
||||
$timer6 = time();
|
||||
$proc6Query = $this->tmux->proc_query(6, $dbName, '');
|
||||
$proc6Result = DB::selectOne($proc6Query);
|
||||
|
||||
if ($proc6Result) {
|
||||
foreach ((array) $proc6Result as $key => $value) {
|
||||
$this->runVar['timers']['newOld'][$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
logger()->error('Error collecting table counts: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get row count for a table
|
||||
* @param array<array-key, mixed> $tables
|
||||
* @return array{binaries_table: int, parts_table: int, missed_parts_table: int}
|
||||
*/
|
||||
protected function getTableRowCount(string $tableName): int
|
||||
protected function aggregateTableRowEstimates(array $tables): array
|
||||
{
|
||||
try {
|
||||
$result = DB::selectOne(
|
||||
'SELECT TABLE_ROWS AS count FROM information_schema.TABLES
|
||||
WHERE TABLE_NAME = ? AND TABLE_SCHEMA = DATABASE()',
|
||||
[$tableName]
|
||||
);
|
||||
$counts = [
|
||||
'binaries_table' => 0,
|
||||
'parts_table' => 0,
|
||||
'missed_parts_table' => 0,
|
||||
];
|
||||
|
||||
return (int) ($result->count ?? 0);
|
||||
} catch (\Exception $e) {
|
||||
return 0;
|
||||
foreach ($tables as $table) {
|
||||
if (! is_object($table)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tableName = (string) ($table->name ?? '');
|
||||
$count = (int) ($table->row_count ?? 0);
|
||||
|
||||
if (str_contains($tableName, 'binaries')) {
|
||||
$counts['binaries_table'] += $count;
|
||||
} elseif (str_contains($tableName, 'missed_parts')) {
|
||||
$counts['missed_parts_table'] += $count;
|
||||
} elseif (str_contains($tableName, 'parts')) {
|
||||
$counts['parts_table'] += $count;
|
||||
}
|
||||
}
|
||||
|
||||
return $counts;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -485,15 +488,18 @@ class TmuxMonitorService
|
||||
*/
|
||||
protected function updateConnectionCounts(): void
|
||||
{
|
||||
$socketSnapshot = $this->tmux->getSocketSnapshot();
|
||||
$this->runVar['conncounts'] = $this->tmux->getUSPConnections(
|
||||
'primary',
|
||||
$this->runVar['connections']
|
||||
$this->runVar['connections'],
|
||||
$socketSnapshot,
|
||||
);
|
||||
|
||||
if ((int) ($this->runVar['constants']['alternate_nntp'] ?? 0) === 1) {
|
||||
$alternateConns = $this->tmux->getUSPConnections(
|
||||
'alternate',
|
||||
$this->runVar['connections']
|
||||
$this->runVar['connections'],
|
||||
$socketSnapshot,
|
||||
);
|
||||
$this->runVar['conncounts'] = array_merge($this->runVar['conncounts'], $alternateConns);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,10 @@ class TmuxOutput extends Tmux
|
||||
|
||||
private mixed $tmpMasks;
|
||||
|
||||
private ?string $gitVersion = null;
|
||||
|
||||
private ?string $gitBranch = null;
|
||||
|
||||
/**
|
||||
* TmuxOutput constructor.
|
||||
*
|
||||
@@ -112,8 +116,11 @@ class TmuxOutput extends Tmux
|
||||
{
|
||||
$buffer = '';
|
||||
$state = ((int) $this->runVar['settings']['is_running'] === 1) ? 'Running' : 'Disabled';
|
||||
$version = str_replace(["\n", "\r"], '', Process::run('git describe --tags')->output());
|
||||
$branch = str_replace(["\n", "\r"], '', Process::run('git branch --show-current')->output());
|
||||
$this->gitVersion ??= trim(Process::run('git describe --tags')->output());
|
||||
$this->gitBranch ??= trim(Process::run('git branch --show-current')->output());
|
||||
|
||||
$version = $this->gitVersion;
|
||||
$branch = $this->gitBranch;
|
||||
|
||||
$buffer .= sprintf(
|
||||
$this->tmpMasks[2],
|
||||
@@ -395,52 +402,34 @@ class TmuxOutput extends Tmux
|
||||
|
||||
protected function _getPaths(): string
|
||||
{
|
||||
$buffer = '';
|
||||
$paths = array_values(array_unique(array_filter([
|
||||
$this->runVar['settings']['monitor_path'] ?? null,
|
||||
$this->runVar['settings']['monitor_path_a'] ?? null,
|
||||
$this->runVar['settings']['monitor_path_b'] ?? null,
|
||||
], static fn (mixed $path): bool => is_string($path) && $path !== '' && file_exists($path))));
|
||||
|
||||
// assign timers from tmux table
|
||||
$monitor_path = $this->runVar['settings']['monitor_path'];
|
||||
$monitor_path_a = $this->runVar['settings']['monitor_path_a'];
|
||||
$monitor_path_b = $this->runVar['settings']['monitor_path_b'];
|
||||
if ($paths === []) {
|
||||
return PHP_EOL;
|
||||
}
|
||||
|
||||
if (($monitor_path !== null && file_exists($monitor_path))
|
||||
|| ($monitor_path_a !== null && file_exists($monitor_path_a))
|
||||
|| ($monitor_path_b !== null && file_exists($monitor_path_b))) {
|
||||
$buffer .= "\n";
|
||||
$buffer .= sprintf($this->tmpMasks[3], 'File System', 'Used', 'Free');
|
||||
$buffer .= $this->_getSeparator();
|
||||
$buffer = PHP_EOL;
|
||||
$buffer .= sprintf($this->tmpMasks[3], 'File System', 'Used', 'Free');
|
||||
$buffer .= $this->_getSeparator();
|
||||
|
||||
if (! empty($monitor_path) && file_exists($monitor_path)) {
|
||||
$disk_use = $this->decodeSize(disk_total_space($monitor_path) - disk_free_space($monitor_path));
|
||||
$disk_free = $this->decodeSize(disk_free_space($monitor_path));
|
||||
if (basename($monitor_path) === '') {
|
||||
$show = '/';
|
||||
} else {
|
||||
$show = basename($monitor_path);
|
||||
}
|
||||
$buffer .= sprintf($this->tmpMasks[4], $show, $disk_use, $disk_free);
|
||||
foreach ($paths as $path) {
|
||||
$total = disk_total_space($path);
|
||||
$free = disk_free_space($path);
|
||||
if ($total === false || $free === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! empty($monitor_path_a) && file_exists($monitor_path_a)) {
|
||||
$disk_use = $this->decodeSize(disk_total_space($monitor_path_a) - disk_free_space($monitor_path_a));
|
||||
$disk_free = $this->decodeSize(disk_free_space($monitor_path_a));
|
||||
if (basename($monitor_path_a) === '') {
|
||||
$show = '/';
|
||||
} else {
|
||||
$show = basename($monitor_path_a);
|
||||
}
|
||||
$buffer .= sprintf($this->tmpMasks[4], $show, $disk_use, $disk_free);
|
||||
}
|
||||
|
||||
if (! empty($monitor_path_b) && file_exists($monitor_path_b)) {
|
||||
$disk_use = $this->decodeSize(disk_total_space($monitor_path_b) - disk_free_space($monitor_path_b));
|
||||
$disk_free = $this->decodeSize(disk_free_space($monitor_path_b));
|
||||
if (basename($monitor_path_b) === '') {
|
||||
$show = '/';
|
||||
} else {
|
||||
$show = basename($monitor_path_b);
|
||||
}
|
||||
$buffer .= sprintf($this->tmpMasks[4], $show, $disk_use, $disk_free);
|
||||
}
|
||||
$name = basename($path) ?: '/';
|
||||
$buffer .= sprintf(
|
||||
$this->tmpMasks[4],
|
||||
$name,
|
||||
$this->decodeSize($total - $free),
|
||||
$this->decodeSize($free),
|
||||
);
|
||||
}
|
||||
|
||||
return $buffer.PHP_EOL;
|
||||
@@ -455,7 +444,7 @@ class TmuxOutput extends Tmux
|
||||
$this->tmpMasks[4],
|
||||
'Combined',
|
||||
sprintf(
|
||||
'%d %d %d %d %d %d %d',
|
||||
'%.3f %.3f %.3f %.3f %.3f %.3f %.3f',
|
||||
$this->runVar['timers']['query']['tmux_time'],
|
||||
$this->runVar['timers']['query']['split_time'],
|
||||
$this->runVar['timers']['query']['init_time'],
|
||||
@@ -465,7 +454,7 @@ class TmuxOutput extends Tmux
|
||||
$this->runVar['timers']['query']['tpg_time']
|
||||
),
|
||||
sprintf(
|
||||
'%d %d %d %d %d %d %d',
|
||||
'%.3f %.3f %.3f %.3f %.3f %.3f %.3f',
|
||||
$this->runVar['timers']['query']['tmux_time'],
|
||||
$this->runVar['timers']['query']['split1_time'],
|
||||
$this->runVar['timers']['query']['init1_time'],
|
||||
|
||||
@@ -4,7 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Services\Tmux\Tmux;
|
||||
use App\Services\Tmux\TmuxMonitorService;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
@@ -47,4 +49,74 @@ class TmuxMonitorServiceTest extends TestCase
|
||||
$this->assertSame('0', $updatedRunVar['counts']['diff']['work_available']);
|
||||
$this->assertSame(2, $updatedRunVar['counts']['now']['total_work']);
|
||||
}
|
||||
|
||||
#[DataProvider('refreshScheduleProvider')]
|
||||
public function test_refresh_schedule(float $lastRefreshAt, int $interval, float $now, bool $expected): void
|
||||
{
|
||||
$reflection = new ReflectionClass(TmuxMonitorService::class);
|
||||
$monitor = $reflection->newInstanceWithoutConstructor();
|
||||
$refreshIsDue = new ReflectionMethod(TmuxMonitorService::class, 'refreshIsDue');
|
||||
|
||||
$this->assertSame($expected, $refreshIsDue->invoke($monitor, $lastRefreshAt, $interval, $now));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array{float, int, float, bool}>
|
||||
*/
|
||||
public static function refreshScheduleProvider(): array
|
||||
{
|
||||
return [
|
||||
'first refresh' => [0.0, 60, 100.0, true],
|
||||
'before interval' => [100.0, 60, 159.999, false],
|
||||
'at interval' => [100.0, 60, 160.0, true],
|
||||
];
|
||||
}
|
||||
|
||||
public function test_collection_table_estimates_do_not_contribute_to_release_total(): void
|
||||
{
|
||||
$reflection = new ReflectionClass(TmuxMonitorService::class);
|
||||
$monitor = $reflection->newInstanceWithoutConstructor();
|
||||
$aggregate = new ReflectionMethod(TmuxMonitorService::class, 'aggregateTableRowEstimates');
|
||||
|
||||
$counts = $aggregate->invoke($monitor, [
|
||||
(object) ['name' => 'collections', 'row_count' => 242650],
|
||||
(object) ['name' => 'binaries', 'row_count' => 120],
|
||||
(object) ['name' => 'multigroup_parts_1', 'row_count' => 80],
|
||||
(object) ['name' => 'missed_parts', 'row_count' => 5],
|
||||
]);
|
||||
|
||||
$this->assertSame([
|
||||
'binaries_table' => 120,
|
||||
'parts_table' => 80,
|
||||
'missed_parts_table' => 5,
|
||||
], $counts);
|
||||
$this->assertArrayNotHasKey('releases', $counts);
|
||||
}
|
||||
|
||||
public function test_connection_counts_are_derived_from_one_socket_snapshot(): void
|
||||
{
|
||||
$reflection = new ReflectionClass(Tmux::class);
|
||||
/** @var Tmux $tmux */
|
||||
$tmux = $reflection->newInstanceWithoutConstructor();
|
||||
$snapshot = implode("\n", [
|
||||
'ESTAB 0 0 10.0.0.2:40000 192.0.2.10:119',
|
||||
'CLOSE-WAIT 0 0 10.0.0.2:40001 192.0.2.10:119',
|
||||
'ESTAB 0 0 10.0.0.2:40002 192.0.2.20:563',
|
||||
]);
|
||||
$connections = [
|
||||
'ip' => '192.0.2.10',
|
||||
'port' => 119,
|
||||
'ip_a' => '192.0.2.20',
|
||||
'port_a' => 563,
|
||||
];
|
||||
|
||||
$this->assertSame(
|
||||
['primary' => ['active' => 1, 'total' => 2]],
|
||||
$tmux->getUSPConnections('primary', $connections, $snapshot),
|
||||
);
|
||||
$this->assertSame(
|
||||
['alternate' => ['active' => 1, 'total' => 1]],
|
||||
$tmux->getUSPConnections('alternate', $connections, $snapshot),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user