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 tableRef = useRef(null)
|
||||||
const colMetaRef = useRef([])
|
const colMetaRef = useRef([])
|
||||||
const expandDepthRef = useRef(null)
|
const expandDepthRef = useRef(null)
|
||||||
|
const initIdRef = useRef(0)
|
||||||
|
|
||||||
function onDragStart(e) {
|
function onDragStart(e) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
@ -136,6 +137,7 @@ export default function Forecast({ sourceId, versionId }) {
|
|||||||
async function initViewer(vid, sid) {
|
async function initViewer(vid, sid) {
|
||||||
const viewer = viewerRef.current
|
const viewer = viewerRef.current
|
||||||
if (!viewer) return
|
if (!viewer) return
|
||||||
|
const myId = ++initIdRef.current
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
setLargeDataset(false)
|
setLargeDataset(false)
|
||||||
setLoadProgress(null)
|
setLoadProgress(null)
|
||||||
@ -177,12 +179,37 @@ export default function Forecast({ sourceId, versionId }) {
|
|||||||
|
|
||||||
if (rowCount >= 500000) setLargeDataset(true)
|
if (rowCount >= 500000) setLargeDataset(true)
|
||||||
|
|
||||||
if (workerRef.current) { try { workerRef.current.terminate() } catch {} }
|
if (myId !== initIdRef.current) return
|
||||||
const worker = await perspective.worker()
|
|
||||||
workerRef.current = worker
|
if (!workerRef.current) workerRef.current = await perspective.worker()
|
||||||
tableRef.current = rowCount > 0
|
const worker = workerRef.current
|
||||||
? await worker.table(buffer, { name: tableName, index: 'pf_id' })
|
|
||||||
: await worker.table([], { name: tableName, index: 'pf_id' })
|
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)
|
await viewer.load(worker)
|
||||||
viewer.setAttribute('theme', dark ? 'Pro Dark' : 'Pro Light')
|
viewer.setAttribute('theme', dark ? 'Pro Dark' : 'Pro Light')
|
||||||
@ -283,7 +310,7 @@ export default function Forecast({ sourceId, versionId }) {
|
|||||||
async function applyLayout(layout) {
|
async function applyLayout(layout) {
|
||||||
const viewer = viewerRef.current
|
const viewer = viewerRef.current
|
||||||
if (!viewer) return
|
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)
|
const cfg = cleanLayout(layout.config, validCols)
|
||||||
await viewer.restore(cfg)
|
await viewer.restore(cfg)
|
||||||
if (cfg.plugin_config) {
|
if (cfg.plugin_config) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user