Show the row count while the load is still waiting on it
X-Row-Count is exact but travels with the response headers, and in grain mode the server aggregates the whole table before sending any -- so the number appeared just as the fifteen-second wait ended, which is no use to anyone watching it. The forecast table's own count goes up first instead, from the same table-info the status bar already reads, and the exact figure replaces it when the headers arrive. The count query is deliberately not awaited: it scans the whole table, and the load must not wait on a progress message. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9753846d34
commit
1a0a9db8d0
@ -57,10 +57,15 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio
|
|||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [largeDataset, setLargeDataset] = useState(false)
|
const [largeDataset, setLargeDataset] = useState(false)
|
||||||
const [loadProgress, setLoadProgress] = useState(null) // { received, total }
|
const [loadProgress, setLoadProgress] = useState(null) // { received, total }
|
||||||
// Rows the server says it is sending, from X-Row-Count. Known as soon as the
|
// Rows the load is waiting on, so the wait can say what it is waiting for --
|
||||||
// headers land, well before the body has been read, so the wait can say what
|
// on this data the row count is the wait (see CLAUDE.md, "Load time is
|
||||||
// it is waiting for -- on this data the row count is the wait (see CLAUDE.md,
|
// dominated by row count").
|
||||||
// "Load time is dominated by row count").
|
//
|
||||||
|
// Filled twice. X-Row-Count is exact but arrives with the response headers,
|
||||||
|
// and in grain mode the server aggregates before sending any: the number
|
||||||
|
// turned up just as the wait ended. So the forecast table's own count goes in
|
||||||
|
// first, from the same table-info the status bar reads, and the exact figure
|
||||||
|
// replaces it when the headers land.
|
||||||
const [loadRows, setLoadRows] = useState(null)
|
const [loadRows, setLoadRows] = useState(null)
|
||||||
const [msg, setMsg] = useState(null)
|
const [msg, setMsg] = useState(null)
|
||||||
|
|
||||||
@ -646,7 +651,7 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio
|
|||||||
if (!r.ok) { const { error } = await r.json(); throw new Error(error || 'Failed to load data') }
|
if (!r.ok) { const { error } = await r.json(); throw new Error(error || 'Failed to load data') }
|
||||||
const rowCount = parseInt(r.headers.get('X-Row-Count') || '0')
|
const rowCount = parseInt(r.headers.get('X-Row-Count') || '0')
|
||||||
const total = parseInt(r.headers.get('Content-Length') || '0') || null
|
const total = parseInt(r.headers.get('Content-Length') || '0') || null
|
||||||
setLoadRows(rowCount || null)
|
if (rowCount) setLoadRows(rowCount)
|
||||||
const reader = r.body.getReader()
|
const reader = r.body.getReader()
|
||||||
const chunks = []
|
const chunks = []
|
||||||
let received = 0
|
let received = 0
|
||||||
@ -684,6 +689,12 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio
|
|||||||
setLargeDataset(false)
|
setLargeDataset(false)
|
||||||
setLoadProgress(null)
|
setLoadProgress(null)
|
||||||
setLoadRows(null)
|
setLoadRows(null)
|
||||||
|
// deliberately not awaited: it is a count over the whole forecast table and
|
||||||
|
// the load must not wait on it
|
||||||
|
fetch(`/api/versions/${vid}/table-info`)
|
||||||
|
.then(r => r.ok ? r.json() : null)
|
||||||
|
.then(info => { if (info?.rows && initIdRef.current === myId) setLoadRows(n => n ?? info.rows) })
|
||||||
|
.catch(() => {})
|
||||||
setSlices([])
|
setSlices([])
|
||||||
setExpandDepth(null)
|
setExpandDepth(null)
|
||||||
adoptSplit([], 0)
|
adoptSplit([], 0)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user