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:
Dushnilin
2026-09-12 21:17:20 +03:00
parent e834971919
commit dceb04677e
9 changed files with 221 additions and 33 deletions
+32 -9
View File
@@ -432,10 +432,24 @@ function pid_is_sing_box(pid) {
if (match(pid, /^[0-9]+$/) == null)
return false;
let exe = fs.readlink("/proc/" + pid + "/exe");
if (exe != null && exe != "")
return path_basename(exe) == "sing-box";
let comm = fs.readfile("/proc/" + pid + "/comm");
if (comm != null)
return trim(comm) == "sing-box";
return path_basename(command_trimmed_output_from_args([ "readlink", "/proc/" + pid + "/exe" ])) == "sing-box";
}
function sing_box_service_pid_runtime() {
for (let path in [ "/var/run/sing-box.pid", "/var/run/sing-box/sing-box.pid" ]) {
let pid = int(trim(fs.readfile(path) || "0"));
if (pid > 0 && pid_is_sing_box(pid))
return pid;
}
let data = command_output_from_args([ "ubus", "call", "service", "list", "{\"name\":\"sing-box\"}" ]);
try {
let pid = sing_box_service_pid_from_value(json(data));
@@ -444,14 +458,7 @@ function sing_box_service_pid_runtime() {
}
catch (e) {
// ubus is unavailable or answered with something that is not the
// service list; the pid files below are the fallback that exists for
// exactly this case.
}
for (let path in [ "/var/run/sing-box.pid", "/var/run/sing-box/sing-box.pid" ]) {
let pid = int(trim(fs.readfile(path) || "0"));
if (pid > 0 && pid_is_sing_box(pid))
return pid;
// service list; the fallback below handles this case.
}
let pids = split(trim(command_trimmed_output_from_args([ "pidof", "sing-box" ])), /[ \t\r\n]+/);
@@ -611,9 +618,25 @@ function sing_box_service_stable(min_age) {
return age != null && age >= min_age;
}
let network_check_cache = {
key: "",
checked_at: 0,
result: false
};
function tachyon_runtime_network_configured(rt_table, nft_table, mark) {
return command_success_from_args([ "nft", "list", "table", "inet", nft_table ]) &&
let key = as_string(rt_table) + ":" + as_string(nft_table) + ":" + as_string(mark);
let now = int(clock()[0]);
if (network_check_cache.key == key && network_check_cache.result && (now - network_check_cache.checked_at) < 5)
return true;
let result = command_success_from_args([ "nft", "list", "table", "inet", nft_table ]) &&
command_success_from_args([ "ucode", "-L", LIB_DIR, LIB_DIR + "/nft/apply.uc", "tproxy-route-rule-present", rt_table, mark ]);
network_check_cache.key = key;
network_check_cache.checked_at = now;
network_check_cache.result = result;
return result;
}
function tachyon_running(rt_table, nft_table, mark) {
+25 -2
View File
@@ -1049,9 +1049,32 @@ function ui_capabilities_json() {
write_json(capability_flags());
}
function process_memory_rss_mb(process_name) {
function get_process_pids(process_name) {
let pids = [];
let d = fs.opendir("/proc");
if (d) {
let entry;
while ((entry = d.read()) != null) {
if (match(entry, /^[0-9]+$/) != null) {
let comm = fs.readfile("/proc/" + entry + "/comm");
if (comm && trim(comm) == process_name)
push(pids, entry);
}
}
d.close();
return pids;
}
let output = common.command_output_from_args([ "pidof", process_name ]);
let pids = split(trim(as_string(output)), /[ \t\r\n]+/);
for (let p in split(trim(as_string(output)), /[ \t\r\n]+/)) {
let pid = trim(as_string(p));
if (pid && match(pid, /^[0-9]+$/) != null)
push(pids, pid);
}
return pids;
}
function process_memory_rss_mb(process_name) {
let pids = get_process_pids(process_name);
let total_kb = 0;
for (let p in pids) {
let pid = trim(as_string(p));