test(build): the maxConcurrent-1 test no longer bets on which thread wins #60

Merged
mi merged 1 commits from flaky-maxconcurrent-gate into main 2026-09-13 11:54:27 +02:00
Owner

"with maxConcurrent 1 a second branch stays PENDING until the first
finished" failed on CI on a branch that does not touch BuildExecutor, and
the message named the cause: expected: but was:. Not
merely started -- already finished.

The test started two builds of two branches back to back and assumed the
first one started would get the single slot. Nothing provides that. Each
branch has a worker thread of its own, both call slot.acquire() inside
execute, and RUNNING is recorded only afterwards; which thread arrives
first is the scheduler's business. Semaphore fairness orders threads
already waiting, not two threads racing to arrive. So branch-b took the
slot, ran echo ok to the end, released it, branch-a went RUNNING -- the
state the test was waiting for -- and the next line found branch-b
finished. Starting the two in the other order reproduces it every time.

The gate file from 9221550 could never cover this: it holds branch-a only
once branch-a already has the slot.

branch-b is now enqueued after branch-a is observed RUNNING, which is
proof the slot is taken, and the gate keeps it taken. No window is left,
and nothing is assumed about scheduling. It is also the case the test is
named after and the one that happens in production: a push arriving while
another branch builds. The transitions assertion at the end, which is the
real requirement, is unchanged.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com

"with maxConcurrent 1 a second branch stays PENDING until the first finished" failed on CI on a branch that does not touch BuildExecutor, and the message named the cause: expected:<PENDING> but was:<SUCCESS>. Not merely started -- already finished. The test started two builds of two branches back to back and assumed the first one started would get the single slot. Nothing provides that. Each branch has a worker thread of its own, both call slot.acquire() inside execute, and RUNNING is recorded only afterwards; which thread arrives first is the scheduler's business. Semaphore fairness orders threads already waiting, not two threads racing to arrive. So branch-b took the slot, ran `echo ok` to the end, released it, branch-a went RUNNING -- the state the test was waiting for -- and the next line found branch-b finished. Starting the two in the other order reproduces it every time. The gate file from 9221550 could never cover this: it holds branch-a only once branch-a already has the slot. branch-b is now enqueued after branch-a is observed RUNNING, which is proof the slot is taken, and the gate keeps it taken. No window is left, and nothing is assumed about scheduling. It is also the case the test is named after and the one that happens in production: a push arriving while another branch builds. The transitions assertion at the end, which is the real requirement, is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
mi added 1 commit 2026-09-13 11:50:53 +02:00
"with maxConcurrent 1 a second branch stays PENDING until the first
finished" failed on CI on a branch that does not touch BuildExecutor, and
the message named the cause: expected:<PENDING> but was:<SUCCESS>. Not
merely started -- already finished.

The test started two builds of two branches back to back and assumed the
first one started would get the single slot. Nothing provides that. Each
branch has a worker thread of its own, both call slot.acquire() inside
execute, and RUNNING is recorded only afterwards; which thread arrives
first is the scheduler's business. Semaphore fairness orders threads
already waiting, not two threads racing to arrive. So branch-b took the
slot, ran `echo ok` to the end, released it, branch-a went RUNNING -- the
state the test was waiting for -- and the next line found branch-b
finished. Starting the two in the other order reproduces it every time.

The gate file from 9221550 could never cover this: it holds branch-a only
once branch-a already has the slot.

branch-b is now enqueued after branch-a is observed RUNNING, which is
proof the slot is taken, and the gate keeps it taken. No window is left,
and nothing is assumed about scheduling. It is also the case the test is
named after and the one that happens in production: a push arriving while
another branch builds. The transitions assertion at the end, which is the
real requirement, is unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
mi force-pushed flaky-maxconcurrent-gate from d4d83b7d2a to 098e564979 2026-09-13 11:50:53 +02:00 Compare
mi merged commit fcd11d960e into main 2026-09-13 11:54:27 +02:00
mi deleted branch flaky-maxconcurrent-gate 2026-09-13 11:54:27 +02:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mi/werkator#60