Commit cfe8951
authored
fix(rpc,openapi): concurrent lazy router resolution, and faster route matching (#1736)
Fixes a bug where lazy routers could be permanently dropped when
requests arrive concurrently, and makes route matching 1.4x to 3.7x
faster.
## The bug
Both matchers rebuilt their pending lazy router collection **after**
awaiting the loader. With two requests in flight, the slower one's write
clobbered the faster one's, and any nested lazy router discovered in
between was lost for the lifetime of the process. Every route inside it
returned a miss from then on.
Reproduced with a 3 level lazy chain: `/p2/p3/leaf3` returned
`undefined` for the concurrent requests **and for every request after
them**.
The same interleaving also reloaded routers. 12 concurrent requests on a
3 level chain triggered 12, then 144, then 1728 loader calls. In the
OpenAPI matcher each redundant load also appended duplicate entries to
the rou3 tree, so the tree grew without bound. Each lazy router now
loads exactly once.
## Performance
Measured with both implementations in one process, interleaved in
rotating order, min of 25 rounds, against a second identical copy of the
original as a noise control.
| case | before | after | |
|---|---|---|---|
| RPC hit | 359ns | 103ns | 3.5x |
| RPC hit, 1296 procedures | 362ns | 105ns | 3.4x |
| RPC hit, 50 pending lazy routers | 1609ns | 224ns | 7.2x |
| OpenAPI static hit | 374ns | 103ns | 3.7x |
| OpenAPI dynamic param hit | 1008ns | 359ns | 2.8x |
| OpenAPI hit, 1000 routes | 1032ns | 415ns | 2.5x |
| OpenAPI hit, 50 pending lazy routers | 3400ns | 1381ns | 2.5x |
End to end that is 1.07x on a minimal RPC request, 1.27x when the router
has unresolved lazy sub routers. Construction is unchanged.
`walkProcedureContractsSync` also got 1.05x to 2.8x depending on router
shape, plus much lower allocation (16.7MB to 4.7MB walking a 50k
procedure router) and it now tolerates deeper nesting.
## Outcome
- Lazy routers are no longer lost when requests race
- Each lazy router loads once instead of once per concurrent request
- No more unbounded duplicate entries in the OpenAPI route tree
- A `RangeError: Maximum call stack size exceeded` is gone from
`walkProcedureContractsSync`, which fired once a single subtree carried
~123k lazy routers
- Route matching is meaningfully faster on every shape measured
## For reviewers
Three things worth a look:
1. **One intentional behaviour change.** The RPC matcher no longer loads
a lazy router mounted at `/lazy` for a request to `/lazyfoo/bar`. That
load could never have served the request, so results are unchanged, but
the loader call count differs. Covered by a test.
2. **A trade-off from sharing in-flight loads.** If a shared load fails,
every concurrent request waiting on it fails. Previously each retried on
its own. The next request still retries, and this is what removes the
1728 load blowup.
3. **`decodeParams` edge cases.** rou3 stores `undefined` for an
optional segment the request omitted, and a param named `__proto__`
needs `defineProperty` rather than assignment. Both are covered by
tests.
## Testing
Full suite green (2704 tests), plus bun (59) and cloudflare (26). Type
check and lint clean. Both matchers are at 100% statements, lines and
functions.
New tests cover concurrent matching on lazy and nested lazy routers,
deep lazy chains asserting each level loads exactly once, failed and
synchronously throwing loaders being retried, and the percent encoded
path that only unlocks a prefixed lazy router after normalisation. Each
new test was checked against the pre change code to confirm it actually
fails there.1 parent 53280cf commit cfe8951
7 files changed
Lines changed: 419 additions & 106 deletions
File tree
- packages
- cloudflare
- openapi/src/adapters/standard
- server/src
- adapters/standard
- playgrounds/cloudflare
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| |||
Lines changed: 132 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
127 | 127 | | |
128 | 128 | | |
129 | 129 | | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
130 | 143 | | |
131 | 144 | | |
132 | 145 | | |
| |||
237 | 250 | | |
238 | 251 | | |
239 | 252 | | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
240 | 313 | | |
241 | 314 | | |
242 | 315 | | |
| |||
272 | 345 | | |
273 | 346 | | |
274 | 347 | | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
275 | 407 | | |
276 | 408 | | |
277 | 409 | | |
| |||
Lines changed: 56 additions & 41 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
| 28 | + | |
| 29 | + | |
28 | 30 | | |
29 | 31 | | |
30 | 32 | | |
31 | 33 | | |
32 | 34 | | |
33 | 35 | | |
34 | 36 | | |
35 | | - | |
36 | | - | |
| 37 | + | |
37 | 38 | | |
38 | 39 | | |
39 | 40 | | |
| |||
60 | 61 | | |
61 | 62 | | |
62 | 63 | | |
63 | | - | |
| 64 | + | |
64 | 65 | | |
65 | 66 | | |
66 | | - | |
| 67 | + | |
67 | 68 | | |
68 | 69 | | |
69 | | - | |
70 | | - | |
| 70 | + | |
| 71 | + | |
71 | 72 | | |
72 | 73 | | |
73 | 74 | | |
| |||
101 | 102 | | |
102 | 103 | | |
103 | 104 | | |
104 | | - | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
105 | 112 | | |
106 | | - | |
| 113 | + | |
107 | 114 | | |
108 | 115 | | |
109 | 116 | | |
110 | 117 | | |
111 | | - | |
112 | | - | |
| 118 | + | |
113 | 119 | | |
114 | | - | |
115 | | - | |
116 | | - | |
117 | | - | |
118 | | - | |
119 | | - | |
120 | | - | |
121 | | - | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
122 | 125 | | |
123 | | - | |
| 126 | + | |
| 127 | + | |
124 | 128 | | |
125 | | - | |
| 129 | + | |
126 | 130 | | |
127 | 131 | | |
128 | 132 | | |
129 | | - | |
| 133 | + | |
130 | 134 | | |
131 | 135 | | |
132 | | - | |
133 | | - | |
| 136 | + | |
| 137 | + | |
134 | 138 | | |
135 | 139 | | |
136 | 140 | | |
137 | 141 | | |
138 | | - | |
139 | | - | |
140 | | - | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
141 | 147 | | |
| 148 | + | |
142 | 149 | | |
143 | | - | |
144 | | - | |
145 | | - | |
146 | | - | |
| 150 | + | |
147 | 151 | | |
148 | | - | |
149 | | - | |
150 | | - | |
151 | | - | |
152 | | - | |
153 | | - | |
| 152 | + | |
| 153 | + | |
154 | 154 | | |
155 | 155 | | |
156 | | - | |
157 | | - | |
158 | 156 | | |
159 | 157 | | |
160 | | - | |
161 | | - | |
162 | | - | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
163 | 164 | | |
164 | 165 | | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
165 | 180 | | |
166 | 181 | | |
167 | 182 | | |
| |||
0 commit comments