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
This commit is contained in:
@@ -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.');
|
||||
|
||||
Reference in New Issue
Block a user