coordinator:status: the capture windows are priorities 0–2, the probe backlog from the planner's estimates
The sampling ranges in other years are capture too, at history priorities: they made a band each. The database size walked the HDD partitions for minutes and the probe counts took 10 s on 11 M rows: dropped, and the partial indexes' reltuples instead. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
680751e26a
commit
c18c320927
1 file changed
+13
-10
+13
-10
@@ -42,7 +42,6 @@ export interface Status {
|
|||||||
gb: number
|
gb: number
|
||||||
capGb: number
|
capGb: number
|
||||||
full: boolean
|
full: boolean
|
||||||
databaseGb: number
|
|
||||||
freeGb: number
|
freeGb: number
|
||||||
}
|
}
|
||||||
bands: RangeBand[]
|
bands: RangeBand[]
|
||||||
@@ -58,6 +57,7 @@ export interface Status {
|
|||||||
held: HeldGroup[]
|
held: HeldGroup[]
|
||||||
snapshots: { count: number; gb: number; oldestHours: number | null; largest: SnapshotFile[] }
|
snapshots: { count: number; gb: number; oldestHours: number | null; largest: SnapshotFile[] }
|
||||||
releases: { estimated: number; provisional: number }
|
releases: { estimated: number; provisional: number }
|
||||||
|
/** `ready` and `waiting` are the planner's estimates (the last analyze), the others counts. */
|
||||||
probes: { ready: number; waiting: number; pendingWaiting: number; pendingNamed: number }
|
probes: { ready: number; waiting: number; pendingWaiting: number; pendingNamed: number }
|
||||||
judge: { dirty: number }
|
judge: { dirty: number }
|
||||||
nodes: { name: string; lastSeenSeconds: number | null }[]
|
nodes: { name: string; lastSeenSeconds: number | null }[]
|
||||||
@@ -76,20 +76,22 @@ class StatusService {
|
|||||||
async collect(): Promise<Status> {
|
async collect(): Promise<Status> {
|
||||||
const tail = coordinatorConfig.tail.priority
|
const tail = coordinatorConfig.tail.priority
|
||||||
const cap = coordinatorConfig.catalogMaxNvmeGb
|
const cap = coordinatorConfig.catalogMaxNvmeGb
|
||||||
|
// The catalog's tablespace only (CatalogSpace measures it every minute anyway): pg_database_size walks the
|
||||||
|
// chunk partitions and the probe cache on the HDD, minutes on homelab.
|
||||||
const { rows: space } = await db.rawQuery(
|
const { rows: space } = await db.rawQuery(
|
||||||
`SELECT coalesce(nullif(current_setting('default_tablespace'), ''), 'pg_default') AS tablespace,
|
`SELECT coalesce(nullif(current_setting('default_tablespace'), ''), 'pg_default') AS tablespace,
|
||||||
pg_tablespace_size(coalesce(nullif(current_setting('default_tablespace'), ''), 'pg_default')) AS bytes,
|
pg_tablespace_size(coalesce(nullif(current_setting('default_tablespace'), ''), 'pg_default')) AS bytes`
|
||||||
pg_database_size(current_database()) AS database_bytes`
|
|
||||||
)
|
)
|
||||||
const tablespaceGb = Number(space[0].bytes) / 1e9
|
const tablespaceGb = Number(space[0].bytes) / 1e9
|
||||||
const fs = await statfs('/').catch(() => null)
|
const fs = await statfs('/').catch(() => null)
|
||||||
const freeGb = fs ? (fs.bavail * fs.bsize) / 1e9 : 0
|
const freeGb = fs ? (fs.bavail * fs.bsize) / 1e9 : 0
|
||||||
|
|
||||||
// The ranges by band: the tail, the capture windows by priority, the history by its thousand.
|
// The ranges by band: the tail, the capture windows (priorities 0–2; the sampling ranges in other years are
|
||||||
|
// capture too, at their history priorities), the history by its thousand.
|
||||||
const { rows: bands } = await db.rawQuery(
|
const { rows: bands } = await db.rawQuery(
|
||||||
`SELECT CASE WHEN priority <= ? THEN 'tail'
|
`SELECT CASE WHEN priority <= ? THEN 'tail'
|
||||||
WHEN priority < 0 THEN 'raised'
|
WHEN priority < 0 THEN 'raised'
|
||||||
WHEN capture THEN 'window ' || priority
|
WHEN capture AND priority <= 2 THEN 'window ' || priority
|
||||||
WHEN priority < 1000 THEN 'history 0-999'
|
WHEN priority < 1000 THEN 'history 0-999'
|
||||||
ELSE 'history ' || (priority / 1000) * 1000 || '+' END AS band,
|
ELSE 'history ' || (priority / 1000) * 1000 || '+' END AS band,
|
||||||
count(*) FILTER (WHERE status = 'planned') AS planned,
|
count(*) FILTER (WHERE status = 'planned') AS planned,
|
||||||
@@ -149,11 +151,13 @@ class StatusService {
|
|||||||
}
|
}
|
||||||
files.sort((a, b) => b.mb - a.mb)
|
files.sort((a, b) => b.mb - a.mb)
|
||||||
|
|
||||||
|
// The probe backlog from the planner's estimates of the partial indexes (counting 11 M rows took 10 s on
|
||||||
|
// homelab); the rest are small partial indexes.
|
||||||
const { rows: releases } = await db.rawQuery(
|
const { rows: releases } = await db.rawQuery(
|
||||||
`SELECT (SELECT reltuples::bigint FROM pg_class WHERE oid = 'releases'::regclass) AS estimated,
|
`SELECT (SELECT reltuples::bigint FROM pg_class WHERE oid = 'releases'::regclass) AS estimated,
|
||||||
(SELECT count(*) FROM releases WHERE settled_at IS NULL) AS provisional,
|
(SELECT count(*) FROM releases WHERE settled_at IS NULL) AS provisional,
|
||||||
(SELECT count(*) FROM probe_candidates WHERE status = 'waiting' AND ready) AS ready,
|
(SELECT greatest(0, reltuples)::bigint FROM pg_class WHERE oid = 'probe_candidates_ready_idx'::regclass) AS ready,
|
||||||
(SELECT count(*) FROM probe_candidates WHERE status = 'waiting') AS waiting,
|
(SELECT greatest(0, reltuples)::bigint FROM pg_class WHERE oid = 'probe_candidates_waiting_idx'::regclass) AS waiting,
|
||||||
(SELECT count(*) FROM pending_releases WHERE status = 0) AS pending_waiting,
|
(SELECT count(*) FROM pending_releases WHERE status = 0) AS pending_waiting,
|
||||||
(SELECT count(*) FROM pending_releases WHERE status = 4) AS pending_named,
|
(SELECT count(*) FROM pending_releases WHERE status = 4) AS pending_named,
|
||||||
(SELECT count(*) FROM judge_dirty) AS dirty`
|
(SELECT count(*) FROM judge_dirty) AS dirty`
|
||||||
@@ -168,7 +172,6 @@ class StatusService {
|
|||||||
gb: tablespaceGb,
|
gb: tablespaceGb,
|
||||||
capGb: cap,
|
capGb: cap,
|
||||||
full: cap > 0 && tablespaceGb > cap,
|
full: cap > 0 && tablespaceGb > cap,
|
||||||
databaseGb: Number(space[0].database_bytes) / 1e9,
|
|
||||||
freeGb,
|
freeGb,
|
||||||
},
|
},
|
||||||
bands: bands.map((b: any) => ({
|
bands: bands.map((b: any) => ({
|
||||||
@@ -255,7 +258,7 @@ class StatusService {
|
|||||||
lines.push(`Status at ${s.at}`)
|
lines.push(`Status at ${s.at}`)
|
||||||
lines.push('')
|
lines.push('')
|
||||||
lines.push(
|
lines.push(
|
||||||
`Catalog: ${s.catalog.gb.toFixed(1)} GB in tablespace ${s.catalog.tablespace}${s.catalog.capGb ? ` of ${s.catalog.capGb} GB${s.catalog.full ? ' (FULL: history paused)' : ''}` : ' (no cap)'}; database ${s.catalog.databaseGb.toFixed(1)} GB; ${s.catalog.freeGb.toFixed(1)} GB free on /`
|
`Catalog: ${s.catalog.gb.toFixed(1)} GB in tablespace ${s.catalog.tablespace}${s.catalog.capGb ? ` of ${s.catalog.capGb} GB${s.catalog.full ? ' (FULL: history paused)' : ''}` : ' (no cap)'}; ${s.catalog.freeGb.toFixed(1)} GB free on /`
|
||||||
)
|
)
|
||||||
lines.push(
|
lines.push(
|
||||||
`Last hour: ${b(s.lastHour.indexedArticles)} articles indexed in ${n(s.lastHour.doneRanges)} ranges (${n(s.lastHour.indexedArticles / 3600)}/s); corpus fill ${b(s.lastHour.storedArticles)} in ${n(s.lastHour.storedRanges)} ranges, ${n(s.corpus.pendingRanges)} capture ranges (${b(s.corpus.pendingArticles)}) still to store`
|
`Last hour: ${b(s.lastHour.indexedArticles)} articles indexed in ${n(s.lastHour.doneRanges)} ranges (${n(s.lastHour.indexedArticles / 3600)}/s); corpus fill ${b(s.lastHour.storedArticles)} in ${n(s.lastHour.storedRanges)} ranges, ${n(s.corpus.pendingRanges)} capture ranges (${b(s.corpus.pendingArticles)}) still to store`
|
||||||
@@ -321,7 +324,7 @@ class StatusService {
|
|||||||
`Releases: ~${n(s.releases.estimated)}, ${n(s.releases.provisional)} provisional. Judge: ${n(s.judge.dirty)} dirty`
|
`Releases: ~${n(s.releases.estimated)}, ${n(s.releases.provisional)} provisional. Judge: ${n(s.judge.dirty)} dirty`
|
||||||
)
|
)
|
||||||
lines.push(
|
lines.push(
|
||||||
`Probes: ${n(s.probes.ready)} ready of ${n(s.probes.waiting)} waiting; pending ladder ${n(s.probes.pendingWaiting)} waiting, ${n(s.probes.pendingNamed)} named`
|
`Probes: ~${n(s.probes.ready)} ready of ~${n(s.probes.waiting)} waiting; pending ladder ${n(s.probes.pendingWaiting)} waiting, ${n(s.probes.pendingNamed)} named`
|
||||||
)
|
)
|
||||||
lines.push(
|
lines.push(
|
||||||
`Nodes: ${s.nodes.map((x) => `${x.name} (${age(x.lastSeenSeconds)})`).join(', ') || 'none'}`
|
`Nodes: ${s.nodes.map((x) => `${x.name} (${age(x.lastSeenSeconds)})`).join(', ') || 'none'}`
|
||||||
|
|||||||
Reference in new issue
Block a user