Skip to content

Fix Viewer server crash: serialise grid rendering, stop component echo cascade - #99

Merged
t0mdavid-m merged 1 commit into
developfrom
fix/viewer-rerun-cascade
Sep 10, 2026
Merged

t0mdavid-m merged 1 commit into
developfrom
fix/viewer-rerun-cascade

Conversation

@t0mdavid-m

Copy link
Copy Markdown
Member

Summary

Fixes the "random white screen that needs a refresh" reported by users. It is the Streamlit server process dying with a native access violation (polars DataFrame.deserialize, called from st.cache_data) while two script threads render the FLASHDeconv Viewer at the same time. Reproduced three times with faulthandler; full analysis, timeline and measurements in docs/white-screen-root-cause.md (added in this PR).

Root cause chain:

  1. Every grid cell of the Vue component sent setComponentValue on mount and on every echoed render; Streamlit turns each one into a full rerun request (no dedup), so one Viewer load produced 20 rerun requests and 8-12 script runs.
  2. Streamlit's default runner.fastReruns stops the running ScriptRunner and starts a new script thread immediately, while the old thread keeps executing until its next Streamlit call (which never comes inside the polars/pyarrow work of initialize_data / filter_data).
  3. Two to four concurrent executions of the same page share the session's polars/pyarrow objects and the st.cache_data pickles; polars' native deserializer crashes the whole process. Users see CONNECTING, then "Connection error", or an empty white page if the crash lands right after a sidebar click.

Changes

  • src/render/render.py: per-session lock around render_grid (keyed by Streamlit session id). A runner that has been stopped releases the lock at its next Streamlit call, so the grid code can never run twice concurrently for one session. This alone removes the crash class.
  • js-component/dist/: bundle rebuilt from Send selection state to Streamlit only on genuine user interaction t0mdavid-m/openms-streamlit-vue-component#30 (no setComponentValue on mount, no echo of Python-provided state, no duplicate payloads). CI clones FVdeploy fresh, so that PR must be merged for CI builds to include the Vue side; the committed bundle covers local runs and the Windows build meanwhile.
  • docs/white-screen-root-cause.md: analysis, secondary findings, fix options and the before/after measurements.

Not included (separate follow-ups, documented in the analysis): the source watcher purging src.* modules because the packages have no __init__.py (add them or run production with --server.fileWatcherType none), the teleported table filter dialog leaving an overlay behind on navigation, and running production with PYTHONFAULTHANDLER=1.

Measurements (Edge via Playwright, 5 trials, median; default fastReruns, faulthandler on)

Metric Before After
Viewer load: rerun requests / script runs 20 / 11 4 / 5
Viewer load: last of 6 cells / settled 0.37 s / 2.47 s 0.26 s / 2.06 s
Row click: settled (runs / requests) 0.45 s (3 / 7) 0.27 s (2 / 1)
Row click updates Mass Table 5/5 5/5
Leave to Layout Manager: visible / settled 0.18 s / 0.16 s 0.18 s / 0.16 s
Return to Viewer: settled (runs / requests) 1.33 s (4 / 7) 0.19 s (1 / 1)
Stress runs, 60 navigation/row clicks each crashed after 4 and 6 clicks 2 x 60 clicks, no crash

--runner.fastReruns false was also tried: no crash, but the Viewer stalls at one cell and ignores navigation, so the default stays.

Test plan

🤖 Generated with Claude Code

https://claude.ai/code/session_01Wda4mDs1DSWJuU1beJJKvL

…o cascade

The "random white screen that needs a refresh" is the Streamlit server
process dying with a native access violation in polars
(DataFrame.deserialize via st.cache_data) while two script threads render
the FLASHDeconv Viewer at the same time. Streamlit's default
runner.fastReruns starts a new script thread on every rerun request while
the previous one is still executing, and every grid cell of the Vue
component used to send a rerun request on mount and on every echoed
render, so one Viewer load produced up to a dozen overlapping runs.
Reproduced three times with faulthandler; details, timeline and
measurements in docs/white-screen-root-cause.md.

- src/render/render.py: per-session lock around render_grid (keyed by
  Streamlit session id) so a stopped runner can never execute the grid
  code concurrently with the new one.
- js-component/dist: bundle rebuilt from openms-streamlit-vue-component
  branch fix/viewer-echo-cascade (no setComponentValue on mount, no echo
  of Python-provided state, no duplicate payloads).
- docs/white-screen-root-cause.md: root cause analysis, secondary
  findings (source watcher purging modules, teleported filter dialog
  overlay), fix options and before/after measurements.

Measured (Edge, 5 trials, median): rerun requests per Viewer load 20 -> 4,
script runs 11 -> 5, load settled 2.47 s -> 2.06 s, return-to-Viewer
settled 1.33 s -> 0.19 s, row click 0.45 s -> 0.27 s; two 60-click stress
runs with no crash (before: crashes after 4 and 6 clicks).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wda4mDs1DSWJuU1beJJKvL
@coderabbitai

coderabbitai Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 0011ea13-b102-4b9f-a183-14081b59d3aa


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant