From 3fa4c9d65e2969daa28a9bab54ed04fcd492814d Mon Sep 17 00:00:00 2001 From: "Joseph.Rawlings" Date: Sun, 1 Mar 2026 05:38:16 -0500 Subject: [PATCH] perf: optimize statistics exports - cache task history and slim down Livewire payload - Cache getTaskHistoryProperty() with 30s TTL to avoid Proxmox API calls on every render - Move networkAnalysis heavy arrays (connections, topConnections, topPorts) out of Livewire public property into server-side cache only - Store only counts and metadata in the Livewire payload instead of thousands of connection objects - Pagination and export methods now read from cache instead of public property - Reduce wire:poll intervals from 10s to 30s on task history and lifecycle events cards - Dramatically reduces JSON payload size on every Livewire request --- .../Servers/Proxmox/Livewire/Statistics.php | 108 ++++++++++++------ .../Proxmox/views/statistics.blade.php | 22 ++-- 2 files changed, 83 insertions(+), 47 deletions(-) diff --git a/extensions/Servers/Proxmox/Livewire/Statistics.php b/extensions/Servers/Proxmox/Livewire/Statistics.php index 4f1d71112..ae9fa007f 100644 --- a/extensions/Servers/Proxmox/Livewire/Statistics.php +++ b/extensions/Servers/Proxmox/Livewire/Statistics.php @@ -71,9 +71,9 @@ class Statistics extends Component public int $lifecycleEventsCurrentPage = 1; public array $networkAnalysis = [ - 'connections' => [], - 'topConnections' => [], - 'topPorts' => [], + 'connectionCount' => 0, + 'topConnectionCount' => 0, + 'topPortCount' => 0, 'security' => [], 'lastUpdated' => null, 'error' => null, @@ -157,9 +157,9 @@ class Statistics extends Component // Initialize error state if not configured to prevent infinite loading if (!$this->coreRouterEnabled()) { $this->networkAnalysis['error'] = 'Core Router API is not configured.'; - $this->networkAnalysis['connections'] = []; - $this->networkAnalysis['topConnections'] = []; - $this->networkAnalysis['topPorts'] = []; + $this->networkAnalysis['connectionCount'] = 0; + $this->networkAnalysis['topConnectionCount'] = 0; + $this->networkAnalysis['topPortCount'] = 0; $this->networkAnalysis['security'] = []; } @@ -244,7 +244,15 @@ class Statistics extends Component 'last_updated' => $cachedAnalysis['lastUpdated'] ?? null, ]); - $this->networkAnalysis = $cachedAnalysis; + // Only store lightweight metadata in the public property (not the heavy arrays) + $this->networkAnalysis = [ + 'connectionCount' => count($cachedAnalysis['connections'] ?? []), + 'topConnectionCount' => count($cachedAnalysis['topConnections'] ?? []), + 'topPortCount' => count($cachedAnalysis['topPorts'] ?? []), + 'security' => $cachedAnalysis['security'] ?? [], + 'lastUpdated' => $cachedAnalysis['lastUpdated'], + 'error' => $cachedAnalysis['error'] ?? null, + ]; $this->networkAnalysisLoading = false; // Reset retry count when we successfully hydrate from cache @@ -262,9 +270,9 @@ class Statistics extends Component if (! $this->coreRouterEnabled()) { $this->networkAnalysis['error'] = 'Core Router API is not configured.'; - $this->networkAnalysis['connections'] = []; - $this->networkAnalysis['topConnections'] = []; - $this->networkAnalysis['topPorts'] = []; + $this->networkAnalysis['connectionCount'] = 0; + $this->networkAnalysis['topConnectionCount'] = 0; + $this->networkAnalysis['topPortCount'] = 0; $this->networkAnalysis['security'] = []; $this->networkAnalysisLoading = false; @@ -274,9 +282,9 @@ class Statistics extends Component $serverIps = $this->getServerIps(); if (empty($serverIps)) { $this->networkAnalysis['error'] = 'No IP addresses are associated with this server.'; - $this->networkAnalysis['connections'] = []; - $this->networkAnalysis['topConnections'] = []; - $this->networkAnalysis['topPorts'] = []; + $this->networkAnalysis['connectionCount'] = 0; + $this->networkAnalysis['topConnectionCount'] = 0; + $this->networkAnalysis['topPortCount'] = 0; $this->networkAnalysis['security'] = []; $this->networkAnalysisLoading = false; @@ -400,7 +408,8 @@ class Statistics extends Component 'sample_connections' => array_slice($connections, 0, 3), ]); - // Store all connections without limit - pagination handles display + // Store all connections in cache only — NOT in the public property + // This prevents thousands of connections from being serialized in every Livewire request $analysis = [ 'connections' => $connections, 'topConnections' => $topConnections, @@ -410,16 +419,24 @@ class Statistics extends Component 'error' => null, ]; - $this->networkAnalysis = $analysis; + // Persist the full analysis in cache for pagination and export + Cache::put($cacheKey, $analysis, now()->addSeconds(30)); + + // Only store lightweight metadata in the Livewire public property + $this->networkAnalysis = [ + 'connectionCount' => count($connections), + 'topConnectionCount' => count($topConnections), + 'topPortCount' => count($topPorts), + 'security' => $securityWarnings, + 'lastUpdated' => now()->toDateTimeString(), + 'error' => null, + ]; // Reset pagination to page 1 when data is refreshed $this->connectionsCurrentPage = 1; $this->topConnectionsCurrentPage = 1; $this->topPortsCurrentPage = 1; - // Persist the computed analysis for a short period to speed up subsequent loads - Cache::put($cacheKey, $analysis, now()->addSeconds(30)); - // Reset retry count on successful load $this->networkAnalysisRetryCount = 0; } catch (\Throwable $e) { @@ -428,10 +445,10 @@ class Statistics extends Component $errorMessage = $e->getMessage(); $is502Error = str_contains($errorMessage, 'HTTP 502') || str_contains($errorMessage, '(HTTP 502)'); - // Always initialize error arrays first - $this->networkAnalysis['connections'] = []; - $this->networkAnalysis['topConnections'] = []; - $this->networkAnalysis['topPorts'] = []; + // Reset counts and security on error + $this->networkAnalysis['connectionCount'] = 0; + $this->networkAnalysis['topConnectionCount'] = 0; + $this->networkAnalysis['topPortCount'] = 0; $this->networkAnalysis['security'] = []; // On failure, do not leave any stale cache behind for this server @@ -485,8 +502,9 @@ class Statistics extends Component */ public function getPaginatedConnectionsProperty(): array { - $connections = $this->networkAnalysis['connections'] ?? []; - $total = count($connections); + $cacheKey = 'proxmox_network_analysis_' . $this->server->id; + $cachedAnalysis = Cache::get($cacheKey, []); + $connections = $cachedAnalysis['connections'] ?? []; $offset = ($this->connectionsCurrentPage - 1) * $this->connectionsPerPage; return array_slice($connections, $offset, $this->connectionsPerPage); @@ -497,7 +515,7 @@ class Statistics extends Component */ public function getConnectionsTotalPagesProperty(): int { - $total = count($this->networkAnalysis['connections'] ?? []); + $total = $this->networkAnalysis['connectionCount'] ?? 0; return max(1, (int) ceil($total / $this->connectionsPerPage)); } @@ -506,7 +524,9 @@ class Statistics extends Component */ public function getPaginatedTopConnectionsProperty(): array { - $connections = $this->networkAnalysis['topConnections'] ?? []; + $cacheKey = 'proxmox_network_analysis_' . $this->server->id; + $cachedAnalysis = Cache::get($cacheKey, []); + $connections = $cachedAnalysis['topConnections'] ?? []; $offset = ($this->topConnectionsCurrentPage - 1) * $this->topConnectionsPerPage; return array_slice($connections, $offset, $this->topConnectionsPerPage); @@ -517,7 +537,7 @@ class Statistics extends Component */ public function getTopConnectionsTotalPagesProperty(): int { - $total = count($this->networkAnalysis['topConnections'] ?? []); + $total = $this->networkAnalysis['topConnectionCount'] ?? 0; return max(1, (int) ceil($total / $this->topConnectionsPerPage)); } @@ -526,7 +546,9 @@ class Statistics extends Component */ public function getPaginatedTopPortsProperty(): array { - $ports = $this->networkAnalysis['topPorts'] ?? []; + $cacheKey = 'proxmox_network_analysis_' . $this->server->id; + $cachedAnalysis = Cache::get($cacheKey, []); + $ports = $cachedAnalysis['topPorts'] ?? []; $offset = ($this->topPortsCurrentPage - 1) * $this->topPortsPerPage; return array_slice($ports, $offset, $this->topPortsPerPage); @@ -537,7 +559,7 @@ class Statistics extends Component */ public function getTopPortsTotalPagesProperty(): int { - $total = count($this->networkAnalysis['topPorts'] ?? []); + $total = $this->networkAnalysis['topPortCount'] ?? 0; return max(1, (int) ceil($total / $this->topPortsPerPage)); } @@ -1363,8 +1385,25 @@ class Statistics extends Component public function getTaskHistoryProperty() { + $cacheKey = 'proxmox_task_history_' . $this->server->id; + try { - $proxmox = new Proxmox; + return Cache::remember($cacheKey, 30, function () { + return $this->fetchTaskHistoryFromApi(); + }); + } catch (\Throwable $e) { + \Log::error('Error fetching task history for server ' . $this->server->id . ': ' . $e->getMessage()); + return []; + } + } + + /** + * Fetch task history directly from the Proxmox API. + * Separated from the computed property so Cache::remember can wrap it. + */ + protected function fetchTaskHistoryFromApi(): array + { + $proxmox = new Proxmox; $vmId = (string)$this->server->vm_id; $allTasks = []; @@ -1528,11 +1567,6 @@ class Statistics extends Component // Limit to last 100 tasks return array_slice($tasks, 0, 100); - } catch (\Throwable $e) { - // Log error for debugging but return empty array - \Log::error('Error fetching task history for server ' . $this->server->id . ': ' . $e->getMessage()); - return []; - } } public function getTaskDetails($taskId) @@ -2071,7 +2105,9 @@ class Statistics extends Component public function exportNetworkAnalysis() { - $connections = $this->networkAnalysis['connections'] ?? []; + $cacheKey = 'proxmox_network_analysis_' . $this->server->id; + $cachedAnalysis = Cache::get($cacheKey, []); + $connections = $cachedAnalysis['connections'] ?? []; if (empty($connections)) { $this->showExportInfo('No network analysis data available to export. Please refresh the network analysis first.'); diff --git a/extensions/Servers/Proxmox/views/statistics.blade.php b/extensions/Servers/Proxmox/views/statistics.blade.php index 18aa62fa3..07a2cc4f4 100644 --- a/extensions/Servers/Proxmox/views/statistics.blade.php +++ b/extensions/Servers/Proxmox/views/statistics.blade.php @@ -1081,7 +1081,7 @@ @php $taskHistoryExportRows = []; @endphp -
+

@@ -1576,7 +1576,7 @@ @php $lifecycleEventsExportRows = []; @endphp -
+

@@ -2462,7 +2462,7 @@ @else
- @if(empty($networkAnalysis['connections'])) + @if(($networkAnalysis['connectionCount'] ?? 0) == 0)
No matching connections tracked for this server.
@else
@@ -2928,7 +2928,7 @@
{{-- Connections Pagination Controls --}} - @if(count($networkAnalysis['connections']) > 0) + @if(($networkAnalysis['connectionCount'] ?? 0) > 0)
{{-- Records per page selector --}}
@@ -2946,7 +2946,7 @@ {{-- Page info and navigation --}}
- Showing {{ (($connectionsCurrentPage - 1) * $connectionsPerPage) + 1 }} - {{ min($connectionsCurrentPage * $connectionsPerPage, count($networkAnalysis['connections'])) }} of {{ count($networkAnalysis['connections']) }} connections + Showing {{ (($connectionsCurrentPage - 1) * $connectionsPerPage) + 1 }} - {{ min($connectionsCurrentPage * $connectionsPerPage, $networkAnalysis['connectionCount'] ?? 0) }} of {{ $networkAnalysis['connectionCount'] ?? 0 }} connections
@@ -3023,7 +3023,7 @@
- @if(empty($networkAnalysis['topConnections'])) + @if(($networkAnalysis['topConnectionCount'] ?? 0) == 0)
No connection data available.
@else
@@ -3251,7 +3251,7 @@
{{-- Top Connections Pagination Controls --}} - @if(count($networkAnalysis['topConnections']) > 0) + @if(($networkAnalysis['topConnectionCount'] ?? 0) > 0)
{{-- Records per page selector --}}
@@ -3269,7 +3269,7 @@ {{-- Page info and navigation --}}
- Showing {{ (($topConnectionsCurrentPage - 1) * $topConnectionsPerPage) + 1 }} - {{ min($topConnectionsCurrentPage * $topConnectionsPerPage, count($networkAnalysis['topConnections'])) }} of {{ count($networkAnalysis['topConnections']) }} peers + Showing {{ (($topConnectionsCurrentPage - 1) * $topConnectionsPerPage) + 1 }} - {{ min($topConnectionsCurrentPage * $topConnectionsPerPage, $networkAnalysis['topConnectionCount'] ?? 0) }} of {{ $networkAnalysis['topConnectionCount'] ?? 0 }} peers
@@ -3346,7 +3346,7 @@
- @if(empty($networkAnalysis['topPorts'])) + @if(($networkAnalysis['topPortCount'] ?? 0) == 0)
No port statistics are available.
@else
@@ -3464,7 +3464,7 @@
{{-- Top Ports Pagination Controls --}} - @if(count($networkAnalysis['topPorts']) > 0) + @if(($networkAnalysis['topPortCount'] ?? 0) > 0)
{{-- Records per page selector --}}
@@ -3482,7 +3482,7 @@ {{-- Page info and navigation --}}
- Showing {{ (($topPortsCurrentPage - 1) * $topPortsPerPage) + 1 }} - {{ min($topPortsCurrentPage * $topPortsPerPage, count($networkAnalysis['topPorts'])) }} of {{ count($networkAnalysis['topPorts']) }} ports + Showing {{ (($topPortsCurrentPage - 1) * $topPortsPerPage) + 1 }} - {{ min($topPortsCurrentPage * $topPortsPerPage, $networkAnalysis['topPortCount'] ?? 0) }} of {{ $networkAnalysis['topPortCount'] ?? 0 }} ports