Stabilize Forecast viewer lifecycle
Reuse a single Perspective worker across version switches and delete the previous table instead of terminating the worker — terminate was returning a rejecting promise the sync try/catch missed, and each new worker leaked WASM memory. applyLayout no longer leaks a view per call; it reads schema directly from the table. An init id guards against concurrent runs (StrictMode, rapid version switches) clobbering each other, and a catch on "already exists" recovers via open_table+delete when a stale table from a previous run is still hosted. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
e279a510d8
commit
8492557621
@ -71,6 +71,7 @@ export default function Forecast({ sourceId, versionId }) {
|
||||
const tableRef = useRef(null)
|
||||
const colMetaRef = useRef([])
|
||||
const expandDepthRef = useRef(null)
|
||||
const initIdRef = useRef(0)
|
||||
|
||||
function onDragStart(e) {
|
||||
e.preventDefault()
|
||||
@ -136,6 +137,7 @@ export default function Forecast({ sourceId, versionId }) {
|
||||
async function initViewer(vid, sid) {
|
||||
const viewer = viewerRef.current
|
||||
if (!viewer) return
|
||||
const myId = ++initIdRef.current
|
||||
setLoading(true)
|
||||
setLargeDataset(false)
|
||||
setLoadProgress(null)
|
||||
@ -177,12 +179,37 @@ export default function Forecast({ sourceId, versionId }) {
|
||||
|
||||
if (rowCount >= 500000) setLargeDataset(true)
|
||||
|
||||
if (workerRef.current) { try { workerRef.current.terminate() } catch {} }
|
||||
const worker = await perspective.worker()
|
||||
workerRef.current = worker
|
||||
tableRef.current = rowCount > 0
|
||||
? await worker.table(buffer, { name: tableName, index: 'pf_id' })
|
||||
: await worker.table([], { name: tableName, index: 'pf_id' })
|
||||
if (myId !== initIdRef.current) return
|
||||
|
||||
if (!workerRef.current) workerRef.current = await perspective.worker()
|
||||
const worker = workerRef.current
|
||||
|
||||
if (tableRef.current) {
|
||||
try { await tableRef.current.delete() } catch {}
|
||||
tableRef.current = null
|
||||
}
|
||||
|
||||
const opts = { name: tableName, index: 'pf_id' }
|
||||
const makeTable = async () => rowCount > 0 ? worker.table(buffer, opts) : worker.table([], opts)
|
||||
try {
|
||||
tableRef.current = await makeTable()
|
||||
} catch (err) {
|
||||
if (/already exists/i.test(String(err?.message || err))) {
|
||||
try {
|
||||
const existing = await worker.open_table(tableName)
|
||||
if (existing) await existing.delete()
|
||||
} catch {}
|
||||
tableRef.current = await makeTable()
|
||||
} else {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
if (myId !== initIdRef.current) {
|
||||
try { await tableRef.current.delete() } catch {}
|
||||
tableRef.current = null
|
||||
return
|
||||
}
|
||||
|
||||
await viewer.load(worker)
|
||||
viewer.setAttribute('theme', dark ? 'Pro Dark' : 'Pro Light')
|
||||
@ -283,7 +310,7 @@ export default function Forecast({ sourceId, versionId }) {
|
||||
async function applyLayout(layout) {
|
||||
const viewer = viewerRef.current
|
||||
if (!viewer) return
|
||||
const validCols = new Set(tableRef.current ? Object.keys(await (await tableRef.current.view()).schema()) : [])
|
||||
const validCols = new Set(tableRef.current ? Object.keys(await tableRef.current.schema()) : [])
|
||||
const cfg = cleanLayout(layout.config, validCols)
|
||||
await viewer.restore(cfg)
|
||||
if (cfg.plugin_config) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user