Chapter 31 / 51
Volume 4 · Hot Reload Deep Dive (finale)

Boundaries & anti-patterns: hot reload is not a universal hammer

Knowing "when not to use it" matters as much as knowing "how". Volume 4 closes: every boundary collected into one table, then a real attack sample tests the system's resilience.

Overview

Positioning: volume 4's "negative list". The anti-patterns all come from real scenes — drill's attack samples, func_test's storm cases, and the common temptation to mistake hot reload for a deployment tool.

31.1 Intro

Hot reload solves the online update of business code. It does not solve: process-structure changes (endpoints/threading model), the rebuilding of non-script resources, and it doesn't suit high-frequency releases. The predicate is simple — if the change involves only "script content + certificates + same-endpoint config", reload; everything else restarts. Carving this boundary into your ops manual beats memorizing ten details.

31.2 The immutable-items summary

Changereload outcomeRight path
engine.workersFAILED (rejected)restart the process
immutable listener fields (recv_limit etc.)FAILED (explicit rejection)restart the process
endpoint structure (class/ip/port combos that can't hand off)FAILED (degradation suggested)restart; or split into two servers and roll
scripts of custom-class servicesonline reload unsupportedrestart (raw resources carry no terminal lease)
adding / removing custom serversunsupportedrestart
script content / certificates / same-endpoint configSUCCEEDEDreload (that's exactly its job)

Note custom's position: it hands connections and resources entirely to the application, so the host has no "when is it safe" signal — hence the honest "unsupported" instead of an unsafe switch. Business that needs hot updates belongs on the http/ws/tcp/udp classes.

31.3 Reload bombs & resilience

drill's attack sample list has one aimed squarely at hot reload: the bad-script reload bomb — cyclically submitting uncompilable scripts (the sample fires 6 in a row), paired with func_test's 20-shot storm case, verifying three resilience points:

reload bomb (bad scripts ×N in a burst) ├── each intent: FAILED + Message naming the compile error /* rejections are clean */ ├── the old generation serves untouched throughout /* rejection never harms the live service */ └── latest-wins absorbs the concurrent storm /* queues don't pile up */ companion protections: pending ≤64 (Submit returns 0 when full) result ring 256 (audits don't grow unbounded) a good script submitted right after the bad ones → one success restores service

The sample's point: the reload surface is an attack surface — the management-route auth (ch. 27) blocks external triggering, while the mechanism itself guarantees even an insider's slip can only buy a clean rejection with bad input.

31.4 The anti-pattern collection

Anti-patternSymptomRight solution
Reloading every second (as a deployment system)compile costs pile up; GC queue growsa reload is a change, not a release; iterate with dev-environment restarts
Stuffing a whole database into Swapswaps slow down; snapshots bloatbig data lives in db/files (naturally cross-generation); Swap carries light state only
Sharing raw pointers across generations "for convenience"sporadic UAFhand over values (Swap) or global-singleton resources with explicit lifetimes
Using reload to cure "weird memory states"symptom palliation; the problem returnsfix the cause; reload shouldn't be a magic reset button (even though it often "works")
Auto file-triggering in productioneditor autosave = accidental deploymentauto-trigger is dev-only; production goes through authenticated management routes

31.5 Contract

  • Boundary: scripts/certificates/same-endpoint config → reload; structure/threads/custom → restart.
  • Resilience: bad input = clean rejection + intact old generation + bounded queues.
  • Discipline: keep reload frequency and Swap size "human-scale" (minute-level changes, KB-level snapshots).

Common mistakes

Trying to change a port via reload
Symptom
FAILED; Message points at a structural change.
Cause
Endpoint changes need listener rebuilds — swapping a listener safely online takes a new port as a transition (dual-server rolling) or a restart. Not a defect; an honest boundary.
Hunting for reload on a custom service
Symptom
Docs/intuition say it reloads; no path exists.
Cause
The custom class keeps connections and resources entirely application-owned — the host cannot judge a safe point. Put hot-updatable business on the four protocol classes; keep custom for the genuinely special.

Exercises

BasicThe boundary checklist

Trigger every change in 31.2's table and record each reload outcome message.

Acceptance: all six rows verified; every rejection carries an explicit Message.
AdvancedReproducing the bomb

Replicate the reload bomb: 20 bad scripts in a burst with one good script inserted mid-way, all under load.

Acceptance: 20 FAILED + 1 SUCCEEDED; zero load failures; the good script takes effect instantly.
ChallengeRolling a port change

Design and implement a "port change without downtime" scheme (hint: temporary dual servers + client cutover + cleanup).

Acceptance: 100% availability throughout; a rollback path documented for every step.

Summary

PointConclusion
The predicatescripts/certificates/same-endpoint config → reload; everything else restarts
Immutable itemsworkers, listener fields, endpoint structure, the custom class
Resiliencebad input cleanly rejected, old generation intact, queues bounded
Disciplineminute-level changes, KB-level snapshots, no auto-triggering in production
Volume 4 closesmodel→trigger→handover→timeline→GC→boundaries: the hot reload loop is complete
xrt further reading