← back to Dw Yolo Loop
scripts/queue-slo-snapshot/queue-slo-snapshot.mjs
96 lines
// queue-slo-snapshot — read-only SLO snapshot of the Shopify prod write queue
// (shopify_api_queue). Every DW prod write (product create/update/archive,
// GMC-exclude, April restore, draft reactivation, body-leak strip) drains
// through this queue via the shopify-queue-worker on Kamatera. Before Steve
// fires any ≤1k/day batched apply, he needs to know the live queue is healthy
// and not already backed up.
//
// ⚠️ STALENESS GATE (cycle-21 lesson: the instrument must detect its own
// blindness). The Mac2 mirror copy of shopify_api_queue is NOT in the 2-table
// logical-replication set (see memory dw-unified-kamatera-canonical), so on Mac2
// it is a FROZEN snapshot — empirically last_completed froze 2026-04-14. This
// script REFUSES to report SLO numbers as "live" when the newest completed_at is
// older than --max-stale-hours (default 6). On Mac2 it will (correctly) report
// STALE / UNRELIABLE. To get a real read, point DW_UNIFIED_URL at the canonical
// Kamatera DB (a read-only SELECT; prod-DB read is Steve-gated from Mac2).
//
// node queue-slo-snapshot.mjs [--max-stale-hours N]
// READ-ONLY. $0. No writes.
import { execFileSync } from 'node:child_process';
import fs from 'node:fs';
const PSQL = [ '/opt/homebrew/opt/postgresql@14/bin/psql', '/usr/local/opt/postgresql@14/bin/psql', 'psql' ]
.find(p => { try { execFileSync(p, ['--version'], { stdio: 'ignore' }); return true; } catch { return false; } }) || 'psql';
const DB = process.env.DW_UNIFIED_URL || 'postgresql:///dw_unified?host=/tmp';
const args = process.argv.slice(2);
const MAX_STALE_H = parseFloat(args.find((_,i,a)=>a[i-1]==='--max-stale-hours') || '6') || 6;
const today = new Date().toISOString().slice(0,10); // dynamic — a scheduled run must not clobber a prior day's report
const OUT = `${process.env.HOME}/.claude/yolo-queue/queue-slo-snapshot-${today}.json`;
const MD = `${process.env.HOME}/.claude/yolo-queue/queue-slo-snapshot-${today}.md`;
function q(sql) {
const out = execFileSync(PSQL, [DB, '-At', '-F', '|', '-c', sql], { encoding: 'utf8', maxBuffer: 64*1024*1024 });
return out.trim() ? out.trim().split('\n').map(r => r.split('|')) : [];
}
// freshness probe
const fresh = q(`select extract(epoch from (now()-max(completed_at)))/3600.0,
max(completed_at), max(created_at),
count(*) filter (where completed_at > now()-interval '24 hours')
from shopify_api_queue`)[0] || [];
const hoursSinceCompleted = fresh[0] === '' || fresh[0] == null ? null : parseFloat(fresh[0]);
const lastCompleted = fresh[1] || '(none)';
const lastCreated = fresh[2] || '(none)';
const tput24 = parseInt(fresh[3] || '0', 10);
const STALE = hoursSinceCompleted == null || hoursSinceCompleted > MAX_STALE_H;
const byStatus = q(`select status, count(*) from shopify_api_queue group by status order by 2 desc`);
const pendingAge = q(`select min(created_at), extract(epoch from (now()-min(created_at)))/3600.0
from shopify_api_queue where status in ('pending','queued','pending_product')`)[0] || [];
const errAgg = q(`select count(*) filter (where error is not null),
count(*) filter (where retries>0),
count(*) filter (where retries>=max_retries and max_retries>0)
from shopify_api_queue`)[0] || [];
const report = {
generated_at: new Date().toISOString(), db: DB.replace(/:[^:@/]*@/, ':***@'),
freshness: { hours_since_last_completed: hoursSinceCompleted, last_completed: lastCompleted,
last_created: lastCreated, throughput_24h: tput24, max_stale_hours: MAX_STALE_H, stale: STALE },
by_status: Object.fromEntries(byStatus.map(r => [r[0], parseInt(r[1],10)])),
oldest_pending: pendingAge[0] || null, oldest_pending_age_h: pendingAge[1] ? parseFloat(pendingAge[1]) : null,
errors: { with_error: parseInt(errAgg[0]||'0',10), retried: parseInt(errAgg[1]||'0',10), exhausted: parseInt(errAgg[2]||'0',10) },
};
fs.writeFileSync(OUT, JSON.stringify(report, null, 2));
let md = `# Shopify write-queue SLO snapshot — ${new Date().toISOString().slice(0,16)}\n\n`;
md += `DB: \`${report.db}\` · **READ-ONLY, $0.**\n\n`;
if (STALE) {
md += `## 🔴 STALE / UNRELIABLE — do NOT treat these numbers as live\n`;
md += `Newest \`completed_at\` is **${hoursSinceCompleted==null?'unknown':hoursSinceCompleted.toFixed(1)+'h'} old** (last: ${lastCompleted}), throughput-24h = **${tput24}**.\n`;
md += `This copy is not being drained — on Mac2 the queue table is a frozen snapshot (not in the 2-table logical-replication set, per \`dw-unified-kamatera-canonical\`). `;
md += `**A real SLO read must run against the canonical Kamatera DB.** Below is the frozen-snapshot content for reference only.\n\n`;
} else {
md += `## 🟢 LIVE — drained within ${MAX_STALE_H}h\nLast completed ${hoursSinceCompleted.toFixed(2)}h ago · throughput-24h = ${tput24}.\n\n`;
}
md += `**Status distribution:** ${byStatus.map(r=>`${r[0]}=${r[1]}`).join(' · ')}\n\n`;
md += `**Oldest pending:** ${report.oldest_pending||'—'} (${report.oldest_pending_age_h?report.oldest_pending_age_h.toFixed(1)+'h':'—'})\n\n`;
md += `**Errors:** with_error=${report.errors.with_error} · retried=${report.errors.retried} · retry-exhausted=${report.errors.exhausted}\n`;
fs.writeFileSync(MD, md);
console.log(`[queue-slo] ${STALE?'🔴 STALE':'🟢 LIVE'} · last_completed=${lastCompleted} · tput24=${tput24} · pending=${report.by_status.pending||0}`);
console.log(`Report: ${MD}`);
// Alert ONLY on a genuine LIVE backup/error — NEVER on the by-design Mac2 frozen-mirror
// STALE (that's expected here and would cry-wolf nightly). best-effort, never throws.
const liveBackup = !STALE && ((report.oldest_pending_age_h ?? 0) > 12 || report.errors.exhausted > 0);
if (liveBackup) {
try { const ac = new AbortController(); const t = setTimeout(()=>ac.abort(), 6000);
await fetch(`${process.env.CNCP_URL||'http://localhost:3333'}/api/parking-lot`,
{ method:'POST', headers:{'Content-Type':'application/json'},
body: JSON.stringify({ url:'file://queue-slo-snapshot',
note:`[QUEUE-SLO 🔴] LIVE write-queue backed up: oldest_pending=${report.oldest_pending_age_h?.toFixed(1)}h · retry-exhausted=${report.errors.exhausted} · pending=${report.by_status.pending||0}` }),
signal: ac.signal });
clearTimeout(t); } catch { /* dashboard down — report still written */ }
}
process.exit(STALE ? 2 : 0);