Skip to content

Draft fix for console data spinner#36613

Draft
frankmcsherry wants to merge 1 commit into
MaterializeInc:mainfrom
frankmcsherry:console_data_spinner
Draft

Draft fix for console data spinner#36613
frankmcsherry wants to merge 1 commit into
MaterializeInc:mainfrom
frankmcsherry:console_data_spinner

Conversation

@frankmcsherry
Copy link
Copy Markdown
Contributor

@frankmcsherry frankmcsherry commented May 18, 2026

Claude and I and @leedqin iterated to repro and potentially address an issue where the Console's data pane produced a spinner than ran forever. Claude's take is a race in websocket setup, in which the two reasons a connection could be started both fire, and race, and trip over each other.

Caveat: all logging retain, by request for ease of understanding, but we should remove before merging anything (the purported fix is one line).

Claude authored summary (horrid line breaks and all):


Summary

Fixes an intermittent indefinite spinner on the Data pane (object
explorer).

The hang happens when the user navigates to the object explorer
after the
environment is already healthy. useAutomaticallyConnectSocket has
two
effects that both initiate a websocket connect on first mount:

  1. Effect 1 instantiates a WebsocketConnectionManager; its
    constructor
    synchronously runs handleEnvironmentChange
    resumeConnection
    target.reconnect() and starts socket #1.
  2. Effect 2 sees previousRequest === undefined and calls
    subscribe.connect(request, …), which disconnect()s socket #1
    while
    it's still in CONNECTING state and opens socket #2.

The browser logs WebSocket is closed before the connection is established
for socket #1. Usually socket #2 opens fine; intermittently the
browser
appears to leave it stuck in CONNECTING and neither open,
close,
nor error ever fires — so SubscribeManager never sees onOpen
/
onReadyForQuery, the SUBSCRIBE is never sent, snapshotComplete
stays
false, and ObjectExplorer keeps rendering <LoadingContainer />.

allObjects (mounted at app boot in AppInitializer) almost
always
sidesteps this — the env isn't enabled yet when its effects run,
so
effect 1's connect path is a no-op and only effect 2 connects.
allNamespaces (mounted on entry to /regions/.../objects)
reliably hits
the race because the env is already healthy by then.

Changes

  • useAutomaticallyConnectSocket.ts: the request-change effect
    now
    skips when previousRequest === undefined (first mount). The
    manager
    constructor already kicks off the initial connect using the
    sqlRequest captured by SubscribeManager's constructor, so
    this
    effect's job is only to handle subsequent request changes.
  • useSubscribe.ts: useState(new SubscribeManager(...))
    useState(() => new SubscribeManager(...)) in all three hook
    variants.
    The non-lazy form allocated a throwaway SubscribeManager (and
    inner
    MaterializeWebsocket) on every render. Unrelated to the hang
    but
    surfaced while diagnosing.

Test plan

  • Navigate to Data on a healthy staging environment; tree loads
    on
    first visit (previously reproduced ~50% of the time).
  • Browser devtools console no longer shows
    WebSocket is closed before the connection is established.
  • Navigate away and back to Data; still loads.
  • Existing unit/integration tests pass.

Tweak the test-plan numbers to whatever you actually observed.

leedqin added a commit that referenced this pull request May 19, 2026
This fixes
[CNS-77](https://linear.app/materializeinc/issue/CNS-77/data-explorer-page-not-loading)
where the data explorer spinner keeps spinning for a while. Reported by
@sjwiesman and @frankmcsherry , I have only been able to repro this in
Safari. Iterated in this draft fix PR:
#36613 , there is a
race condition in the websocket hook useEffects that initiates the
Websocket connection and the second effect kills the `CONNECTING`
socket. I refactored to route the `useAutomaticallyConnectWebsocket`
reconnect signals in the Websocket Connection Manager and added a guard
to serialize the overlapping triggers.


Fixed another bug that happens when a user navigates away from Data
Explorer page and comes back, it reloads the page. Coming back to Data
Explorer, opens a new Websocket connection and reset the state that
clobbered the existing snapshot. Fixed the hook
`useGlobalUpsertSubscribe` to hold the cached snapshot until new manager
has the data.


### Verification

- Added a test for the race condition
Before:


https://github.com/user-attachments/assets/58c2b834-f76d-4158-b4f2-ef330172bb64



- Videos of the fix here



https://github.com/user-attachments/assets/8c12d53f-6d0b-48c1-8ae4-b5a433ba2ee9
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