fix(stability): optimize Go memory tiers, prevent nftables degradation deadlock, harden NTP & process supervision

- singbox/runtime.uc: add dynamic 4-tier GOMEMLIMIT and GOGC limits based on RAM, enforce /etc/tachyon/mem_scale, and install managed init script across all binary variants
- service/event_controller.uc: guard probe_nftables() with recovery_bypass and native_internet_restored checks to prevent 5-minute network drop deadlock
- service/watchdog.uc: reduce Telegram worker heartbeat timeout from 900s to 300s and add nslookup fallback to Zero-RTT prefetcher
- diagnostics/runtime.uc & service/lifecycle.uc: use direct-IP NTP pool to eliminate circular DNS/TLS deadlocks, and step clock forward on cold boot if RTC desynced
- service/failover.uc & diagnostics/runtime.uc: flush conntrack states on proxy group switch so client connections immediately migrate
This commit is contained in:
Dushnilin
2026-09-03 06:13:30 +03:00
parent ea85164646
commit b3358f77a3
6 changed files with 44 additions and 11 deletions
+3 -1
View File
@@ -1864,6 +1864,8 @@ function clash_api(action, arg1, arg2, arg3) {
push(args, "--data-raw");
push(args, payload);
let result = status_capture([ "clash-set-group-proxy-result", arg1, arg2 ], command_output(command_from_args(args)));
if (result.status == 0)
command_status("conntrack -F >/dev/null 2>&1 || true");
if (result.output != "")
print(result.output);
return result.status;
@@ -3930,7 +3932,7 @@ function apply_quick_fix(codes_str) {
status = (rc == 0);
msg = status ? "Tachyon stopped, stock dnsmasq and native direct internet routing restored" : "Native internet restore failed (exit " + rc + ")";
} else if (c == "fix_system_time") {
let rc = command_status("ntpd -q -p pool.ntp.org 2>/dev/null || rdate -s time.cloudflare.com 2>/dev/null; /etc/init.d/sysntpd restart 2>/dev/null");
let rc = command_status("ntpd -q -p 194.190.168.1 -p 216.239.35.0 -p 162.159.200.1 2>/dev/null || ntpd -q -p pool.ntp.org 2>/dev/null || rdate -s time.cloudflare.com 2>/dev/null; /etc/init.d/sysntpd restart 2>/dev/null");
status = (rc == 0);
msg = status ? "System time synchronized with NTP" : "NTP sync failed (exit " + rc + ")";
} else if (c == "flush_conntrack") {
@@ -642,6 +642,8 @@ function controller(bus, opts) {
// ── Probe: nftables table and QoS marks ───────────────────────────────────
function probe_nftables() {
if (setting("recovery_bypass", "0") == "1") return;
if (fs.stat("/var/run/tachyon/native_internet_restored") != null) return;
if (is_list_update_running()) return;
if (is_reload_in_progress()) return;
@@ -666,6 +668,7 @@ function controller(bus, opts) {
// ── Probe: TPROXY port liveness ───────────────────────────────────────────
function probe_tproxy() {
if (setting("recovery_bypass", "0") == "1") return;
if (fs.stat("/var/run/tachyon/native_internet_restored") != null) return;
if (is_reload_in_progress()) return;
let pid = get_sing_box_pid();
@@ -121,6 +121,7 @@ function switch_group(section_name, outbound_name) {
log_message("runtime switch failed: " + out, "warn");
return false;
}
command_status("conntrack -F >/dev/null 2>&1 || true");
return true;
}
@@ -835,6 +835,11 @@ function start_impl() {
if (wd_status != 0)
log_message("Watchdog start-runtime failed (status " + as_string(wd_status) + ")", "warn");
if (time() < 1704067200) {
log_message("System clock is in the past (RTC desync); performing quick NTP synchronization...", "warn");
command_status("ntpd -q -p 194.190.168.1 -p 216.239.35.0 -p 162.159.200.1 >/dev/null 2>&1 || true");
}
let status = start_main();
if (status != 0)
return status;
+2 -2
View File
@@ -349,7 +349,7 @@ function run_zero_rtt_prefetching() {
// that followed it attached to a null command in this shell instead
// of to the backgrounded loop. Backgrounding is now the wrapper's
// job, so the loop gets the redirections it was always meant to have.
let batch_cmd = "for d in " + join(" ", batch) + "; do dig @127.0.0.1 \"$d\" A >/dev/null 2>&1; done";
let batch_cmd = "for d in " + join(" ", batch) + "; do dig @127.0.0.1 \"$d\" A >/dev/null 2>&1 || nslookup \"$d\" 127.0.0.1 >/dev/null 2>&1; done";
system(common.background_command(batch_cmd));
batch = [];
}
@@ -1976,7 +1976,7 @@ function check_telegram_worker() {
}
let age = time() - int(heartbeat);
if (age <= 900)
if (age <= 300)
return;
let pid = trim(fs.readfile(TELEGRAM_PID_FILE) || "");
+30 -8
View File
@@ -369,7 +369,7 @@ function owner_pid() {
function managed_service_installed() {
let data = fs.readfile("/etc/init.d/sing-box");
return data != null && index(as_string(data), SB_MANAGED_SERVICE_MARKER) >= 0;
return data != null && index(as_string(data), SB_MANAGED_SERVICE_MARKER) >= 0 && index(as_string(data), "GOGC") >= 0;
}
function managed_service_text() {
@@ -381,7 +381,7 @@ function managed_service_text() {
"start_service() {\n" +
" config_load \"sing-box\"\n" +
" local enabled config_file working_directory\n" +
" local log_stderr mem_total_kb mem_limit_mb\n\n" +
" local log_stderr mem_total_kb mem_limit_mb gogc scale scale_pct\n\n" +
" config_get_bool enabled \"main\" \"enabled\" \"0\"\n" +
" [ \"$enabled\" -eq \"1\" ] || return 0\n\n" +
" config_get config_file \"main\" \"conffile\" \"/etc/sing-box/config.json\"\n" +
@@ -394,10 +394,32 @@ function managed_service_text() {
" procd_set_param limits core=\"unlimited\"\n" +
" procd_set_param limits nofile=\"1000000 1000000\"\n" +
" mem_total_kb=\"$(awk '/MemTotal/ {print $2}' /proc/meminfo 2>/dev/null || echo 0)\"\n" +
" if [ \"$mem_total_kb\" -gt 0 ] && [ \"$mem_total_kb\" -lt 262144 ]; then\n" +
" mem_limit_mb=$(( mem_total_kb / 1024 * 35 / 100 ))\n" +
" [ \"$mem_limit_mb\" -lt 24 ] && mem_limit_mb=24\n" +
" procd_set_param env GOMEMLIMIT=\"${mem_limit_mb}MiB\"\n" +
" if [ \"$mem_total_kb\" -gt 0 ]; then\n" +
" if [ \"$mem_total_kb\" -lt 131072 ]; then\n" +
" mem_limit_mb=$(( mem_total_kb / 1024 * 30 / 100 ))\n" +
" [ \"$mem_limit_mb\" -lt 16 ] && mem_limit_mb=16\n" +
" gogc=20\n" +
" elif [ \"$mem_total_kb\" -lt 262144 ]; then\n" +
" mem_limit_mb=$(( mem_total_kb / 1024 * 35 / 100 ))\n" +
" [ \"$mem_limit_mb\" -lt 24 ] && mem_limit_mb=24\n" +
" gogc=25\n" +
" elif [ \"$mem_total_kb\" -lt 524288 ]; then\n" +
" mem_limit_mb=$(( mem_total_kb / 1024 * 40 / 100 ))\n" +
" gogc=30\n" +
" else\n" +
" mem_limit_mb=$(( mem_total_kb / 1024 * 30 / 100 ))\n" +
" [ \"$mem_limit_mb\" -gt 256 ] && mem_limit_mb=256\n" +
" gogc=40\n" +
" fi\n" +
" if [ -r \"/etc/tachyon/mem_scale\" ]; then\n" +
" scale=\"$(cat /etc/tachyon/mem_scale 2>/dev/null)\"\n" +
" scale_pct=\"$(awk -v s=\"$scale\" 'BEGIN { pct=int(s*100); if (pct < 20) pct=20; if (pct > 100) pct=100; print pct }')\"\n" +
" if [ -n \"$scale_pct\" ] && [ \"$scale_pct\" -lt 100 ]; then\n" +
" mem_limit_mb=$(( mem_limit_mb * scale_pct / 100 ))\n" +
" [ \"$mem_limit_mb\" -lt 12 ] && mem_limit_mb=12\n" +
" fi\n" +
" fi\n" +
" procd_set_param env GOMEMLIMIT=\"${mem_limit_mb}MiB\" GOGC=\"$gogc\"\n" +
" fi\n" +
" procd_set_param term_timeout 15\n" +
" procd_set_param respawn\n" +
@@ -450,8 +472,8 @@ function prepare_service_disabled() {
function configure_service() {
let settings = uci_settings();
if (sing_box_compressed_marker_set() && !install_managed_service_script()) {
log_message("Failed to install managed sing-box service for compressed binary. Aborted.", "fatal");
if (!managed_service_installed() && !install_managed_service_script()) {
log_message("Failed to install managed sing-box service. Aborted.", "fatal");
exit(1);
}