Skip to content

fix(py): make cleanup() best-effort and prevent duplicate mod_server() retries - #309

Closed
cpsievert wants to merge 1 commit into
mainfrom
fix/py-server-cleanup-hardening
Closed

fix(py): make cleanup() best-effort and prevent duplicate mod_server() retries#309
cpsievert wants to merge 1 commit into
mainfrom
fix/py-server-cleanup-hardening

Conversation

@cpsievert

Copy link
Copy Markdown
Contributor

Summary

Follow-up hardening in the same spirit as #306 (R side): closing gaps in querychat's session-lifecycle and resource-cleanup guarantees so a single failure can't cascade into leaked resources or duplicate server setup.

  • cleanup() no longer gives up partway through. If a data source or the query executor failed to clean up, cleanup() used to stop right there -- owned chatlas clients (and any remaining data sources) were left open. Now each resource is torn down independently; a failure is warned about, not fatal to the rest of teardown.
  • Retired-resource retries are no longer silent. A resource that keeps failing to clean up used to be retried forever with zero visibility. It's still retried, but now emits a warning each time, so a persistently-broken resource is actually noticeable.
  • Shiny Express sessions can't double-register. Counting a session as "live" only after mod_server() succeeds (so a failed session doesn't wrongly block add_table()/remove_table()) reopened a window where a later lazy call -- from .df(), .sql(), etc. -- could retry mod_server() and re-register its reactive effects and bookmark/history hooks a second time. Each session now gets at most one mod_server() attempt.

Test plan

  • uv run pytest pkg-py/tests --ignore=pkg-py/tests/playwright -p no:playwright -- 952 passed
  • uv run ruff check --fix pkg-py --config pyproject.toml -- clean
  • uv run pyright (from pkg-py/) -- 0 errors

🤖 Generated with Claude Code

…) retries

cleanup() previously aborted before closing owned chatlas clients if a
data source's cleanup() raised, and a permanently-failing retired
resource was retried forever with no visibility. Separately, marking a
session initialized only after mod_server() succeeds reopened Express's
lazy _ensure_server_started() guard on failure, letting a later
.df()/.sql() call retry mod_server() and duplicate its reactive-effect
and bookmark/history registrations.

- cleanup() now handles the query executor, each data source, and each
  owned client independently, warning per failure instead of aborting
  the rest of teardown.
- _flush_retired_resources() warns when a retried resource's cleanup
  fails again, instead of retrying silently forever.
- QueryChatExpress tracks attempted sessions so a failed mod_server()
  call is never retried within the same session.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@cpsievert

Copy link
Copy Markdown
Contributor Author

Closing without merging. Working through #300#302#303#304#308 → this PR made it clear that every fix is patching the same root cause: server(data_source=) writes a session-scoped resource (data source, executor, greeter table list, system prompt) into the app-global QueryChat instance, and then we have to guess who else might still be using it. The retired-resource list, live-session counting, best-effort flushes, and the _attempted_sessions guard added here all exist to answer that question.

Rather than harden that further, the plan is to make sessions read from the instance and never write to it: server(data_source=) builds a session-local table set (sources + executor + prompt + greeting tables), hands it to mod_server(), and closes what it created on session.on_ended. That removes the counting/retirement machinery this PR extends instead of hardening it.

Keeping the branch around as a reference for the failure modes it found. The behaviors worth carrying forward as acceptance tests for the redesign: a failed server() must not block later sessions, and Express must never run mod_server() twice for one session.

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