Commit c4ab2bb
authored
fix: address three remaining flakes from CI Stress run #25511463854 (#196)
After PR #193 dropped the per-shard failure rate from 32% to 7.5%,
three flakes remained. Investigation produced one production fix,
one fixture-side race fix, and a more honest test of the render
coalescing invariant.
Validation regex timeout — production fix
─────────────────────────────────────────
ValidationIntegrationTests.Full_Pipeline_Valid_State failed because
Validate.Email's MatchValidator capped regex execution at 200ms wall
clock. Under threadpool starvation on a CI runner, the timer that
RegexMatchTimeout uses can fire mid-match even though CPU time was
microseconds, causing IsMatch to throw RegexMatchTimeoutException
and the validator to mark the legitimate "user@example.com" as
INVALID. Bump validator regex timeout to 1s. Diagnostic-path
timeouts (DevtoolsPropertyTools, LogCaptureBuffer, DevtoolsUiaTools)
keep their 200ms caps — they're admin/dev-only, not on the
keystroke path. False-invalid validation on legitimate input is
worse UX than slower tarpit detection on adversarial input, since
the former affects every user under any CI-loaded environment.
AsyncResource.Framerate.DepsThrashing — fixture race fix
─────────────────────────────────────────────────────────
DepsThrashing_AtMostOneCompleted asserted completed<=1; observed
completed=2. The fetcher's race window: `await Task.Delay(200, ct)`
followed unconditionally by `Interlocked.Increment(ref completed)`.
When deps change exactly as the 200ms timer fires, Task.Delay's
internal atomic result-set can be won by the timer before
cancellation lands — the await resumes normally and the increment
runs for a fetch the production hook will correctly discard. Add
`ct.ThrowIfCancellationRequested()` after the await so the fixture's
counter accurately reflects fetches that ran to completion versus
those that completed-then-got-discarded.
ThreadSafe_RenderCoalescing — split into honest + strict variants
──────────────────────────────────────────────────────────────────
The threshold "1000 setStates → fewer than 200 renders" was too
aggressive for the algorithm's actual semantics. Trace: the CAS
gate in RequestRender promises "at most one RenderLoop pending" —
not "many calls → ~1 render" — when the producer (Task.Run) runs
in parallel with an idle UI thread that drains RenderLoop between
each setState. Observed 436/1000 and 456/1000 = ~45% coalescing,
right in the alternation-dominant regime.
Two changes:
1) Existing background-thread fixture: relax threshold from
`renders<200` to `renders<writes` (i.e., <1000). This proves
*some* coalescing happened and catches a totally-broken-gate
regression (which would give renders==writes), but does not
over-claim a coalescing ratio the algorithm doesn't promise.
Comment updated to reference the strict-coalescing sibling.
2) New ThreadSafe_RenderCoalescingDispatcherBatch fixture: fires
1000 setStates from inside a single DispatcherQueue.TryEnqueue
block on the UI thread. Because the UI thread is busy executing
the loop, RenderLoop cannot drain between calls — every setState
after the first finds the CAS gate held and is coalesced. This
is the actual invariant the production gate is designed for, and
should hold deterministically: renders<=5 (expected ~2).1 parent 9096ae2 commit c4ab2bb
4 files changed
Lines changed: 98 additions & 15 deletions
File tree
- src/Reactor/Controls/Validation/Validators
- tests/Reactor.AppTests.Host/SelfTest
- Fixtures
Lines changed: 9 additions & 5 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
157 | 157 | | |
158 | 158 | | |
159 | 159 | | |
160 | | - | |
161 | | - | |
162 | | - | |
163 | | - | |
164 | | - | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
165 | 169 | | |
166 | 170 | | |
167 | 171 | | |
| |||
Lines changed: 9 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
72 | 72 | | |
73 | 73 | | |
74 | 74 | | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
75 | 84 | | |
76 | 85 | | |
77 | 86 | | |
| |||
Lines changed: 78 additions & 10 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
1 | 2 | | |
2 | 3 | | |
3 | 4 | | |
| |||
260 | 261 | | |
261 | 262 | | |
262 | 263 | | |
263 | | - | |
264 | | - | |
265 | | - | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
266 | 272 | | |
267 | 273 | | |
268 | 274 | | |
| |||
287 | 293 | | |
288 | 294 | | |
289 | 295 | | |
| 296 | + | |
290 | 297 | | |
291 | 298 | | |
292 | | - | |
| 299 | + | |
293 | 300 | | |
294 | 301 | | |
295 | 302 | | |
296 | 303 | | |
297 | 304 | | |
298 | 305 | | |
299 | 306 | | |
300 | | - | |
301 | | - | |
302 | | - | |
303 | | - | |
304 | | - | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
305 | 312 | | |
306 | 313 | | |
307 | 314 | | |
308 | 315 | | |
309 | 316 | | |
310 | | - | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 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 | + | |
311 | 379 | | |
312 | 380 | | |
313 | 381 | | |
| |||
Lines changed: 2 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
272 | 272 | | |
273 | 273 | | |
274 | 274 | | |
| 275 | + | |
275 | 276 | | |
276 | 277 | | |
277 | 278 | | |
| |||
1038 | 1039 | | |
1039 | 1040 | | |
1040 | 1041 | | |
| 1042 | + | |
1041 | 1043 | | |
1042 | 1044 | | |
1043 | 1045 | | |
| |||
0 commit comments