[object Object]

← back to Nationalrealestate

Loop: reserve >=1 slot/tick for fast-consumer streams (homes/businesses/firm) so parcel backfills stop starving them (USRE_CONSUMER_SLOTS, default 1)

908d6036e5ea26ac9474e97f1b9d86a22f09f1f7 · 2026-07-30 18:34:43 -0700 · steve@designerwallcoverings.com

Files touched

Diff

commit 908d6036e5ea26ac9474e97f1b9d86a22f09f1f7
Author: steve@designerwallcoverings.com <steve@designerwallcoverings.com>
Date:   Thu Jul 30 18:34:43 2026 -0700

    Loop: reserve >=1 slot/tick for fast-consumer streams (homes/businesses/firm) so parcel backfills stop starving them (USRE_CONSUMER_SLOTS, default 1)
---
 src/jobs/hourly_loop.ts | 21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/src/jobs/hourly_loop.ts b/src/jobs/hourly_loop.ts
index 2616f16..a37701b 100644
--- a/src/jobs/hourly_loop.ts
+++ b/src/jobs/hourly_loop.ts
@@ -25,6 +25,12 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
 const ROOT = join(__dirname, '..', '..');
 const DRY = process.argv.includes('--dry');
 const MAX_JOBS_PER_TICK = Number(process.env.USRE_MAX_JOBS_PER_TICK || 2);
+// Fast-consumer streams (homes + businesses + firm enrichment) that the huge
+// paginating parcel/deed backfills would otherwise starve out of every slot.
+// Reserve >=1 slot/tick for the stalest eligible consumer so they get a
+// guaranteed cadence. Env-tunable (0 = old global-stalest behavior).
+const CONSUMER = new Set(['listings', 'places-seed', 'firm-crawl', 'discover']);
+const CONSUMER_SLOTS = Number(process.env.USRE_CONSUMER_SLOTS || 1);
 
 type Job = {
   name: string;
@@ -99,7 +105,20 @@ async function main() {
     console.log(`   ${mark} ${s.job.name.padEnd(12)} [${s.health.state.padEnd(11)}] age=${a.padStart(6)}  ${s.health.reason}`);
   }
 
-  const pick = plan.filter(s => s.eligible).slice(0, MAX_JOBS_PER_TICK);
+  // Pool-aware pick: reserve up to CONSUMER_SLOTS for the stalest eligible
+  // fast-consumer jobs (homes/businesses/firm) so paginating parcel/deed backfills
+  // can't monopolize every slot; fill the rest with the global stalest. `plan` is
+  // already sorted stalest/most-boosted first, so first-match = stalest.
+  const eligible = plan.filter(s => s.eligible);
+  const pick: typeof eligible = [];
+  for (const s of eligible) {
+    if (pick.length >= CONSUMER_SLOTS) break;
+    if (CONSUMER.has(s.job.name)) pick.push(s);
+  }
+  for (const s of eligible) {
+    if (pick.length >= MAX_JOBS_PER_TICK) break;
+    if (!pick.includes(s)) pick.push(s);
+  }
   if (!pick.length) { console.log('[loop] nothing eligible — idle tick (all fresh, blocked, or quarantined)'); await pool.end(); return; }
 
   await pool.end(); // children own their own pools

← 82774fe Make listings CAP env-tunable (USRE_LISTINGS_CAP, default 10  ·  back to Nationalrealestate  ·  discover: fail-soft run recording (try/catch closeRun) so se 0321546 →