Commit cf426cd
committed
fix(daemon): a 503 is an answer, not a wedge — stop restarting a healthy
service under load
GH #1419 Issue 3b (Steve postmortem): the supervisor cycled nexus-service
after 3 consecutive failed health checks caused purely by CPU-bound local
embedding, not by any failure — severing a client mid-batch.
MECHANISM, verified on both sides of the wire. GET /health is implemented
engine-side as dataSource.getConnection() + SELECT 1 (HealthHandler.java), so
it takes a HikariCP pool connection. The supervisor probed it with a 2s
timeout and exited for an OS restart after 3 consecutive failures at 1s — a
3-second grace window. Under indexing load the pool is contended, /health
blocks past 2s, the counter fills, and the service is restarted. The probe
that decides whether to KILL the service competes for the exact resource
saturation exhausts, so the loop is self-amplifying: load -> restart ->
in-flight clients severed -> retries -> more load.
THE DEEPER DEFECT: _service_healthy returned a bare bool, collapsing
"answered 503" and "did not answer at all" into one False. Those are opposite
pieces of evidence. A 503 proves the process is ALIVE and talking — its
dependency is unhappy — and restarting it cannot fix a down database while it
certainly does sever clients. Only silence is consistent with a wedge.
So the probe is now tri-state (HealthProbe OK / UNREADY / UNKNOWN) and ONLY
UNKNOWN advances the restart counter. UNREADY resets it, declines to stamp the
lease (consumers should not be routed to a service saying it cannot serve),
and logs. _service_healthy survives as a boolean for the STARTUP readiness
gate, where UNREADY and UNKNOWN really are equivalent.
BOUNDED, AND HONESTLY SO. Widening the probe budget hits an invariant: the
probe blocks the heartbeat thread, so a tick costs _HEALTH_TIMEOUT +
heartbeat interval and the lease TTL must be >= 3x that
(test_ttl_exceeds_worst_case_heartbeat_tick). With TTL 15s that caps the probe
at exactly 4.0s. A first attempt at 20s tripped that test — the supervisor
would have lost its OWN lease mid-probe, trading a spurious restart for a
vanished endpoint. 4.0s x 4 beats = ~16s of total silence before restart, up
from ~6s, at the invariant boundary with no margin left.
That mitigates the saturation case rather than closing it: a pool whose own
connectionTimeout is 30s can still outlast a 4s probe. Closing it needs a
dependency-free engine liveness endpoint and/or the probe off the heartbeat
thread — filed as nexus-hubc0, engine half batched into v0.1.55. The
503-vs-timeout distinction shipped here is what actually protects the
DB-unhappy case, and it stands on its own.
9 new tests, each RED first, driving a REAL loopback server on port 0 whose
status is switchable rather than stubbing the probe — a classifier that never
classifies would otherwise pass. Two existing heartbeat tests re-pointed at
the tri-state seam; the eight startup-gate stubs keep the boolean by design.
Bead: nexus-7f7gb
Follow-up: nexus-hubc01 parent 6907e23 commit cf426cd
3 files changed
Lines changed: 332 additions & 16 deletions
File tree
- src/nexus/daemon
- tests/daemon
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
65 | 65 | | |
66 | 66 | | |
67 | 67 | | |
| 68 | + | |
68 | 69 | | |
69 | 70 | | |
70 | 71 | | |
| |||
103 | 104 | | |
104 | 105 | | |
105 | 106 | | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
106 | 126 | | |
107 | 127 | | |
108 | 128 | | |
| |||
115 | 135 | | |
116 | 136 | | |
117 | 137 | | |
118 | | - | |
119 | | - | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
120 | 158 | | |
121 | 159 | | |
122 | 160 | | |
| |||
133 | 171 | | |
134 | 172 | | |
135 | 173 | | |
136 | | - | |
137 | | - | |
138 | | - | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
139 | 184 | | |
140 | | - | |
| 185 | + | |
141 | 186 | | |
142 | 187 | | |
143 | 188 | | |
| |||
644 | 689 | | |
645 | 690 | | |
646 | 691 | | |
647 | | - | |
648 | | - | |
| 692 | + | |
| 693 | + | |
| 694 | + | |
| 695 | + | |
| 696 | + | |
| 697 | + | |
| 698 | + | |
| 699 | + | |
| 700 | + | |
| 701 | + | |
649 | 702 | | |
650 | 703 | | |
651 | | - | |
| 704 | + | |
652 | 705 | | |
653 | 706 | | |
| 707 | + | |
654 | 708 | | |
655 | 709 | | |
656 | 710 | | |
657 | | - | |
658 | | - | |
659 | | - | |
| 711 | + | |
| 712 | + | |
| 713 | + | |
| 714 | + | |
| 715 | + | |
| 716 | + | |
| 717 | + | |
| 718 | + | |
| 719 | + | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
660 | 729 | | |
661 | 730 | | |
662 | 731 | | |
| |||
947 | 1016 | | |
948 | 1017 | | |
949 | 1018 | | |
950 | | - | |
| 1019 | + | |
| 1020 | + | |
| 1021 | + | |
| 1022 | + | |
951 | 1023 | | |
952 | 1024 | | |
953 | 1025 | | |
954 | 1026 | | |
955 | 1027 | | |
956 | 1028 | | |
| 1029 | + | |
| 1030 | + | |
| 1031 | + | |
| 1032 | + | |
| 1033 | + | |
| 1034 | + | |
| 1035 | + | |
| 1036 | + | |
| 1037 | + | |
| 1038 | + | |
| 1039 | + | |
| 1040 | + | |
| 1041 | + | |
| 1042 | + | |
957 | 1043 | | |
958 | 1044 | | |
959 | 1045 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
0 commit comments