fix: resolve ERR_INSUFFICIENT_RESOURCES connection exhaustion
- Add concurrency limiter (max 4 GET requests) to API layer, leaving slots for SSE and health checks. Write ops bypass the limiter. - Add AbortController to ContainerStats, project detail page, and dashboard to cancel in-flight requests on navigation/unmount. - Move global SSE connection from layout to events page (only consumer). - Add 30s heartbeat to SSE endpoint to detect zombie connections. - Serialize dashboard project fetches to avoid parallel burst. - Rebuild frontend in dev-server.sh so go:embed stays in sync.
This commit is contained in:
@@ -8,12 +8,9 @@
|
||||
import LocaleSwitcher from '$lib/components/LocaleSwitcher.svelte';
|
||||
import { IconDashboard, IconProjects, IconDeploy, IconEvents, IconWifi, IconSettings, IconMenu, IconX, IconLogout, IconGlobe } from '$lib/components/icons';
|
||||
import { goto } from '$app/navigation';
|
||||
import { connectGlobalEvents, type SSEConnection } from '$lib/sse';
|
||||
import { instanceStatusStore } from '$lib/stores/instance-status';
|
||||
import { resolvedTheme, applyTheme } from '$lib/stores/theme';
|
||||
import { exchangeOidcToken, setAuthToken, clearAuth, isAuthenticated } from '$lib/auth';
|
||||
import { logout as apiLogout, getHealth } from '$lib/api';
|
||||
import { publishEventLog } from '$lib/stores/event-log-bus';
|
||||
import type { DockerHealth, ProxyHealth } from '$lib/types';
|
||||
import { t } from '$lib/i18n';
|
||||
|
||||
@@ -38,7 +35,6 @@
|
||||
return pathname.startsWith(href);
|
||||
}
|
||||
|
||||
let sseConnection: SSEConnection | null = null;
|
||||
let sidebarOpen = $state(false);
|
||||
let dockerHealth = $state<DockerHealth | null>(null);
|
||||
let proxyHealth = $state<ProxyHealth | null>(null);
|
||||
@@ -85,26 +81,13 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Start SSE and health polling when authenticated.
|
||||
// Start health polling when authenticated.
|
||||
// Uses $effect to react to route changes (e.g., after login navigation).
|
||||
$effect(() => {
|
||||
void $page.url.pathname;
|
||||
|
||||
if (!isAuthenticated() || sseConnection) return;
|
||||
if (!isAuthenticated() || healthInterval) return;
|
||||
|
||||
sseConnection = connectGlobalEvents({
|
||||
onInstanceStatus(payload) {
|
||||
instanceStatusStore.update(payload);
|
||||
},
|
||||
onDeployStatus(payload) {
|
||||
instanceStatusStore.notifyDeploy(payload);
|
||||
},
|
||||
onEventLog(payload) {
|
||||
publishEventLog(payload);
|
||||
}
|
||||
});
|
||||
|
||||
// Poll Docker health every 30s.
|
||||
async function checkHealth() {
|
||||
try {
|
||||
const h = await getHealth();
|
||||
@@ -121,8 +104,6 @@
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
sseConnection?.close();
|
||||
sseConnection = null;
|
||||
if (healthInterval) clearInterval(healthInterval);
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user