[object Object]

← back to Ventura Corridor

TK-12208: skip 404 lottery query when magazine_features absent (to_regclass probe, 10-min cache) — stops pg log flood on prod

8822e43b653b9bfadc9d3ed6a5f4d10d41c40750 · 2026-09-25 13:20:52 -0700 · Steve Abrams

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D7LB6rCwpA7ubJTqYzqXEX

Files touched

Diff

commit 8822e43b653b9bfadc9d3ed6a5f4d10d41c40750
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Fri Sep 25 13:20:52 2026 -0700

    TK-12208: skip 404 lottery query when magazine_features absent (to_regclass probe, 10-min cache) — stops pg log flood on prod
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01D7LB6rCwpA7ubJTqYzqXEX
---
 src/server/index.ts | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/src/server/index.ts b/src/server/index.ts
index 9712f2f..6e1154f 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -6156,12 +6156,27 @@ ${today ? `<div class="player">
 // ─── Static viewer ─────────────────────────────────────────────────────
 app.use('/', express.static(path.resolve(__dirname, '../../public')));
 
+// Prod DB may lack the magazine migrations (016+). Probe with to_regclass (never errors) instead of
+// letting the 404 lottery query fail — a swallowed error still writes a pg log line per bot 404.
+const MAGAZINE_PROBE_TTL_MS = 10 * 60 * 1000;
+let magazineProbe: { ok: boolean; at: number } | null = null;
+async function magazineTableExists(): Promise<boolean> {
+  if (magazineProbe && Date.now() - magazineProbe.at < MAGAZINE_PROBE_TTL_MS) return magazineProbe.ok;
+  try {
+    const r = await query(`SELECT to_regclass('magazine_features') IS NOT NULL AS ok`);
+    magazineProbe = { ok: !!r.rows[0]?.ok, at: Date.now() };
+  } catch {
+    magazineProbe = { ok: false, at: Date.now() };
+  }
+  return magazineProbe.ok;
+}
+
 // Friendly 404 — magazine-flavored, suggests likely routes + a "lottery" pick
 app.use(async (req, res) => {
   if (req.accepts('html')) {
     const escapeHtml = (s: any) => String(s ?? '').replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]!));
     let lottery: { id: number; headline: string; subhead: string; biz: string } | null = null;
-    try {
+    if (await magazineTableExists()) try {
       const r = await query(
         `SELECT mf.id, mf.headline, mf.subhead, b.name AS biz
          FROM magazine_features mf JOIN businesses b ON b.id = mf.business_id

← eacc15b security: add rel=noopener noreferrer to target=_blank links  ·  back to Ventura Corridor  ·  (newest)