perf(luci): eliminate excessive router CPU load during LuCI session
- Optimize get_ui_state backend by reading /proc natively instead of spawning pidof - Avoid spawning readlink and redundant ubus queries in state.uc - Cache positive tachyon_runtime_network_configured checks for 5s - Back off idle UI state polling interval from 3s to 10s (60s when tab is hidden) - Back off TachyonLogWatcher interval to 30s - Pause Dashboard and Monitoring polling when browser tab is inactive (Page Visibility API) - Fix cleanTabId to ensure inactive tab pollers and timers unmount properly
This commit is contained in:
@@ -5,7 +5,7 @@ export function isActiveLuciTab(tabId: string) {
|
||||
|
||||
return Boolean(
|
||||
document.querySelector(
|
||||
`.cbi-tab[data-tab="${tabId}"]:not(.cbi-tab-disabled)`,
|
||||
`.cbi-tab[data-tab="${tabId}"]:not(.cbi-tab-disabled), .cbi-tab[data-tab="cbi-tachyon-${tabId}"]:not(.cbi-tab-disabled)`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ type CoreServiceOptions = {
|
||||
logWatcherStartDelayMs?: number;
|
||||
};
|
||||
|
||||
const LOG_WATCHER_INTERVAL_MS = 10000;
|
||||
const LOG_WATCHER_INTERVAL_MS = 30000;
|
||||
const LOG_WATCHER_START_DELAY_MS = 5000;
|
||||
|
||||
function componentDisplayName(component: string) {
|
||||
|
||||
@@ -4,12 +4,12 @@ import { logger } from './logger.service';
|
||||
import { applyUiStateToStore } from './uiState.service';
|
||||
|
||||
const RUNTIME_UI_STATE_REFRESH_MIN_INTERVAL_MS = 500;
|
||||
const RUNTIME_UI_STATE_IDLE_POLL_INTERVAL_MS = 3000;
|
||||
const RUNTIME_UI_STATE_IDLE_POLL_INTERVAL_MS = 10_000;
|
||||
const RUNTIME_UI_STATE_ACTIVE_POLL_INTERVAL_MS = 500;
|
||||
// A hidden tab must not keep spawning shell commands on the router every
|
||||
// second: back off to one request per 30s while hidden (a force refresh on
|
||||
// A hidden tab must not keep spawning shell commands on the router:
|
||||
// back off to one request per 60s while hidden (a force refresh on
|
||||
// visibilitychange covers the moment the tab comes back).
|
||||
const RUNTIME_UI_STATE_HIDDEN_POLL_INTERVAL_MS = 30_000;
|
||||
const RUNTIME_UI_STATE_HIDDEN_POLL_INTERVAL_MS = 60_000;
|
||||
type RuntimeUiStateListener = (uiState: Tachyon.UiState) => void;
|
||||
|
||||
let runtimeUiStateRefreshPromise: Promise<Tachyon.UiState | undefined> | null =
|
||||
|
||||
@@ -39,13 +39,18 @@ class TabService {
|
||||
this.notify();
|
||||
}
|
||||
|
||||
private cleanTabId(tab: string | null | undefined): string | null {
|
||||
if (!tab) return null;
|
||||
return tab.replace(/^(?:cbi-tachyon-|tab-)/, '');
|
||||
}
|
||||
|
||||
private getTabsInfo(): TabInfo[] {
|
||||
const tabs = Array.from(
|
||||
document.querySelectorAll<HTMLElement>('.cbi-tab, .cbi-tab-disabled'),
|
||||
);
|
||||
return tabs.map((el) => ({
|
||||
el,
|
||||
id: el.dataset.tab || '',
|
||||
id: this.cleanTabId(el.dataset.tab) || '',
|
||||
active:
|
||||
el.classList.contains('cbi-tab') &&
|
||||
!el.classList.contains('cbi-tab-disabled'),
|
||||
@@ -56,7 +61,7 @@ class TabService {
|
||||
const active = document.querySelector<HTMLElement>(
|
||||
'.cbi-tab:not(.cbi-tab-disabled)',
|
||||
);
|
||||
return active?.dataset.tab || null;
|
||||
return this.cleanTabId(active?.dataset.tab);
|
||||
}
|
||||
|
||||
private notify() {
|
||||
|
||||
@@ -68,7 +68,7 @@ import { shouldShowLoadingForRestoredAction } from '../../helpers/restoredAction
|
||||
import { getServiceAvailability } from '../../helpers/serviceAvailability';
|
||||
import { capSetSize, capMapSize } from '../../helpers/capCollectionSize';
|
||||
|
||||
const SECTIONS_REFRESH_INTERVAL_MS = 10000;
|
||||
const SECTIONS_REFRESH_INTERVAL_MS = 15000;
|
||||
const LATENCY_TEST_BUTTON_CLASS = 'dashboard-sections-grid-item-test-latency';
|
||||
const LATENCY_TEST_BUTTON_LABEL_CLASS =
|
||||
'dashboard-sections-grid-item-test-latency__label';
|
||||
@@ -96,6 +96,8 @@ const customProxyLatencies = new Map<string, number>();
|
||||
const singleTestingOutboundCodes: Record<string, boolean> = {};
|
||||
let isTestingAllSections = false;
|
||||
|
||||
let dashboardVisibilityPaused = false;
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.addEventListener('pagehide', () => {
|
||||
pageUnloading = true;
|
||||
@@ -105,6 +107,25 @@ if (typeof window !== 'undefined') {
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
typeof document !== 'undefined' &&
|
||||
typeof document.addEventListener === 'function'
|
||||
) {
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.hidden) {
|
||||
if (dashboardDataUpdatesStarted) {
|
||||
stopDashboardDataUpdates();
|
||||
dashboardVisibilityPaused = true;
|
||||
}
|
||||
} else if (dashboardVisibilityPaused) {
|
||||
dashboardVisibilityPaused = false;
|
||||
if (dashboardMounted && getDashboardServiceAvailability() !== 'stopped') {
|
||||
startDashboardDataUpdates();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Fetchers
|
||||
|
||||
async function fetchDashboardSectionsOnce(mountId: number) {
|
||||
@@ -640,6 +661,11 @@ function startDashboardDataUpdates() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof document !== 'undefined' && document.hidden) {
|
||||
dashboardVisibilityPaused = true;
|
||||
return;
|
||||
}
|
||||
|
||||
dashboardDataUpdatesStarted = true;
|
||||
const dataUpdatesId = ++dashboardDataUpdatesId;
|
||||
void fetchDashboardSections({ force: true });
|
||||
@@ -2026,8 +2052,12 @@ function renderStoreWidget(
|
||||
}
|
||||
|
||||
async function fetchConnections() {
|
||||
const shouldFetchHostnames = expandedSections.has('active_clients');
|
||||
const needsFallbackPolling = directSocketsFailed || !canUseDirectClashApi();
|
||||
if (!needsFallbackPolling && !shouldFetchHostnames) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const shouldFetchHostnames = expandedSections.has('active_clients');
|
||||
const [res, hostnames] = await Promise.all([
|
||||
TachyonShellMethods.getClashApiConnections(),
|
||||
shouldFetchHostnames
|
||||
@@ -2037,7 +2067,7 @@ async function fetchConnections() {
|
||||
if (res.success && res.data && typeof res.data === 'object') {
|
||||
const payload = res.data as any;
|
||||
|
||||
if (directSocketsFailed || !canUseDirectClashApi()) {
|
||||
if (needsFallbackPolling) {
|
||||
const downloadTotal = Number(payload.downloadTotal) || 0;
|
||||
const uploadTotal = Number(payload.uploadTotal) || 0;
|
||||
const memory = Number(payload.memory) || 0;
|
||||
@@ -2391,6 +2421,7 @@ async function onPageMount() {
|
||||
function onPageUnmount() {
|
||||
dashboardMounted = false;
|
||||
dashboardMountId += 1;
|
||||
dashboardVisibilityPaused = false;
|
||||
|
||||
directSocketsFailed = false;
|
||||
lastTrafficPollTime = 0;
|
||||
|
||||
@@ -71,7 +71,7 @@ function normalizeConnectionsPayload(value: unknown): ClashConnectionsPayload {
|
||||
}
|
||||
|
||||
const RENDER_INTERVAL_MS = 500;
|
||||
const CONNECTIONS_RPC_POLL_INTERVAL_MS = 1500;
|
||||
const CONNECTIONS_RPC_POLL_INTERVAL_MS = 3000;
|
||||
const CLOSED_CONNECTION_LIMIT = 300;
|
||||
const ALL_FILTER_VALUE = 'all';
|
||||
|
||||
@@ -1826,9 +1826,51 @@ async function onPageMount() {
|
||||
}, RENDER_INTERVAL_MS);
|
||||
}
|
||||
|
||||
let monitoringVisibilityPaused = false;
|
||||
|
||||
function pauseMonitoringUpdates() {
|
||||
if (!monitoringMounted || monitoringVisibilityPaused) return;
|
||||
monitoringVisibilityPaused = true;
|
||||
if (renderTimer) {
|
||||
clearInterval(renderTimer);
|
||||
renderTimer = null;
|
||||
}
|
||||
stopConnectionsUpdates();
|
||||
}
|
||||
|
||||
function resumeMonitoringUpdates() {
|
||||
if (!monitoringMounted || !monitoringVisibilityPaused) return;
|
||||
monitoringVisibilityPaused = false;
|
||||
if (serviceAvailability === 'running') {
|
||||
startConnectionsUpdates();
|
||||
}
|
||||
if (!renderTimer) {
|
||||
renderTimer = setInterval(() => {
|
||||
if (monitoringPaused) {
|
||||
return;
|
||||
}
|
||||
renderConnections();
|
||||
}, RENDER_INTERVAL_MS);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
typeof document !== 'undefined' &&
|
||||
typeof document.addEventListener === 'function'
|
||||
) {
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.hidden) {
|
||||
pauseMonitoringUpdates();
|
||||
} else {
|
||||
resumeMonitoringUpdates();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onPageUnmount() {
|
||||
monitoringMounted = false;
|
||||
monitoringMountId += 1;
|
||||
monitoringVisibilityPaused = false;
|
||||
|
||||
if (renderTimer) {
|
||||
clearInterval(renderTimer);
|
||||
|
||||
Reference in New Issue
Block a user