[object Object]

← back to Nationalrealestate

TK-5: fix Hennepin assemblage guard to be prod-scale safe (ANALYZE + PK-driven)

fcf4fb9fbd64b980b7d48c011f3d4b36c165ce71 · 2026-08-11 12:19:46 -0700 · Steve Abrams

The bulk-guard UPDATE hung ~4h on the 15M-row prod parcel table: after bulk-loading
447k rows, stale planner stats made the county_fips filter seq-scan the whole table
(same hazard wake_nc.ts documents). Fix: ANALYZE parcel before the guard so the
(county_fips,source_id) PK index range-scans just the county's rows, and drop the
intermediate MATERIALIZED CTE (removes a second full scan). Verified fast locally;
re-applying to prod Hennepin (parcels+events already loaded).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit fcf4fb9fbd64b980b7d48c011f3d4b36c165ce71
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Aug 11 12:19:46 2026 -0700

    TK-5: fix Hennepin assemblage guard to be prod-scale safe (ANALYZE + PK-driven)
    
    The bulk-guard UPDATE hung ~4h on the 15M-row prod parcel table: after bulk-loading
    447k rows, stale planner stats made the county_fips filter seq-scan the whole table
    (same hazard wake_nc.ts documents). Fix: ANALYZE parcel before the guard so the
    (county_fips,source_id) PK index range-scans just the county's rows, and drop the
    intermediate MATERIALIZED CTE (removes a second full scan). Verified fast locally;
    re-applying to prod Hennepin (parcels+events already loaded).
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 src/ingest/parcels/hennepin_mn.ts | 26 +++++++++++++++-----------
 1 file changed, 15 insertions(+), 11 deletions(-)

diff --git a/src/ingest/parcels/hennepin_mn.ts b/src/ingest/parcels/hennepin_mn.ts
index 533c68e..ef82d39 100644
--- a/src/ingest/parcels/hennepin_mn.ts
+++ b/src/ingest/parcels/hennepin_mn.ts
@@ -163,21 +163,25 @@ export async function ingestHennepin(opts: { maxPages?: number } = {}): Promise<
       // in-DB assemblage guard: this feed has no deed book/page, so a portfolio /
       // multi-parcel sale stamps the SAME (SALE_DATE, SALE_VALUE) onto every parcel
       // in the deal (verified: 25 Hennepin PINs share 2021-06 @ $2.3M). Without this
-      // each parcel would record the full price → N-fold inflated sales. Mirror the
-      // Wake guard: any exact (last_sale_date, last_sale_price) shared by >1 parcel
-      // is un-allocatable → null the price + flag bulk (pkey join keeps it indexed).
+      // each parcel would record the full price → N-fold inflated sales. Any exact
+      // (last_sale_date, last_sale_price) shared by >1 parcel is un-allocatable →
+      // null the price + flag bulk.
+      // ANALYZE first: after bulk-loading 447k rows into the multi-million-row prod
+      // parcel table, stale planner stats made the `county_fips=$1` filter SEQ-SCAN
+      // the whole table (a ~4h prod hang, 2026-08-11). Fresh stats let the
+      // (county_fips, source_id) PK index range-scan just this county's rows, and
+      // dropping the intermediate CTE removes a second full scan.
+      await query(`ANALYZE parcel`);
       const b = await query(
-        `WITH grp AS MATERIALIZED (
+        `WITH grp AS (
            SELECT last_sale_date, last_sale_price, COUNT(*)::int AS cnt
              FROM parcel WHERE county_fips=$1 AND last_sale_price IS NOT NULL AND last_sale_date IS NOT NULL
-            GROUP BY 1,2 HAVING COUNT(*) > 1),
-         targets AS MATERIALIZED (
-           SELECT p.source_id, g.cnt FROM parcel p
-             JOIN grp g ON p.last_sale_date=g.last_sale_date AND p.last_sale_price=g.last_sale_price
-            WHERE p.county_fips=$1)
+            GROUP BY 1,2 HAVING COUNT(*) > 1)
          UPDATE parcel p SET last_sale_price = NULL,
-           extra = p.extra || jsonb_build_object('bulk_sale', true, 'bulk_parcel_count', t.cnt)
-           FROM targets t WHERE p.county_fips=$1 AND p.source_id = t.source_id RETURNING 1`, [FIPS]);
+           extra = p.extra || jsonb_build_object('bulk_sale', true, 'bulk_parcel_count', grp.cnt)
+           FROM grp WHERE p.county_fips=$1
+             AND p.last_sale_date = grp.last_sale_date AND p.last_sale_price = grp.last_sale_price
+           RETURNING 1`, [FIPS]);
       bulk = b.rowCount ?? 0;
       const n = await registerParcelSource(FIPS, LAYER,
         `Hennepin County MN (Minneapolis) via MN Met Council Metropolitan 7-County Parcels aggregate (CO_NAME='HENNEPIN' filter) — ${upserted} parcels: owner (100%), situs address + city + zip, EMV land+bldg+TOTAL (estimated MARKET value, ~95%), year built (~94%), units, use class, priced last-sale (SALE_VALUE + SALE_DATE, ~87%) → ${sales} parcel_event sale rows. sqft (FIN_SQ_FT) not populated for Hennepin in this aggregate. No beds/baths or zoning. ${bulk} multi-parcel (assemblage/portfolio) sales flagged bulk + price nulled.`);

← c112f07 TK-5: add 2 metro parcel adapters — Hennepin MN (Minneapolis  ·  back to Nationalrealestate  ·  usre: add commercial deals feed + per-deal detail API over r 41cb360 →