diff --git a/deploy/files/etc/sysctl.d/99-left4me.conf b/deploy/files/etc/sysctl.d/99-left4me.conf new file mode 100644 index 0000000..9212bbb --- /dev/null +++ b/deploy/files/etc/sysctl.d/99-left4me.conf @@ -0,0 +1,21 @@ +# Host-side perf baseline for left4me — see +# docs/superpowers/specs/2026-05-09-l4d2-server-host-perf-baseline-design.md +# +# UDP socket buffers: distro defaults of ~128 KiB are too small for sustained +# Source-engine UDP across multiple instances. 8 MiB matches the standard +# 1 Gbit recommendation; rmem_default/wmem_default protect sockets that don't +# explicitly enlarge their buffers. +net.core.rmem_max = 8388608 +net.core.wmem_max = 8388608 +net.core.rmem_default = 524288 +net.core.wmem_default = 524288 + +# Kernel softirq UDP path: the per-CPU backlog queue starts dropping packets +# at the default 1000 under multi-instance burst; 5000 absorbs realistic peaks. +# netdev_budget = 600 gives softirq more drain headroom per pass. +net.core.netdev_max_backlog = 5000 +net.core.netdev_budget = 600 + +# Latency-sensitive default: avoid swap unless the box is really under +# pressure. Harmless on swapless hosts. +vm.swappiness = 10 diff --git a/deploy/tests/test_deploy_artifacts.py b/deploy/tests/test_deploy_artifacts.py index 059b3d3..10efe4d 100644 --- a/deploy/tests/test_deploy_artifacts.py +++ b/deploy/tests/test_deploy_artifacts.py @@ -11,6 +11,7 @@ WEB_UNIT = DEPLOY / "files/usr/local/lib/systemd/system/left4me-web.service" SERVER_UNIT = DEPLOY / "files/usr/local/lib/systemd/system/left4me-server@.service" GAME_SLICE = DEPLOY / "files/usr/local/lib/systemd/system/l4d2-game.slice" BUILD_SLICE = DEPLOY / "files/usr/local/lib/systemd/system/l4d2-build.slice" +SYSCTL_CONF = DEPLOY / "files/etc/sysctl.d/99-left4me.conf" GLOBAL_REFRESH_SERVICE = DEPLOY / "files/usr/local/lib/systemd/system/left4me-refresh-global-overlays.service" GLOBAL_REFRESH_TIMER = DEPLOY / "files/usr/local/lib/systemd/system/left4me-refresh-global-overlays.timer" SANDBOX_UNIT_DIR = DEPLOY / "files/usr/local/lib/systemd/system" @@ -128,6 +129,21 @@ def test_l4d2_build_slice_exists_with_low_weights(): assert "IOWeight=10" in text +def test_sysctl_conf_present_with_perf_settings(): + assert SYSCTL_CONF.is_file() + text = SYSCTL_CONF.read_text() + for line in ( + "net.core.rmem_max = 8388608", + "net.core.wmem_max = 8388608", + "net.core.rmem_default = 524288", + "net.core.wmem_default = 524288", + "net.core.netdev_max_backlog = 5000", + "net.core.netdev_budget = 600", + "vm.swappiness = 10", + ): + assert line in text, f"missing {line!r} in 99-left4me.conf" + + def _fake_command(tmp_path, command_name): marker = tmp_path / f"{command_name}.args" command = tmp_path / command_name