mustflow 2.117.0 → 2.118.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/cli/commands/run/args.js +5 -0
- package/dist/cli/commands/run/execution.js +7 -6
- package/dist/cli/commands/run/preview.js +6 -5
- package/dist/cli/commands/run/receipt.js +1 -0
- package/dist/cli/commands/run.js +2 -1
- package/dist/cli/commands/workspace.js +216 -81
- package/dist/cli/i18n/en.js +1 -0
- package/dist/cli/i18n/es.js +1 -0
- package/dist/cli/i18n/fr.js +1 -0
- package/dist/cli/i18n/hi.js +1 -0
- package/dist/cli/i18n/ko.js +1 -0
- package/dist/cli/i18n/zh.js +1 -0
- package/dist/cli/lib/manifest-lock.js +10 -1
- package/dist/cli/lib/repo-map.js +5 -0
- package/dist/cli/lib/run-context.js +136 -0
- package/dist/cli/lib/run-root-trust.js +22 -15
- package/dist/cli/lib/validation/index.js +8 -0
- package/dist/core/config-loading.js +107 -14
- package/dist/core/run-receipt.js +1 -0
- package/dist/core/workspace-command-authority.js +98 -0
- package/package.json +1 -1
- package/schemas/run-receipt.schema.json +10 -0
- package/schemas/workspace-command-catalog.schema.json +3 -0
- package/schemas/workspace-command-fragments.schema.json +2 -0
- package/schemas/workspace-status.schema.json +4 -1
- package/schemas/workspace-verification-plan.schema.json +3 -0
- package/templates/default/common/.mustflow/config/mustflow.toml +2 -0
- package/templates/default/i18n.toml +14 -2
- package/templates/default/locales/en/.mustflow/skills/INDEX.md +12 -3
- package/templates/default/locales/en/.mustflow/skills/ai-game-asset-production/SKILL.md +209 -0
- package/templates/default/locales/en/.mustflow/skills/ai-game-asset-production/references/asset-contract-validation.md +106 -0
- package/templates/default/locales/en/.mustflow/skills/ai-game-asset-production/references/raster-alpha-atlas-checklist.md +109 -0
- package/templates/default/locales/en/.mustflow/skills/ai-game-asset-production/references/tile-animation-checklist.md +98 -0
- package/templates/default/locales/en/.mustflow/skills/connection-lifecycle-integrity-review/SKILL.md +302 -0
- package/templates/default/locales/en/.mustflow/skills/connection-lifecycle-integrity-review/references/connection-fault-injection-resource-lifetime-validation.md +277 -0
- package/templates/default/locales/en/.mustflow/skills/connection-lifecycle-integrity-review/references/node-stream-transport-lifecycle-checklist.md +381 -0
- package/templates/default/locales/en/.mustflow/skills/memory-lifetime-review/SKILL.md +94 -2
- package/templates/default/locales/en/.mustflow/skills/memory-lifetime-review/references/cross-runtime-memory-stream-reproduction-harness.md +289 -0
- package/templates/default/locales/en/.mustflow/skills/memory-lifetime-review/references/node-memory-resource-backpressure-diagnostics.md +369 -0
- package/templates/default/locales/en/.mustflow/skills/routes.toml +23 -0
- package/templates/default/manifest.toml +17 -1
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# Connection Fault-Injection and Resource-Lifetime Validation
|
|
2
|
+
|
|
3
|
+
Use this reference when a connection-lifecycle review must design, execute, or judge repeated
|
|
4
|
+
fault injection, soak tests, resource-growth evidence, regression gates, or root-cause attribution.
|
|
5
|
+
The lifecycle model remains in `node-stream-transport-lifecycle-checklist.md`; this document owns
|
|
6
|
+
the validation campaign and the strength of the resulting claim.
|
|
7
|
+
|
|
8
|
+
The concrete API examples target Node.js HTTP/1.1 `http` and `net`, plus HTTP/2 `http2`. Preserve
|
|
9
|
+
the test structure and evidence model in other runtimes, but replace runtime-specific APIs and
|
|
10
|
+
thresholds only through a predeclared repository or release contract.
|
|
11
|
+
|
|
12
|
+
## Contents
|
|
13
|
+
|
|
14
|
+
1. [Evidence boundaries and common misdiagnoses](#1-evidence-boundaries-and-common-misdiagnoses)
|
|
15
|
+
2. [Harness isolation and request ledger](#2-harness-isolation-and-request-ledger)
|
|
16
|
+
3. [Common failure gates](#3-common-failure-gates)
|
|
17
|
+
4. [Repeated fault and soak matrix](#4-repeated-fault-and-soak-matrix)
|
|
18
|
+
5. [Termination-method side effects](#5-termination-method-side-effects)
|
|
19
|
+
6. [Compatibility and regression matrix](#6-compatibility-and-regression-matrix)
|
|
20
|
+
7. [Lifecycle implementation requirements](#7-lifecycle-implementation-requirements)
|
|
21
|
+
8. [Root-cause attribution](#8-root-cause-attribution)
|
|
22
|
+
9. [Mitigation and containment decisions](#9-mitigation-and-containment-decisions)
|
|
23
|
+
10. [Resolution claim gate](#10-resolution-claim-gate)
|
|
24
|
+
|
|
25
|
+
## 1. Evidence boundaries and common misdiagnoses
|
|
26
|
+
|
|
27
|
+
Do not equate RSS growth with retained application objects. RSS also contains allocator
|
|
28
|
+
fragmentation, native TLS buffers, runtime arenas, and pages that the allocator has not returned to
|
|
29
|
+
the operating system. Pair heap evidence with external memory, array buffers, native handles, file
|
|
30
|
+
descriptors, and protocol ownership.
|
|
31
|
+
|
|
32
|
+
Do not count `TIME_WAIT` as an open application socket. It represents a connection the kernel is
|
|
33
|
+
remembering after close. Sustained `CLOSE_WAIT`, by contrast, is strong evidence that the process
|
|
34
|
+
observed peer FIN but did not complete its local close path. Confirm the owning process and socket
|
|
35
|
+
before assigning cause.
|
|
36
|
+
|
|
37
|
+
Do not treat a `destroy()` call as cleanup completion. It is an abortive action that can discard
|
|
38
|
+
buffered data, fail or strand callbacks, and destroy sibling work on a shared HTTP/2 session.
|
|
39
|
+
Require terminal settlement, resource release, and physical close observation separately.
|
|
40
|
+
|
|
41
|
+
Keep these evidence classes distinct:
|
|
42
|
+
|
|
43
|
+
- configured repeated test;
|
|
44
|
+
- configured controlled benchmark;
|
|
45
|
+
- configured soak or chaos lane;
|
|
46
|
+
- heap or retained-object diagnostic;
|
|
47
|
+
- native handle, file-descriptor, or TCP-state diagnostic;
|
|
48
|
+
- packet or infrastructure-path evidence;
|
|
49
|
+
- static lifecycle review;
|
|
50
|
+
- manual-only evidence; and
|
|
51
|
+
- missing evidence.
|
|
52
|
+
|
|
53
|
+
Never promote a static review or one green reproducer into a soak, production, or root-cause claim.
|
|
54
|
+
|
|
55
|
+
## 2. Harness isolation and request ledger
|
|
56
|
+
|
|
57
|
+
Separate the system under test, clean-load generator, raw TCP or HTTP fault peer, and network-fault
|
|
58
|
+
injector into different processes or isolation units. Measure only the system under test. On Linux,
|
|
59
|
+
a dedicated cgroup and PID namespace can prevent harness descriptors and timers from contaminating
|
|
60
|
+
the process ledger. Treat namespaces, native RST helpers, packet capture, and network emulation as
|
|
61
|
+
configured or manual-only infrastructure; this reference does not authorize raw commands.
|
|
62
|
+
|
|
63
|
+
Generate FIN through a graceful peer write close. Generate RST with a runtime-supported reset API
|
|
64
|
+
or a narrowly scoped native helper whose behavior is proven. A proxy that disconnects a connection
|
|
65
|
+
tests disconnect handling; it does not prove packet loss, reordering, or blackhole behavior. Use a
|
|
66
|
+
kernel-level network emulator when the configured environment owns those profiles.
|
|
67
|
+
|
|
68
|
+
Assign every logical request:
|
|
69
|
+
|
|
70
|
+
- a monotonic request identifier;
|
|
71
|
+
- deterministic payload bytes and SHA-256;
|
|
72
|
+
- intended and observed byte counts;
|
|
73
|
+
- an idempotency key where side effects are possible;
|
|
74
|
+
- monotonic timestamps for creation, connect, headers, body, completion, cancel, timeout, error,
|
|
75
|
+
and physical close; and
|
|
76
|
+
- exactly one terminal classification: succeeded, expected fault, or unexpected failure.
|
|
77
|
+
|
|
78
|
+
Require the closed-cohort conservation rule:
|
|
79
|
+
|
|
80
|
+
```text
|
|
81
|
+
started = succeeded + expected_faulted + unexpected_failed
|
|
82
|
+
outstanding = 0
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Sample at a declared interval, normally one second for long-running Node campaigns:
|
|
86
|
+
|
|
87
|
+
- RSS, heap used, external memory, and array-buffer bytes;
|
|
88
|
+
- file descriptors or native handles and TCP state counts;
|
|
89
|
+
- active timers and runtime resource categories;
|
|
90
|
+
- HTTP/1 agent busy and free socket counts;
|
|
91
|
+
- HTTP/2 session and stream counts;
|
|
92
|
+
- pending promises and write callbacks; and
|
|
93
|
+
- event-loop delay and clean-request latency.
|
|
94
|
+
|
|
95
|
+
Use `process.getActiveResourcesInfo()` only as a resource-category observation. It does not prove
|
|
96
|
+
ownership. Private runtime APIs such as `process._getActiveHandles()` may support test diagnostics
|
|
97
|
+
when explicitly configured, but must not become production cleanup logic or the sole proof.
|
|
98
|
+
|
|
99
|
+
Establish clean offered load before injecting faults. A useful default is 70% of the maximum clean
|
|
100
|
+
throughput that still satisfies the p99 SLO. Mix faulted and clean requests so the campaign exposes
|
|
101
|
+
cross-request damage. Run fixed campaigns with at least three predeclared independent seeds; never
|
|
102
|
+
rerun until a favorable seed appears.
|
|
103
|
+
|
|
104
|
+
## 3. Common failure gates
|
|
105
|
+
|
|
106
|
+
Zero-tolerance correctness rules are not environment calibration points:
|
|
107
|
+
|
|
108
|
+
- no crash, OOM kill, assertion, uncaught exception, or unhandled rejection;
|
|
109
|
+
- no successful request with a byte-length or SHA-256 mismatch;
|
|
110
|
+
- no idempotency key committed twice and no cancelled request later committed as success;
|
|
111
|
+
- no duplicate terminal transition, common finalizer, or object `close` observation;
|
|
112
|
+
- every accepted write callback or promise settles exactly once; and
|
|
113
|
+
- every closed-cohort request reaches exactly one terminal outcome.
|
|
114
|
+
|
|
115
|
+
Use the following numeric defaults only when the repository has not declared a stricter gate and
|
|
116
|
+
the harness can measure the named quantity consistently. Record any override before the run.
|
|
117
|
+
|
|
118
|
+
| Surface | Default failure gate |
|
|
119
|
+
| --- | --- |
|
|
120
|
+
| Settlement | After load stops, any outstanding request, promise, or write callback remains after `max(2 * longest timeout, 30s)` |
|
|
121
|
+
| Write callback | Settlement exceeds the operation timeout plus 250ms |
|
|
122
|
+
| Fixed-run heap | After cooldown, heap exceeds clean baseline by 16MiB |
|
|
123
|
+
| Fixed-run RSS | After cooldown, RSS exceeds clean baseline by 64MiB |
|
|
124
|
+
| Fixed-run heap slope | The lower bound of the 95% confidence interval across 500-request quiescent snapshots exceeds 256 bytes per request |
|
|
125
|
+
| Fixed-run handles | After explicitly closing agents and HTTP/2 sessions, descriptors exceed baseline by 3 or active TCP exceeds baseline by 1 |
|
|
126
|
+
| `CLOSE_WAIT` | Any additional connection persists for at least 60 seconds |
|
|
127
|
+
| Soak heap slope | After the first hour, one-minute medians exceed 0.5MiB/hour in two consecutive 30-minute windows |
|
|
128
|
+
| Soak external slope | External plus array buffers exceed 1MiB/hour under the same rule |
|
|
129
|
+
| Soak RSS slope | RSS exceeds 2MiB/hour under the same rule |
|
|
130
|
+
| Soak hard cap | RSS grows by `max(128MiB, 25% of baseline)` or heap grows by 64MiB |
|
|
131
|
+
| Handle slope | FD, TCP connection, or HTTP/2 stream growth exceeds 0.2/hour; streams must return to zero and sessions to the configured pool size after faults stop |
|
|
132
|
+
| Clean-request isolation | Unexpected error rate exceeds 0.01% or p99 exceeds clean baseline by 25% |
|
|
133
|
+
| Post-fix performance | Throughput falls below 95%, p99 rises above 115%, or CPU per request rises above 110% of the pre-fix comparator |
|
|
134
|
+
| Owned buffer | Queued bytes exceed the declared limit plus one maximum chunk and 64KiB |
|
|
135
|
+
| Timeout accuracy | Fires over 50ms early or later than the deadline plus `max(250ms, 2 * event-loop p99)` |
|
|
136
|
+
|
|
137
|
+
Keep two different teardown modes. Leak campaigns explicitly close agents and sessions before the
|
|
138
|
+
final baseline. Pool-reuse campaigns intentionally leave the pool open and judge only its declared
|
|
139
|
+
steady state. Mixing them falsely labels healthy keep-alive sockets as leaks.
|
|
140
|
+
|
|
141
|
+
## 4. Repeated fault and soak matrix
|
|
142
|
+
|
|
143
|
+
Run each applicable fault for HTTP/1.1 cleartext, HTTP/1.1 TLS, and HTTP/2 TLS, in both client and
|
|
144
|
+
server roles when the product supports them. A release-level claim must include the production TLS
|
|
145
|
+
and proxy path or explicitly state that this evidence is missing.
|
|
146
|
+
|
|
147
|
+
| Fault | Repeated campaign | Soak campaign | Additional oracle |
|
|
148
|
+
| --- | --- | --- | --- |
|
|
149
|
+
| Slow peer | Headers at 1 byte/250ms, body at 1KiB/500ms, with some permanent stalls; 5,000 fault requests and 100 concurrent slow connections | Six hours with 50 slow connections and 5% slow arrivals | Header and body idle deadlines fire within tolerance; inbound queues stay bounded; clean p99 rises no more than 20%; no handles remain after slow peers close |
|
|
150
|
+
| Disconnect by phase | Disconnect after connect, at 50% headers, after headers, at 25/50/75/99% body, after response headers, and at 25/50/75/99% response body; 20,000 evenly distributed attempts | Six hours with 10% phased disconnects | Partial requests never succeed; files, transactions, locks, timers, and listeners release within five seconds of terminal observation |
|
|
151
|
+
| FIN | Complete message, truncated declared body, and mid-response FIN; 10,000 per position | Six hours with 5% random FIN | Complete framing may succeed; truncated framing must fail; `end` plus `close` never finalizes twice |
|
|
152
|
+
| RST | Same positions as FIN; separate TCP RST from HTTP/2 `RST_STREAM(CANCEL)`; 10,000 TCP cases and 20,000 stream resets | Six hours with 5% random resets | No unfinished work succeeds after TCP RST; stream reset does not fail sibling streams or close a healthy session |
|
|
153
|
+
| Reader stops | Never read, or stop after 64KiB and 1MiB, while peer streams 100MiB; 2,000 cases and 50 stuck readers | Six hours with 20 stuck readers | No enqueue after backpressure without drain; callbacks settle; owned buffers and external memory remain bounded |
|
|
154
|
+
| Writer stops | Declare a large body, send 0/1/50/99%, then leave the connection open; 5,000 cases and 100 stalled writers | Six hours with 50 stalled writers | Body idle timeout retires handler, parser, timer, and transport; partial body never becomes success |
|
|
155
|
+
| Timeout boundary | Connect, headers, body idle, response idle, total, keep-alive, HTTP/2 stream, and session timeouts at `T-100`, `T-10`, `T+10`, and `T+100ms`; 2,000 per timeout | Six hours with 1% of requests near each boundary | No early timeout and no stale timer kills the next request on a reused socket |
|
|
156
|
+
| Unconsumed response | No listener, resume, destroy, or cancel for 0-byte, 1KiB, 1MiB, and endless chunked bodies; 10,000 cases and 100 concurrent bodies | Six hours with 5% unconsumed responses | Lease expiry performs bounded discard or cancel; HTTP/1 busy pool does not stick at its maximum; HTTP/2 sibling p99 rises no more than 10% |
|
|
157
|
+
| Mid-stream cancel | Cancel uploads and downloads at chunk boundaries; 20,000 cases | Six hours with 10% random cancellation | Cancelled HTTP/1 sockets are not reused; HTTP/2 cancels only the stream; partial data never succeeds |
|
|
158
|
+
| Server restart | 200 graceful and 200 hard restarts with 200 in-flight requests; use GOAWAY for graceful HTTP/2 | Six hours, restarting every five minutes and alternating graceful and hard | No accepts after drain starts; grace-eligible work reaches 99.9% success; old process exits within grace plus five seconds; hard-restart client operations settle within their own deadlines |
|
|
159
|
+
| Delay and loss | Directional 200ms plus or minus 50ms, 1% random loss, periodic 5% burst loss, 0.1% reorder, 0.1% duplicate, then a mixed profile; 100,000 requests per profile | Six hours per profile and 24 hours mixed | Zero successful-body corruption and duplicate effects; idempotent success at least 99.9% after at most one allowed retry; retry amplification at most 1.2; no automatic retry for non-idempotent work without a safe key; throughput at least 70% of the same-RTT clean control |
|
|
160
|
+
|
|
161
|
+
TCP packet loss does not directly imply the same request failure rate because retransmission hides
|
|
162
|
+
many losses. Use reset, blackhole, and half-open profiles when the goal is request failure rather
|
|
163
|
+
than prolonged latency and resource ownership.
|
|
164
|
+
|
|
165
|
+
## 5. Termination-method side effects
|
|
166
|
+
|
|
167
|
+
| Change | Risk | Required contract |
|
|
168
|
+
| --- | --- | --- |
|
|
169
|
+
| `destroy()` | Discards writable buffers, can race callbacks, prevents HTTP/1 reuse, and can kill every stream in an HTTP/2 session | Admit abortive termination once; retire only an HTTP/1 connection whose message boundary is untrusted; destroy an HTTP/2 session only for connection-level transport or protocol failure |
|
|
170
|
+
| `cancel()` | A shared signal cancels unrelated requests; retry after partial delivery can duplicate non-idempotent effects; error reasons can be flattened | Use request-scoped cancellation, preserve the reason, forbid unsafe automatic replay, and cancel an HTTP/2 stream rather than its session |
|
|
171
|
+
| `pause()` | A paused stream can retain a socket forever and consume HTTP/2 flow-control credit; a peer-idle timer can contradict application-owned pause | Give pause an owner, high/low-water hysteresis, and a deadline that ends in resume or cancel; charge application pause to a total-processing deadline rather than peer-idle time |
|
|
172
|
+
| `resume()` | Automatic drain can silently discard a body, hold endless chunked responses, and hide trailers or body errors | Auto-drain only under an explicit body-discard contract; cap bytes and time; exclude `HEAD`, `204`, and `304` from body-discard logic |
|
|
173
|
+
| Buffer cap | Truncation can report corrupt data as success; compressed size misses expansion; per-layer high-water marks do not form one process cap | Count compressed and expanded bytes plus every application-owned queue; fail explicitly on overflow; bound overshoot by one chunk plus 64KiB |
|
|
174
|
+
| Timeout | Completion can race a stale timeout; socket-owned timers can kill a later pooled request; stream timeout can destroy an HTTP/2 session | Separate connect, headers, body idle, response idle, total, keep-alive, stream, and session timers; remove request timers before pool return |
|
|
175
|
+
| Forced callback settlement | Runtime callbacks and error events can both complete the same logical write, or neither may do so | Track write identity and settle through one once-only business completion gate; a watchdog diagnoses missing ownership but does not replace it |
|
|
176
|
+
|
|
177
|
+
`finish` proves only that the local writable handed bytes to a lower layer. It does not prove peer
|
|
178
|
+
receipt. Use peer-observed length and digest, or an application-level acknowledgement, for delivery
|
|
179
|
+
integrity.
|
|
180
|
+
|
|
181
|
+
## 6. Compatibility and regression matrix
|
|
182
|
+
|
|
183
|
+
Preserve documented public contracts and legitimate user behavior, not accidental nanosecond event
|
|
184
|
+
order, infinite sockets, or missing callbacks. Adding automatic cancellation, default timeouts, or
|
|
185
|
+
body-size limits is a compatibility change. Use opt-in behavior, a deprecation period, or a major
|
|
186
|
+
version boundary where the existing public contract permits late consumers or slow streams.
|
|
187
|
+
|
|
188
|
+
Minimum regression coverage:
|
|
189
|
+
|
|
190
|
+
- body sizes 0, 1, 63KiB, 64KiB, 65KiB, 1MiB, and 100MiB over HTTP/1 and HTTP/2, with exact length,
|
|
191
|
+
SHA-256, headers, and trailers;
|
|
192
|
+
- success, peer FIN, peer RST, local cancel, and timeout event contracts, asserting partial order and
|
|
193
|
+
counts rather than one exact event array;
|
|
194
|
+
- 100,000 writes around backpressure, destroy, and timeout, with issued callbacks equal to settled
|
|
195
|
+
callbacks and no pending callback after the deadline;
|
|
196
|
+
- a 100MiB producer against a 1KiB/s consumer, with no enqueue after `write() === false` before drain;
|
|
197
|
+
- 10,000 healthy HTTP/1 requests, failing if socket reuse drops by more than five percentage points;
|
|
198
|
+
- 10,000 damaged HTTP/1 requests, proving zero reuse of parser-unsafe sockets and no response-byte
|
|
199
|
+
contamination of the next request;
|
|
200
|
+
- HTTP/2 rounds with 100 concurrent streams and 10 cancellations, proving zero sibling errors,
|
|
201
|
+
zero session closes, and no more than 10% sibling p99 degradation;
|
|
202
|
+
- no-body `HEAD`, `204`, `304`, and zero-length responses, proving no unnecessary body lease;
|
|
203
|
+
- 200 graceful restart rounds with correct GOAWAY retry and no duplicate side effect;
|
|
204
|
+
- preserved public error class, code, abort reason, and timeout reason; and
|
|
205
|
+
- post-suite quiescence at no more than baseline plus three descriptors, baseline plus one TCP
|
|
206
|
+
connection, zero pending callback, and zero HTTP/2 streams.
|
|
207
|
+
|
|
208
|
+
## 7. Lifecycle implementation requirements
|
|
209
|
+
|
|
210
|
+
Route timeout, abort, stream error, socket close, and shutdown through one idempotent finalizer. The
|
|
211
|
+
first terminal reason wins; later contenders remain secondary diagnostics. Remove request timers and
|
|
212
|
+
listeners, settle callbacks, finalize body and stream disposition, perform the narrowest
|
|
213
|
+
protocol-appropriate close, and decide pool eligibility last.
|
|
214
|
+
|
|
215
|
+
Reuse HTTP/1 only after a complete message boundary with no parser or framing error. Cancel HTTP/2
|
|
216
|
+
at stream scope unless transport, TLS, protocol, GOAWAY, or connection-level flow-control evidence
|
|
217
|
+
invalidates the session. Give every pause and every body lease a named owner and bounded end state.
|
|
218
|
+
|
|
219
|
+
## 8. Root-cause attribution
|
|
220
|
+
|
|
221
|
+
Repeat the same deterministic fault schedule through four progressively smaller layers:
|
|
222
|
+
|
|
223
|
+
1. full application;
|
|
224
|
+
2. minimal program using the candidate library;
|
|
225
|
+
3. minimal program using only runtime core APIs; and
|
|
226
|
+
4. raw transport across both direct and production-like infrastructure paths.
|
|
227
|
+
|
|
228
|
+
Classify only from differential evidence:
|
|
229
|
+
|
|
230
|
+
| Class | Confirmation | Primary action |
|
|
231
|
+
| --- | --- | --- |
|
|
232
|
+
| Application | Full app reproduces, minimal library handler does not, and retainers point to app maps, listeners, timers, closures, bodies, or callbacks | Fix lifecycle ownership and body consumption; temporarily isolate or limit the suspect endpoint |
|
|
233
|
+
| Library | Minimal library program reproduces, core-only program does not, or a library-version bisect identifies the boundary | Upgrade to a fixed release, apply a bounded vendor fix, or exactly pin the last known good version |
|
|
234
|
+
| Runtime | Core-only program reproduces and runtime-version evidence identifies a fix or regression | Upgrade to the fixed runtime or exactly pin the last known good runtime after compatibility tests |
|
|
235
|
+
| Environment | Direct path is clean but a load balancer, mesh, NAT, firewall, kernel, container, or proxy path reproduces with packet or configuration evidence | Correct timeout, keep-alive, conntrack, descriptor, NAT, or proxy-buffering policy; do not hide it with indiscriminate destroy calls |
|
|
236
|
+
|
|
237
|
+
An unconsumed body is an application defect when the library contract requires consume or cancel
|
|
238
|
+
and the caller does neither. It becomes a library defect only when documented cancellation still
|
|
239
|
+
leaves the pool slot or HTTP/2 stream retained.
|
|
240
|
+
|
|
241
|
+
## 9. Mitigation and containment decisions
|
|
242
|
+
|
|
243
|
+
Treat these as default incident thresholds only when adopted by the owning operational policy:
|
|
244
|
+
|
|
245
|
+
- above 70% of the process resource budget, or under six hours to projected exhaustion, halve
|
|
246
|
+
suspect concurrency and limit new-connection rate and request-body size;
|
|
247
|
+
- above 85%, or under 30 minutes to exhaustion, stop new admission, drain gracefully, then restart;
|
|
248
|
+
- isolate a request when it can retain over 5% of process memory, a paused HTTP/2 stream reduces
|
|
249
|
+
sibling throughput by over 10%, or one tenant holds over 20% of pool capacity for a sustained
|
|
250
|
+
period; and
|
|
251
|
+
- use separate workers, pools, tenant sessions, or bounded queues for the isolation boundary.
|
|
252
|
+
|
|
253
|
+
A restart, traffic limit, or exact-version pin can prevent exhaustion while root cause remains. Mark
|
|
254
|
+
it as mitigation, not permanent resolution. Never leave periodic restarts as the only long-term
|
|
255
|
+
design when retained ownership still grows monotonically.
|
|
256
|
+
|
|
257
|
+
## 10. Resolution claim gate
|
|
258
|
+
|
|
259
|
+
Call the issue permanently resolved only when all applicable evidence exists:
|
|
260
|
+
|
|
261
|
+
1. The deterministic reproducer fails 20 of 20 runs before the fix. For a probabilistic defect,
|
|
262
|
+
run at least 30,000 pre-fix trials and require the lower bound of its 95% failure-rate confidence
|
|
263
|
+
interval to exceed the declared failure threshold.
|
|
264
|
+
2. The same reproducer succeeds 20 of 20 runs after the fix. For a probabilistic lane, require zero
|
|
265
|
+
failures in at least 30,000 post-fix trials. Run the full correctness suite three times with
|
|
266
|
+
independent seeds and preserve throughput, p99, CPU, integrity, and callback gates.
|
|
267
|
+
3. Each applicable fault passes a six-hour soak and the mixed profile passes 24 hours. If the
|
|
268
|
+
historical median time to failure exceeds eight hours, use the longer of 24 hours or three times
|
|
269
|
+
that median. Accelerating event count does not waive the 24-hour wall-clock floor for this gate.
|
|
270
|
+
4. Retained objects, open handles, version bisect, or reproducible infrastructure mechanics identify
|
|
271
|
+
the owner and causal path.
|
|
272
|
+
|
|
273
|
+
If only the reproducer is green, report a fix candidate. If soak passes without causal ownership,
|
|
274
|
+
report symptom suppression or partial evidence. If traffic limits or restart flatten the graph,
|
|
275
|
+
report mitigation. Never label unavailable long-running, packet, heap, native-handle, or
|
|
276
|
+
production-path evidence as passed; keep it manual-only or missing under the selected repository's
|
|
277
|
+
command contract.
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
# Node Stream and Transport Lifecycle Checklist
|
|
2
|
+
|
|
3
|
+
Use this reference for Node.js `net.Socket`, HTTP/1.1, HTTP/2, Web Streams, Undici, stream
|
|
4
|
+
adapters, connection pools, and process-shutdown reviews. Re-check version-sensitive details against
|
|
5
|
+
the current official documentation and repository tests before making durable compatibility claims.
|
|
6
|
+
|
|
7
|
+
## Contents
|
|
8
|
+
|
|
9
|
+
1. [Layered state model](#1-layered-state-model)
|
|
10
|
+
2. [Events, actions, and terminal causes](#2-events-actions-and-terminal-causes)
|
|
11
|
+
3. [Event path contracts](#3-event-path-contracts)
|
|
12
|
+
4. [Request, body, reader, and reuse rules](#4-request-body-reader-and-reuse-rules)
|
|
13
|
+
5. [Resource and conservation invariants](#5-resource-and-conservation-invariants)
|
|
14
|
+
6. [Backpressure and buffer budgets](#6-backpressure-and-buffer-budgets)
|
|
15
|
+
7. [Retainer and event-loop reference audit](#7-retainer-and-event-loop-reference-audit)
|
|
16
|
+
8. [Verification and fault injection](#8-verification-and-fault-injection)
|
|
17
|
+
9. [Server and client shutdown](#9-server-and-client-shutdown)
|
|
18
|
+
10. [Acceptance gate](#10-acceptance-gate)
|
|
19
|
+
11. [Official references](#11-official-references)
|
|
20
|
+
|
|
21
|
+
## 1. Layered state model
|
|
22
|
+
|
|
23
|
+
Do not model the whole lifecycle as `OPEN -> CLOSING -> CLOSED`. TCP read and write sides close
|
|
24
|
+
independently. HTTP request completion, response-body completion, pool return, and physical socket
|
|
25
|
+
close are different facts. HTTP/2 stream cancellation normally must not kill the session.
|
|
26
|
+
|
|
27
|
+
Use orthogonal dimensions when applicable:
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
TransportState = CONNECTING | OPEN | ABORTING | CLOSED
|
|
31
|
+
ReadState = OPEN | EOF_PENDING_CONSUME | ENDED | ERRORED
|
|
32
|
+
WriteState = OPEN | END_REQUESTED | FINISHED | ERRORED
|
|
33
|
+
PoolState = UNPOOLED | BUSY | IDLE | QUARANTINED
|
|
34
|
+
|
|
35
|
+
RequestState = QUEUED | WRITING | WAITING_HEADERS | BODY_OPEN | SETTLING | SETTLED
|
|
36
|
+
BodyDisposition = NONE | CONSUMED | DISCARDED | CANCELLED | FAILED
|
|
37
|
+
ReaderState = NONE | LOCKED | CANCEL_PENDING | CANCELLED | RELEASED
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Interpret `ReadState=ENDED, WriteState=OPEN` as remote half-close and
|
|
41
|
+
`ReadState=OPEN, WriteState=FINISHED` as local half-close. Do not explode the cross-product into a
|
|
42
|
+
single enum unless a smaller protocol truly forbids independent progress.
|
|
43
|
+
|
|
44
|
+
## 2. Events, actions, and terminal causes
|
|
45
|
+
|
|
46
|
+
Keep Node stream events separate from local actions:
|
|
47
|
+
|
|
48
|
+
| Symbol | Meaning |
|
|
49
|
+
| --- | --- |
|
|
50
|
+
| `E` | socket `end`; readable EOF became observable after buffered data consumption |
|
|
51
|
+
| `F` | socket `finish`; writable `end()` completed flushing to the underlying system |
|
|
52
|
+
| `X` | socket `error` |
|
|
53
|
+
| `K` | socket `close`; the underlying resource closed |
|
|
54
|
+
| `D` | one state-machine-approved `destroy()` or `resetAndDestroy()` action |
|
|
55
|
+
|
|
56
|
+
`destroy()` is not an event. A graceful FIN path can complete with `D=0`. Require connection-record
|
|
57
|
+
finalization exactly once and abortive destroy admission at most once.
|
|
58
|
+
|
|
59
|
+
Normalize termination causes, for example:
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
NORMAL_LOCAL_FIN | NORMAL_REMOTE_FIN | PEER_RESET | TIMEOUT | ABORT_SIGNAL
|
|
63
|
+
READER_CANCEL | WRITER_ERROR | CLIENT_FORCE | SERVER_FORCE | HALF_CLOSE_TIMEOUT
|
|
64
|
+
BODY_ABANDONED | PROTOCOL_ERROR | CONNECT_ERROR
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Route all abortive entry points through one `tryTerminate(cause, mode)`-style boundary. Atomically
|
|
68
|
+
store the first cause as primary. Retain later timeout, abort, reset, or writer contenders as
|
|
69
|
+
secondary diagnostics only. Do not let them repeat destroy, callback settlement, registry removal,
|
|
70
|
+
or terminal metrics.
|
|
71
|
+
|
|
72
|
+
Finalize a physical connection from the physical close observer. Do not remove it from the active
|
|
73
|
+
registry immediately after `destroy()`, because the descriptor or handle can still be live. A
|
|
74
|
+
request may settle earlier after request-scoped cleanup.
|
|
75
|
+
|
|
76
|
+
## 3. Event path contracts
|
|
77
|
+
|
|
78
|
+
The table assumes `E` and `F` have not already happened. Preserve earlier events; after a transport
|
|
79
|
+
error, do not invent later successful `E` or `F` events.
|
|
80
|
+
|
|
81
|
+
| Path | Required relation | `(E,F,X,K,D)` | Reuse |
|
|
82
|
+
| --- | --- | ---: | --- |
|
|
83
|
+
| Graceful FIN, either side first | `E` and `F` once each, both before `K`; their mutual order can vary | `(1,1,0,1,0)` | no |
|
|
84
|
+
| Peer reset | normalize peer-reset error, then `X -> K` | `(0,0,1,1,0)` | no |
|
|
85
|
+
| Idle timeout with local abort | timeout notification, terminal admission, timer cleanup, `D -> X -> K` | `(0,0,1,1,1)` | no |
|
|
86
|
+
| AbortSignal after socket ownership | terminal admission, listener removal, `D -> X -> K` | `(0,0,1,1,1)` | no |
|
|
87
|
+
| Abort before socket allocation | request settles cancelled; no physical socket events | `(0,0,0,0,0)` | n/a |
|
|
88
|
+
| Safe body discard or HTTP/2 stream cancel | settle stream or body, release lock; keep physical connection | `(0,0,0,0,0)` | conditional |
|
|
89
|
+
| HTTP/1.1 body boundary unrecoverable | quarantine, `D -> K` or adapter-defined `D -> X -> K` | transport-dependent | no |
|
|
90
|
+
| Writer error | fail all accepted writes once, `D -> X -> K`; no new `F` | `(0,0,1,1,1)` | no |
|
|
91
|
+
| Local force close without error object | `D -> K` | `(0,0,0,1,1)` | no |
|
|
92
|
+
| Half-close deadline | prior `E`, then timeout `D -> X -> K`; no new `F` | `(1,0,1,1,1)` | no |
|
|
93
|
+
| Paused readable, bounded drain succeeds | consume remaining data, then graceful finish and close | `(1,1,0,1,0)` | no |
|
|
94
|
+
| Paused readable, drain deadline fails | abort before EOF is observable, then `X -> K` | `(0,0,1,1,1)` | no |
|
|
95
|
+
| Pending writes finish gracefully | stop new writes, settle accepted writes, `F`; consume peer EOF, then `K` | `(1,1,0,1,0)` | no |
|
|
96
|
+
| Pending writes aborted | stop new writes, fail accepted writes once, `D -> X -> K`; no `F` | `(0,0,1,1,1)` | no |
|
|
97
|
+
| Unconsumed body before lease deadline | keep request body open and connection busy | `(0,0,0,0,0)` | not yet |
|
|
98
|
+
| Lease discard succeeds | confirm message boundary, settle body, return connection idle | `(0,0,0,0,0)` | yes |
|
|
99
|
+
| Lease discard fails | quarantine and retire the stream or connection | transport-dependent | no |
|
|
100
|
+
|
|
101
|
+
Do not force a total order between `E` and `F` on graceful full close. Local and remote FIN progress
|
|
102
|
+
independently. Require their valid partial order relative to physical close.
|
|
103
|
+
|
|
104
|
+
For Node HTTP objects, do not confuse request or response completion with socket completion:
|
|
105
|
+
|
|
106
|
+
- A successful keep-alive response can emit response `end` and request `close` while the socket
|
|
107
|
+
stays pooled.
|
|
108
|
+
- `ClientRequest` `finish` means request bytes were handed to the operating system, not accepted by
|
|
109
|
+
the server.
|
|
110
|
+
- `ServerResponse` `finish` does not prove client receipt.
|
|
111
|
+
- Premature response close has its own response `aborted`, request `close`, response error, and
|
|
112
|
+
response close surface; confirm exact supported ordering in the target Node version.
|
|
113
|
+
|
|
114
|
+
## 4. Request, body, reader, and reuse rules
|
|
115
|
+
|
|
116
|
+
Return an HTTP/1.1 connection to an idle pool only when all required facts are true:
|
|
117
|
+
|
|
118
|
+
```text
|
|
119
|
+
transportState == OPEN
|
|
120
|
+
poolState == BUSY
|
|
121
|
+
parserAtMessageBoundary == true
|
|
122
|
+
bodyDisposition in {CONSUMED, DISCARDED}
|
|
123
|
+
pendingWriteCount == 0
|
|
124
|
+
reservedBufferBytes == 0
|
|
125
|
+
primaryCause == null
|
|
126
|
+
readState == OPEN
|
|
127
|
+
writeState == OPEN
|
|
128
|
+
protocolCorruption == false
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Quarantine a connection after premature close, framing or content-length mismatch, parser error,
|
|
132
|
+
reset, or unrecoverable body abandonment. Do not trust `writable`, `readable`, or destroyed flags as
|
|
133
|
+
proof that the next response starts at a clean boundary.
|
|
134
|
+
|
|
135
|
+
Apply HTTP/2 cancellation at stream scope. Escalate to session quarantine only for connection-level
|
|
136
|
+
GOAWAY policy, session error, flow-control corruption, or another session-wide violation.
|
|
137
|
+
|
|
138
|
+
Every response body needs a lease:
|
|
139
|
+
|
|
140
|
+
```text
|
|
141
|
+
BodyLease = {
|
|
142
|
+
openedAt,
|
|
143
|
+
deadline,
|
|
144
|
+
maxDiscardBytes,
|
|
145
|
+
discardTimeout,
|
|
146
|
+
disposition,
|
|
147
|
+
readerOwner,
|
|
148
|
+
connectionId,
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
When a body is abandoned, attempt bounded discard only if framing and remaining size make it safe.
|
|
153
|
+
Apply byte and wall-clock limits together. Cancel or retire the narrowest safe protocol scope when
|
|
154
|
+
discard fails.
|
|
155
|
+
|
|
156
|
+
`reader.cancel()` completion and `reader.releaseLock()` are separate facts. Settle pending reads or
|
|
157
|
+
cancel, then release the lock in a finally-like boundary. Give cancel cleanup its own deadline when
|
|
158
|
+
the underlying source can stall.
|
|
159
|
+
|
|
160
|
+
Protocol defaults:
|
|
161
|
+
|
|
162
|
+
| Protocol | Cancellation policy |
|
|
163
|
+
| --- | --- |
|
|
164
|
+
| HTTP/1.1, known small remainder | bounded discard, then parser-boundary confirmation |
|
|
165
|
+
| HTTP/1.1, unknown or excessive remainder | quarantine and retire the socket |
|
|
166
|
+
| HTTP/2 | reset the stream; keep the session if session invariants remain valid |
|
|
167
|
+
| Raw TCP | retire the connection unless the application protocol defines cancel framing |
|
|
168
|
+
| WebSocket | use close handshake or protocol-error handling; do not map reader cancel blindly |
|
|
169
|
+
|
|
170
|
+
## 5. Resource and conservation invariants
|
|
171
|
+
|
|
172
|
+
Track and assert the following where the implementation owns the evidence:
|
|
173
|
+
|
|
174
|
+
| ID | Invariant |
|
|
175
|
+
| --- | --- |
|
|
176
|
+
| I1 | Every connection record reaches physical close and finalizes at most once. |
|
|
177
|
+
| I2 | `connections_created = connections_active + connections_closed`. |
|
|
178
|
+
| I3 | Graceful paths accept no destroy action; abortive paths accept at most one. |
|
|
179
|
+
| I4 | Each stream `end`, `finish`, `error`, and `close` event is counted at most once. |
|
|
180
|
+
| I5 | After terminal transport error, no new successful end or finish transition is accepted. |
|
|
181
|
+
| I6 | Closed connections remain in neither active registry nor pool indexes. |
|
|
182
|
+
| I7 | Every request has exactly one success, cancellation, or failure result. |
|
|
183
|
+
| I8 | Request settlement leaves no request timer, listener, retry token, or body lease. |
|
|
184
|
+
| I9 | Late callbacks cannot re-enter user code after request settlement or generation change. |
|
|
185
|
+
| I10 | Every accepted write settles once as success or failure; shutdown leaves none pending. |
|
|
186
|
+
| I11 | Application-owned reserved buffers remain within request, connection, and global budgets. |
|
|
187
|
+
| I12 | Every body reaches consumed, discarded, cancelled, or failed within its policy deadline. |
|
|
188
|
+
| I13 | Reader and writer locks do not outlive their operation or request. |
|
|
189
|
+
| I14 | Connections without a confirmed message boundary are never reused. |
|
|
190
|
+
| I15 | Retry queues are bounded by item count, bytes, age, and attempt; unsafe bodies are not replayed. |
|
|
191
|
+
| I16 | Intended shutdown leaves no unexpected refed timer, socket, or native handle. |
|
|
192
|
+
| I17 | Server shutdown progresses only through admission-stop, drain, force, and closed phases. |
|
|
193
|
+
|
|
194
|
+
Useful conservation equations:
|
|
195
|
+
|
|
196
|
+
```text
|
|
197
|
+
requests_started = requests_active + requests_succeeded + requests_failed + requests_cancelled
|
|
198
|
+
writes_accepted = writes_pending + writes_succeeded + writes_failed
|
|
199
|
+
body_leases_opened = body_leases_active + body_consumed + body_discarded
|
|
200
|
+
+ body_cancelled + body_failed
|
|
201
|
+
buffer_bytes_reserved = buffer_bytes_current + buffer_bytes_released
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Update registry state and related counters in the same transition boundary. Otherwise exceptions or
|
|
205
|
+
interleaving can leave durable metric divergence.
|
|
206
|
+
|
|
207
|
+
Use bounded metric dimensions such as reason, protocol, role, and mode. Put connection or request
|
|
208
|
+
identifiers in logs and traces, not metric labels.
|
|
209
|
+
|
|
210
|
+
## 6. Backpressure and buffer budgets
|
|
211
|
+
|
|
212
|
+
A stream high-water mark starts backpressure; it is not a whole-system hard limit. Account for every
|
|
213
|
+
application-owned queue:
|
|
214
|
+
|
|
215
|
+
```text
|
|
216
|
+
B_owned = B_node_readable + B_node_writable + B_web_readable + B_web_writable
|
|
217
|
+
+ B_http_parser + B_transform + B_application_queue + B_retry_body
|
|
218
|
+
+ B_discard_buffer + B_observability_capture
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Reserve byte budget before enqueue or body replication. On reservation failure, pause the producer,
|
|
222
|
+
reject admission, or apply the declared drop policy. Discovering an overage from metrics after
|
|
223
|
+
enqueue is not enforcement.
|
|
224
|
+
|
|
225
|
+
Count logical reservations consistently when Buffer slices share backing stores. Observe RSS,
|
|
226
|
+
external memory, kernel socket buffers, and TLS native buffers separately; JavaScript accounting
|
|
227
|
+
cannot promise an absolute process RSS cap.
|
|
228
|
+
|
|
229
|
+
After `write()` reports backpressure, wait for drain or abort instead of continuing to allocate.
|
|
230
|
+
Track every accepted write by sequence or ownership token and settle its callback or promise once.
|
|
231
|
+
|
|
232
|
+
## 7. Retainer and event-loop reference audit
|
|
233
|
+
|
|
234
|
+
Build explicit `ConnectionScope` and `RequestScope` ledgers. Register cleanup at creation time.
|
|
235
|
+
|
|
236
|
+
Connection scope examples:
|
|
237
|
+
|
|
238
|
+
- core socket close and error observers;
|
|
239
|
+
- socket timeout and half-close timer;
|
|
240
|
+
- pool idle timer;
|
|
241
|
+
- native close waiter;
|
|
242
|
+
- connection buffer reservations.
|
|
243
|
+
|
|
244
|
+
Request scope examples:
|
|
245
|
+
|
|
246
|
+
- AbortSignal listener remover;
|
|
247
|
+
- request and response listeners;
|
|
248
|
+
- request deadline timer;
|
|
249
|
+
- retry token;
|
|
250
|
+
- body lease;
|
|
251
|
+
- reader and writer ownership;
|
|
252
|
+
- pending write records;
|
|
253
|
+
- operation buffer reservations.
|
|
254
|
+
|
|
255
|
+
Make disposal idempotent. Request cleanup must not remove connection-core observers needed to record
|
|
256
|
+
physical close.
|
|
257
|
+
|
|
258
|
+
Audit long-lived roots and their capture paths:
|
|
259
|
+
|
|
260
|
+
```text
|
|
261
|
+
global dispatcher -> origin pool -> client -> socket -> parser/listener -> response body -> reader
|
|
262
|
+
module singleton -> request map -> request scope -> timer/retry/body buffer
|
|
263
|
+
libuv timer -> callback closure -> retry queue -> request body stream -> descriptor
|
|
264
|
+
server -> connection registry -> socket -> listener closure -> request/response
|
|
265
|
+
long-lived AbortSignal -> listener -> request scope -> reader/writer/socket
|
|
266
|
+
AsyncLocalStorage -> store -> request/response -> body buffers
|
|
267
|
+
diagnostics subscriber -> correlation map -> request -> body/socket
|
|
268
|
+
unconsumed tee branch -> queued chunks -> backing buffer -> response
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
`once` listeners can still live forever if the event never arrives. `Promise.race` does not cancel
|
|
272
|
+
the losing I/O. `unref` permits process exit but does not remove the object, listener, or descriptor.
|
|
273
|
+
Finalization registries are leak canaries, not deterministic cleanup.
|
|
274
|
+
|
|
275
|
+
For stream completion helpers, check whether listeners are retained after completion and use the
|
|
276
|
+
runtime-supported cleanup option or returned cleanup function. Do not reuse failed pipeline streams
|
|
277
|
+
unless the implementation contract proves their listener and error state is clean.
|
|
278
|
+
|
|
279
|
+
Separate heap retention from event-loop liveness. An unresolved promise alone does not keep Node
|
|
280
|
+
alive, but a promise reachable from a native callback, registry, listener, timer, or retry job can
|
|
281
|
+
retain a large request graph.
|
|
282
|
+
|
|
283
|
+
## 8. Verification and fault injection
|
|
284
|
+
|
|
285
|
+
Use a pre/post active-resource baseline where configured. `process.getActiveResourcesInfo()` can
|
|
286
|
+
show active resource types but not ownership, so it supplements rather than replaces the ledger.
|
|
287
|
+
Normalize version-dependent resource type names into categories such as timeout, TCP, TLS, DNS,
|
|
288
|
+
pipe, and message port.
|
|
289
|
+
|
|
290
|
+
Run natural-exit scenarios in a child process without `process.exit()` when the repository has a
|
|
291
|
+
configured harness. Pair natural exit with open-handle or descriptor evidence so `unref` cannot hide
|
|
292
|
+
an open resource.
|
|
293
|
+
|
|
294
|
+
For heap proof, warm up, repeat a bounded workload, collect only configured snapshot or retention
|
|
295
|
+
evidence, and compare retained request, connection, body lease, reader, and buffer counts with active
|
|
296
|
+
ownership plus fixed instrumentation slack. Pair heap evidence with native handle or descriptor
|
|
297
|
+
evidence when the platform supports it.
|
|
298
|
+
|
|
299
|
+
Fault matrix axes:
|
|
300
|
+
|
|
301
|
+
| Axis | Cases |
|
|
302
|
+
| --- | --- |
|
|
303
|
+
| Time | before connect, after connect, before/after headers, before first body byte, before last byte, around finish |
|
|
304
|
+
| Cause | FIN, RST, timeout, abort, reader cancel, writer error, client kill, server kill |
|
|
305
|
+
| Read | flowing, paused, locked reader, pending read, unconsumed body |
|
|
306
|
+
| Write | none, pending, backpressured, before drain, immediately after end request |
|
|
307
|
+
| Protocol | raw TCP, HTTP/1.1 keep-alive, HTTP/2 multiplexing |
|
|
308
|
+
| Race | abort+timeout, RST+timeout, body end+cancel, shutdown+client reset |
|
|
309
|
+
| Load | one connection, pool limit, many slow consumers, full retry queue |
|
|
310
|
+
|
|
311
|
+
The oracle must include event trace and counts, primary cause, accepted destroy count, request
|
|
312
|
+
result, registry and pool sizes, pending writes, body disposition, lock state, and peak reserved
|
|
313
|
+
bytes. “An error happened” is not a sufficient oracle.
|
|
314
|
+
|
|
315
|
+
## 9. Server and client shutdown
|
|
316
|
+
|
|
317
|
+
Server sequence:
|
|
318
|
+
|
|
319
|
+
```text
|
|
320
|
+
ACCEPTING
|
|
321
|
+
-> stop new accepts
|
|
322
|
+
-> DRAINING
|
|
323
|
+
-> retire idle HTTP/1.1 connections
|
|
324
|
+
-> give active responses and pending writes a grace deadline
|
|
325
|
+
-> send HTTP/2 GOAWAY and reject new streams when applicable
|
|
326
|
+
-> FORCING
|
|
327
|
+
-> retire remaining HTTP connections
|
|
328
|
+
-> retire separately tracked WebSocket, CONNECT, HTTP/2, or upgraded transports
|
|
329
|
+
-> wait for physical close observation
|
|
330
|
+
-> CLOSED
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Do not assume a generic HTTP close-all helper owns upgraded or HTTP/2 transports. Keep an explicit
|
|
334
|
+
registry for every transport class the application accepts.
|
|
335
|
+
|
|
336
|
+
Client sequence:
|
|
337
|
+
|
|
338
|
+
```text
|
|
339
|
+
OPEN
|
|
340
|
+
-> stop new request admission and retry scheduling
|
|
341
|
+
-> fail queued requests
|
|
342
|
+
-> give active body consume or cancel a grace deadline
|
|
343
|
+
-> close idle pool members
|
|
344
|
+
-> force remaining active connections after deadline
|
|
345
|
+
-> verify every request and physical connection finalized
|
|
346
|
+
-> CLOSED
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## 10. Acceptance gate
|
|
350
|
+
|
|
351
|
+
Require the applicable subset of these conditions before calling lifecycle handling complete:
|
|
352
|
+
|
|
353
|
+
```text
|
|
354
|
+
duplicate_finalize_total == 0
|
|
355
|
+
duplicate_destroy_attempt_total == 0
|
|
356
|
+
duplicate_stream_event_total == 0
|
|
357
|
+
post_error_event_total == 0
|
|
358
|
+
connection_conservation_error == 0
|
|
359
|
+
cleanup_residual_total == 0
|
|
360
|
+
late_callback_total == 0
|
|
361
|
+
writes_pending == 0 after shutdown
|
|
362
|
+
reuse_after_error_total == 0
|
|
363
|
+
stream_locks_active == 0 after request settlement
|
|
364
|
+
body_leases_active == 0 after request settlement
|
|
365
|
+
buffer_peak_bytes <= configured global buffer limit
|
|
366
|
+
open handles return to baseline plus intentional pool capacity
|
|
367
|
+
child process exits naturally without process.exit()
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Do not require metrics or diagnostics that the implementation does not own. Report the missing
|
|
371
|
+
evidence and preserve the bounded invariant rather than fabricating proof.
|
|
372
|
+
|
|
373
|
+
## 11. Official references
|
|
374
|
+
|
|
375
|
+
- [Node.js Streams](https://nodejs.org/api/stream.html)
|
|
376
|
+
- [Node.js Net](https://nodejs.org/api/net.html)
|
|
377
|
+
- [Node.js HTTP](https://nodejs.org/api/http.html)
|
|
378
|
+
- [Node.js Web Streams](https://nodejs.org/api/webstreams.html)
|
|
379
|
+
- [Node.js Timers](https://nodejs.org/api/timers.html)
|
|
380
|
+
- [Node.js Process](https://nodejs.org/api/process.html)
|
|
381
|
+
- [Undici Diagnostics Channel](https://undici.nodejs.org/api/DiagnosticsChannel)
|