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,13 +19,21 @@ function ensureShardRegistered(db, model, year, state) {
|
||||
shardCache.set(
|
||||
key,
|
||||
(async () => {
|
||||
const url = `${HF_BASE}/model_id=${model}/YEAR=${year}/LEA_STATE=${state}/data_0.parquet`
|
||||
const res = await fetch(url)
|
||||
if (!res.ok) throw new Error(`Failed to fetch draw shard: HTTP ${res.status}`)
|
||||
const buffer = new Uint8Array(await res.arrayBuffer())
|
||||
const fileName = `${key}.parquet`
|
||||
await db.registerFileBuffer(fileName, buffer)
|
||||
return fileName
|
||||
try {
|
||||
const url = `${HF_BASE}/model_id=${model}/YEAR=${year}/LEA_STATE=${state}/data_0.parquet`
|
||||
const res = await fetch(url)
|
||||
if (!res.ok) throw new Error(`Failed to fetch draw shard: HTTP ${res.status}`)
|
||||
const buffer = new Uint8Array(await res.arrayBuffer())
|
||||
const fileName = `${key}.parquet`
|
||||
await db.registerFileBuffer(fileName, buffer)
|
||||
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)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
setDrawsByGroup(byGroup)
|
||||
setStatus('ready')
|
||||
if (hasDraws) {
|
||||
setDrawsByGroup(byGroup)
|
||||
setStatus('ready')
|
||||
} else {
|
||||
console.warn('useDrawDistribution: query succeeded but returned no usable draws for', { leaid, model, year, state })
|
||||
setDrawsByGroup(null)
|
||||
setStatus('error')
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('useDrawDistribution failed:', err)
|
||||
|
||||
Reference in New Issue
Block a user