You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Idea: let an unrelated write publish during a refresh by forking only the nodes it overlaps
#3905
Status: this is a hypothetical. It is not a planned change, and the current behavior described below is intended under Solid's existing rules. I'm posting it to get feedback on whether the idea holds up.
During a reconnect, setCard(d => (d.error = "Save failed")) re-runs the store's function. That function reads the connection, which is still pending, so the write waits until the reconnect finishes.
What happens today, and why
Under current rules that's correct: a read of something that isn't ready yet holds.
The reason is that overlap means merge. A node has only one pending version at a time. So if a write touches a node that is already waiting on a transition, the write joins that transition. We chose this on purpose so that we never have to fork the graph.
Two workarounds exist today:
Read latest(connection) inside the store's function, so it uses the last settled value instead of waiting.
Model the connection as an async iterator.
Gabriel's point is that the decision should belong to the write. The write is saying "my local error has nothing to do with the reconnect." Solid instead makes whoever wrote the read make that call. In React, a plain setState is urgent and renders right away against what's committed.
The idea
Fork a node only in the specific case where the difference would be visible. The node would hold two versions: a "now" version computed against the committed value of the pending source, and the transition's version. This happens only if all four of these are true:
The pending source is a refresh with a committed value, not a first load. A first load still holds or hits a Loading boundary, as today.
The write has a different cause from the transition. If it comes from the same action, it entangles, as today.
The node actually reads the pending source. It's in the transition's footprint because it reads the source, not just because it was notified.
Something visible reads the node. If nothing on screen depends on it, it just waits.
Every other node keeps one version and entangles as it does today. When the transition commits, it re-derives the forked nodes. So the extra work is limited to nodes that both the write and the transition touch.
What Gabriel would see
Moment
Screen
Reconnect starts
Reconnecting
setCard writes the error
Old card + "Save failed" (shown now)
Reconnect commits
New card + "Save failed"
Every frame reflects a single connection value, so this isn't tearing.
Open questions
Actions: does a write inside an unrelated action count as a "different cause"?
isPending: what should it report for a forked node?
Existing pieces: how does this relate to the parts of Solid that already work somewhat like this? Optimistic updates publish a guess immediately and reconcile later, and some async work already re-runs against committed values while a transition is waiting.
Cost: what are the size and performance costs of letting a node hold two versions, given that avoiding this was the reason for the current design?
Feedback welcome, especially examples where this would give a surprising result.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
The motivating case
Thanks to Gabriel (@GabbeV) for this one. His kanban repro: https://s.olid.uk/id/w6jDCAXZRXu7bAhDyQZ7Fw
connectionis an async memo. Reconnecting is arefreshof it.setCard(d => (d.error = "Save failed"))re-runs the store's function. That function reads the connection, which is still pending, so the write waits until the reconnect finishes.What happens today, and why
Under current rules that's correct: a read of something that isn't ready yet holds.
The reason is that overlap means merge. A node has only one pending version at a time. So if a write touches a node that is already waiting on a transition, the write joins that transition. We chose this on purpose so that we never have to fork the graph.
Two workarounds exist today:
latest(connection)inside the store's function, so it uses the last settled value instead of waiting.Gabriel's point is that the decision should belong to the write. The write is saying "my local error has nothing to do with the reconnect." Solid instead makes whoever wrote the read make that call. In React, a plain
setStateis urgent and renders right away against what's committed.The idea
Fork a node only in the specific case where the difference would be visible. The node would hold two versions: a "now" version computed against the committed value of the pending source, and the transition's version. This happens only if all four of these are true:
Loadingboundary, as today.Every other node keeps one version and entangles as it does today. When the transition commits, it re-derives the forked nodes. So the extra work is limited to nodes that both the write and the transition touch.
What Gabriel would see
setCardwrites the errorEvery frame reflects a single connection value, so this isn't tearing.
Open questions
isPending: what should it report for a forked node?Feedback welcome, especially examples where this would give a surprising result.
— Drafted by Claude via Cursor for @ryansolid
All reactions