~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/knotless

git that lives in your Atmosphere account

knotless.dev
1 collaborator

knotless 0.13.0 Latest 2 packages, 1 image

1
10

just queue-stats counts the ejections its runners write

Merged chadtmiller.com asked to merge into main from chadtmiller.com/knotless:harness/queue-stats-ejections
docs/design/merge-queue.md+4−4
@@ -565,15 +565,15 @@ keeper runs no model.
## Measuring it
`just queue-stats` says how the queue did, by day (UTC), from what it
already writes: the queue records in the owner's PDS, the batch checks and
their logs, and main's log for when each pull landed.
already writes: the queue records in the owner's PDS and its runners', the
batch checks and their logs, and main's log for when each pull landed.
- **queued** and **landed**: queue records written, and pulls landed.
- **queued → landed**: minutes from a pull's first queue record to its
commit on main, past any ejections, at the median and the 95th
percentile.
- **ejected**: ejections over queue records, and by kind: a conflict, a
failed batch, a branch gone, or other.
- **ejected**: ejections over queue records, and by kind: a failed
pre-flight, a review, a conflict, a failed batch, a branch gone, or other.
- **batches passed**: of the batches that ended, those that passed.
- **batch test**: minutes of a batch's `test` run, the longest of its six.
- **cancelled checks**, by why: a runner restart, a batch dropped as the
harness/queue-stats.mjs+28−11
@@ -5,7 +5,8 @@
// node harness/queue-stats.mjs [<handle>/<name>] [--days <n>] [--json]
//
// It reads what the queue already writes: the queue records in the
// owner's PDS, the batch checks the runners file (through `knotless
// owner's PDS and its runners' (a pull is queued by its owner and ejected
// by a runner), the batch checks the runners file (through `knotless
// checks --queue`, so the CLI must be logged in or the repository
// public), their logs, and main's log in this checkout for when each pull
// landed (`git fetch` first). The runner prunes batch checks it no longer
@@ -117,8 +118,19 @@ for (const c of listed) {
// Queue records: when each pull was queued, and each ejection and why.
const owner = listed[0]?.repo.uri.split('/')[2]
const queue = owner ? await records(owner, 'dev.knotless.queue') : []
for (const r of queue) saved.queue = { ...saved.queue, [r.uri]: r.value }
const name = repo.split('/')[1]
const repoUri = owner && `at://${owner}/dev.knotless.repo/${name}`
const repoRecord = async () => {
const q = new URLSearchParams({ repo: owner, collection: 'dev.knotless.repo', rkey: name })
const res = await get(`${await pds(owner)}/xrpc/com.atproto.repo.getRecord?${q}`)
return (await res.json()).value
}
const runners = owner ? ((await repoRecord()).runners ?? []) : []
for (const did of owner ? [owner, ...runners.map((r) => r.did)] : []) {
for (const r of await records(did, 'dev.knotless.queue')) {
saved.queue = { ...saved.queue, [r.uri]: r.value }
}
}
// Landed: the squashed commits on main, each naming its pull.
const landed = {}
@@ -148,13 +160,17 @@ const pct = (q) => (xs) =>
xs.length ? [...xs].sort((a, b) => a - b)[Math.ceil(xs.length * q) - 1] : null
const p95 = pct(0.95)
const kind = (reason = '') =>
reason.includes('conflicts')
? 'conflict'
: reason.includes('failed its checks')
? 'failed batch'
: reason.includes('has no branch')
? 'branch gone'
: 'other'
reason.includes('pre-flight')
? 'failed pre-flight'
: reason.includes('not reviewed') || reason.includes('changes asked')
? 'review'
: reason.includes('conflicts')
? 'conflict'
: reason.includes('failed its checks')
? 'failed batch'
: reason.includes('has no branch')
? 'branch gone'
: 'other'
const cancel = (why = '') =>
why.includes('runner stopped')
? 'runner restart'
@@ -182,8 +198,9 @@ const at = (d) =>
// Each landed pull: from the first time it was queued for this landing,
// past any ejections, to the commit on main.
// A runner serves other repositories too, and the owner has others.
const queueRecords = Object.values(saved.queue ?? {}).filter(
(v) => Date.parse(v.createdAt) >= since,
(v) => Date.parse(v.createdAt) >= since && (!repoUri || v.repo?.uri === repoUri),
)
for (const [pull, { at: when }] of Object.entries(landed)) {
const mine = queueRecords