fix: don't poison shard cache on failure; require real draws for ready status
Two Important review findings on useDrawDistribution: - ensureShardRegistered cached the rejected promise on fetch/registration failure, permanently stuck for that (model,year,state) key until a full page reload. Now deletes the cache entry on failure so the next caller retries fresh. - status could become 'ready' even when the LEAID-bound query returned zero usable rows (district not present in the draws shard), silently mislabeling a fitSkewedInterval-only render as real posterior draws. Now requires at least one group with actual draws before reporting 'ready'.
This commit is contained in:
@@ -19,6 +19,7 @@ function ensureShardRegistered(db, model, year, state) {
|
|||||||
shardCache.set(
|
shardCache.set(
|
||||||
key,
|
key,
|
||||||
(async () => {
|
(async () => {
|
||||||
|
try {
|
||||||
const url = `${HF_BASE}/model_id=${model}/YEAR=${year}/LEA_STATE=${state}/data_0.parquet`
|
const url = `${HF_BASE}/model_id=${model}/YEAR=${year}/LEA_STATE=${state}/data_0.parquet`
|
||||||
const res = await fetch(url)
|
const res = await fetch(url)
|
||||||
if (!res.ok) throw new Error(`Failed to fetch draw shard: HTTP ${res.status}`)
|
if (!res.ok) throw new Error(`Failed to fetch draw shard: HTTP ${res.status}`)
|
||||||
@@ -26,6 +27,13 @@ function ensureShardRegistered(db, model, year, state) {
|
|||||||
const fileName = `${key}.parquet`
|
const fileName = `${key}.parquet`
|
||||||
await db.registerFileBuffer(fileName, buffer)
|
await db.registerFileBuffer(fileName, buffer)
|
||||||
return fileName
|
return fileName
|
||||||
|
} catch (err) {
|
||||||
|
// Don't let a transient failure (network blip, HF outage) poison the
|
||||||
|
// cache forever — remove the rejected entry so the next caller for
|
||||||
|
// this shard gets a fresh attempt instead of the same dead promise.
|
||||||
|
shardCache.delete(key)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
})(),
|
})(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -79,9 +87,22 @@ export function useDrawDistribution({ leaid, state, model, year, groups }) {
|
|||||||
;(byGroup[key] ??= []).push(rate)
|
;(byGroup[key] ??= []).push(rate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A successful query with zero matching rows (this district isn't in
|
||||||
|
// the draws shard, or none of its rows matched a known group) is not
|
||||||
|
// "ready" — there's no real data to show, so treat it like a failure
|
||||||
|
// and let the caller fall back, rather than silently claiming real
|
||||||
|
// draws while every group actually uses the fitted approximation.
|
||||||
|
const hasDraws = Object.values(byGroup).some((draws) => draws.length > 0)
|
||||||
|
|
||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
|
if (hasDraws) {
|
||||||
setDrawsByGroup(byGroup)
|
setDrawsByGroup(byGroup)
|
||||||
setStatus('ready')
|
setStatus('ready')
|
||||||
|
} else {
|
||||||
|
console.warn('useDrawDistribution: query succeeded but returned no usable draws for', { leaid, model, year, state })
|
||||||
|
setDrawsByGroup(null)
|
||||||
|
setStatus('error')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('useDrawDistribution failed:', err)
|
console.error('useDrawDistribution failed:', err)
|
||||||
|
|||||||
Reference in New Issue
Block a user