Repository navigation
Periodically find and queue missing compiler compatibility checks #804
Description
A package can finish publishing, but some of its compiler compatibility checks may never get queued. For example, the server might time out or restart while finding the checks to run. The package is available to users, but its list of supported compilers can remain incomplete indefinitely.
This happened with web-storage@5.0.1 in #800.
#801 makes follow-up scheduling faster and inserts jobs one at a time. That saves jobs already queued before an interruption, but it cannot recover checks it never reached. #803 improves solver performance, but faster solving alone does not repair missing checks.
So the basic problem remains: how do we detect and fix the unavoidable case where a compiler matrix job might not get queued for a published package? And how do we do it without introducing new state?
I think we can accomplish this with a regular, stateless sweep over the registry. The basic idea is to run a small repair command on a regular interval, for example through a systemd timer. Each run starts fresh and asks: Which published package versions still need compiler checks?
For each published package version and each supported compiler:
- If the package metadata already lists that exact compiler, skip it.
- If a matrix job already exists for that package version and compiler, skip it.
- Otherwise, try to find dependency versions that allow a build with that compiler.
- If a build plan is found, immediately insert a matrix job into the existing queue.
- If no plan is found, leave it alone and try again on a later sweep. A dependency may gain compiler support in the meantime.
The normal server executor still runs the compilation jobs and updates package metadata after successful compilation. The repair command does not compile packages or write compatibility metadata itself.
This survives interruptions itself. As a concrete example, suppose a sweep finds 100 missing checks and stops after queuing 40. The next sweep starts over, skips those 40 existing jobs, and finds the remaining checks. We do not need to remember where the previous sweep stopped.
This also finds old gaps, not just gaps caused by the latest publication or compiler release. It should examine all currently published versions, not only the newest version of each package.
The goal is eventual repair: as sweeps and compilation jobs complete, missing compatibility results are filled in wherever a build succeeds. A missing compiler entry is not proof that the package cannot compile with it, and a solved build plan is not proof of compatibility until compilation succeeds.
Side note: separating queueing from publication success
If publication finishes successfully, a later timeout while queuing compatibility checks currently makes the publish job fail. This is kind of misleading though; the compiler matrix is an eventually-correct best-effort thing, not a requirement for publishing. Compatibility scheduling should be outside the publication job's success/failure boundary and timeout. The periodic sweep can take responsibility for discovering that follow-up work.
The same principle applies to matrix jobs: once compilation succeeds and the compatibility metadata is successfully written/pushed, a later failure to schedule dependant checks should not turn that successful check into a failed job.
Activity
mikesol commentedon Oct 14, 2026
Thanks for writing this up! I think the stateless sweep is the right call. Two small things. First, could we say that a finished job counts as "already exists" in step 2, even if it failed? Otherwise, if I understand correctly, a package that fails to compile gets re-queued on every sweep. Second, packages with no build plan get re-solved on every sweep. My understanding is that #803 makes this cheap, but it'd still be good to measure a full sweep and adjust the interval accordingly. Nice work!
thomashoneyman commentedon Oct 14, 2026
Yeah, agreed on both. A job counts as “already exists” whether it’s pending, succeeded, or failed. The sweep would only add checks that never got a job in the first place (it won't retry failed compilations) — I can make that more explicit.
On the other hand, packages with no build plan indeed get re-solved on every sweep. I feel like that's the biggest weakness in this proposal, and I wasn't sure how bad that would be so I grabbed the current registry data and ran the equivalent of one of these sweeps over it. On a machine with a little (not much) more firepower than the registry machine, it took about 12 minutes to run and it repeated about 120,000 unsuccessful solves, so that feels like a dealbreaker.
I took a stab at an implementation so I could do that sweep and it feels pretty good minus the repeated solves. Two options come to mind:
Cacheeffect to put a hash of the solver inputs on disk for a given compatibility check, such that on future re-solves it's just loaded instead of actually solved. But this doesn't help with avoiding going through those 120k (and growing) entries, it just skips solving work altogether.This all assumes we're still happy considering no build plan to be a terminal outcome and we're not promising to keep re-checking all these packages as new dependency versions are published within their bounds.
thomashoneyman commentedon Oct 14, 2026
Well, I guess this has problems too -- we require jobs to have a build plan, and this wouldn't have one. So then we either relax that requirement to allow
null, wherenullmust always correspond with a failed job, or we decide to store this elsewhere.