Skip to content

Collapse retrying into loading with stale-while-revalidate semantics#1677

Open
mcintyre94 wants to merge 1 commit into
reactive-store/with-signalfrom
reactive-store/preserve-retrying-error
Open

Collapse retrying into loading with stale-while-revalidate semantics#1677
mcintyre94 wants to merge 1 commit into
reactive-store/with-signalfrom
reactive-store/preserve-retrying-error

Conversation

@mcintyre94

@mcintyre94 mcintyre94 commented May 20, 2026

Copy link
Copy Markdown
Member

Summary of Changes

This PR modifies the states for the stream-store.

The retrying state is removed, and we now use loading for both first connects and subsequent ones. This matcheshow we structure the action store, and minimises states.

Additionally we preserve the last error (in addition to data) in the loading state, to improve SWR. Again this matches a change made to the action store.

@changeset-bot

changeset-bot Bot commented May 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: bb74599

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 47 packages
Name Type
@solana/subscribable Major
@solana/kit Major
@solana/react Major
@solana/rpc-spec Major
@solana/rpc-subscriptions-channel-websocket Major
@solana/rpc-subscriptions-spec Major
@solana/rpc-subscriptions Major
@solana/accounts Major
@solana/plugin-interfaces Major
@solana/rpc-api Major
@solana/rpc-transport-http Major
@solana/rpc Major
@solana/sysvars Major
@solana/rpc-subscriptions-api Major
@solana/transaction-confirmation Major
@solana/program-client-core Major
@solana/rpc-graphql Major
@solana/addresses Major
@solana/assertions Major
@solana/codecs-core Major
@solana/codecs-data-structures Major
@solana/codecs-numbers Major
@solana/codecs-strings Major
@solana/codecs Major
@solana/compat Major
@solana/errors Major
@solana/fast-stable-stringify Major
@solana/fixed-points Major
@solana/functional Major
@solana/instruction-plans Major
@solana/instructions Major
@solana/keys Major
@solana/nominal-types Major
@solana/offchain-messages Major
@solana/options Major
@solana/plugin-core Major
@solana/programs Major
@solana/promises Major
@solana/rpc-parsed-types Major
@solana/rpc-spec-types Major
@solana/rpc-transformers Major
@solana/rpc-types Major
@solana/signers Major
@solana/transaction-messages Major
@solana/transactions Major
@solana/wallet-account-signer Major
@solana/webcrypto-ed25519-polyfill Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

mcintyre94 commented May 20, 2026

Copy link
Copy Markdown
Member Author

@mcintyre94 mcintyre94 changed the title Collapse loading and retrying into a single loading status Collapse retrying into loading with stale-while-revalidate semantics May 20, 2026
@bundlemon

bundlemon Bot commented May 20, 2026

Copy link
Copy Markdown

BundleMon

Files updated (22)
Status Path Size Limits
react/dist/index.native.mjs
4.89KB (+1.8KB +58.14%) -
react/dist/index.browser.mjs
4.89KB (+1.8KB +58.06%) -
react/dist/index.node.mjs
4.89KB (+1.8KB +58.07%) -
@solana/kit production bundle
kit/dist/index.production.min.js
52.8KB (+202B +0.37%) -
errors/dist/index.browser.mjs
20.9KB (+179B +0.84%) -
errors/dist/index.native.mjs
20.9KB (+179B +0.84%) -
errors/dist/index.node.mjs
20.92KB (+179B +0.84%) -
wallet-account-signer/dist/index.browser.mjs
17.69KB (+169B +0.94%) -
wallet-account-signer/dist/index.native.mjs
17.69KB (+168B +0.94%) -
wallet-account-signer/dist/index.node.mjs
17.71KB (+168B +0.93%) -
kit/dist/index.native.mjs
4.63KB (+161B +3.52%) -
kit/dist/index.node.mjs
4.63KB (+161B +3.52%) -
kit/dist/index.browser.mjs
4.63KB (+160B +3.49%) -
subscribable/dist/index.browser.mjs
2.76KB (+160B +6%) -
subscribable/dist/index.native.mjs
2.77KB (+160B +5.99%) -
subscribable/dist/index.node.mjs
2.83KB (+158B +5.76%) -
rpc-subscriptions-spec/dist/index.browser.mjs
2.19KB (-13B -0.58%) -
rpc-subscriptions-spec/dist/index.native.mjs
2.19KB (-13B -0.58%) -
rpc-subscriptions-spec/dist/index.node.mjs
2.23KB (-13B -0.56%) -
rpc-spec/dist/index.browser.mjs
898B (-20B -2.18%) -
rpc-spec/dist/index.native.mjs
897B (-21B -2.29%) -
rpc-spec/dist/index.node.mjs
896B (-21B -2.29%) -
Unchanged files (125)
Status Path Size Limits
rpc-graphql/dist/index.browser.mjs
18.82KB -
rpc-graphql/dist/index.native.mjs
18.81KB -
rpc-graphql/dist/index.node.mjs
18.81KB -
transaction-messages/dist/index.browser.mjs
11.32KB -
transaction-messages/dist/index.native.mjs
11.32KB -
transaction-messages/dist/index.node.mjs
11.32KB -
instruction-plans/dist/index.browser.mjs
6.58KB -
instruction-plans/dist/index.native.mjs
6.58KB -
instruction-plans/dist/index.node.mjs
6.58KB -
fixed-points/dist/index.browser.mjs
5.08KB -
fixed-points/dist/index.native.mjs
5.07KB -
fixed-points/dist/index.node.mjs
5.07KB -
codecs-data-structures/dist/index.browser.mjs
5.04KB -
codecs-data-structures/dist/index.native.mjs
5.03KB -
codecs-data-structures/dist/index.node.mjs
5.03KB -
offchain-messages/dist/index.browser.mjs
4.89KB -
offchain-messages/dist/index.native.mjs
4.89KB -
offchain-messages/dist/index.node.mjs
4.89KB -
transactions/dist/index.browser.mjs
4.07KB -
transactions/dist/index.native.mjs
4.07KB -
transactions/dist/index.node.mjs
4.07KB -
codecs-core/dist/index.browser.mjs
3.62KB -
codecs-core/dist/index.native.mjs
3.62KB -
codecs-core/dist/index.node.mjs
3.62KB -
webcrypto-ed25519-polyfill/dist/index.node.mj
s
3.61KB -
webcrypto-ed25519-polyfill/dist/index.browser
.mjs
3.59KB -
webcrypto-ed25519-polyfill/dist/index.native.
mjs
3.57KB -
rpc-subscriptions/dist/index.browser.mjs
3.37KB -
rpc-subscriptions/dist/index.node.mjs
3.34KB -
rpc-subscriptions/dist/index.native.mjs
3.31KB -
signers/dist/index.browser.mjs
3.26KB -
signers/dist/index.native.mjs
3.26KB -
signers/dist/index.node.mjs
3.26KB -
rpc-transformers/dist/index.browser.mjs
3.16KB -
rpc-transformers/dist/index.native.mjs
3.16KB -
rpc-transformers/dist/index.node.mjs
3.16KB -
keys/dist/index.node.mjs
3.06KB -
addresses/dist/index.browser.mjs
2.93KB -
addresses/dist/index.native.mjs
2.92KB -
addresses/dist/index.node.mjs
2.92KB -
keys/dist/index.browser.mjs
2.85KB -
keys/dist/index.native.mjs
2.85KB -
codecs-strings/dist/index.browser.mjs
2.55KB -
codecs-strings/dist/index.node.mjs
2.51KB -
codecs-strings/dist/index.native.mjs
2.47KB -
transaction-confirmation/dist/index.node.mjs
2.42KB -
transaction-confirmation/dist/index.native.mj
s
2.37KB -
sysvars/dist/index.browser.mjs
2.37KB -
sysvars/dist/index.native.mjs
2.37KB -
transaction-confirmation/dist/index.browser.m
js
2.37KB -
sysvars/dist/index.node.mjs
2.37KB -
rpc/dist/index.node.mjs
1.95KB -
codecs-numbers/dist/index.browser.mjs
1.95KB -
codecs-numbers/dist/index.native.mjs
1.95KB -
codecs-numbers/dist/index.node.mjs
1.94KB -
rpc-transport-http/dist/index.browser.mjs
1.89KB -
rpc-transport-http/dist/index.native.mjs
1.89KB -
rpc/dist/index.native.mjs
1.81KB -
rpc-types/dist/index.browser.mjs
1.8KB -
rpc/dist/index.browser.mjs
1.8KB -
rpc-types/dist/index.native.mjs
1.8KB -
rpc-types/dist/index.node.mjs
1.8KB -
rpc-transport-http/dist/index.node.mjs
1.71KB -
rpc-subscriptions-channel-websocket/dist/inde
x.node.mjs
1.33KB -
rpc-subscriptions-channel-websocket/dist/inde
x.native.mjs
1.27KB -
rpc-subscriptions-channel-websocket/dist/inde
x.browser.mjs
1.26KB -
program-client-core/dist/index.browser.mjs
1.21KB -
program-client-core/dist/index.native.mjs
1.21KB -
program-client-core/dist/index.node.mjs
1.21KB -
options/dist/index.browser.mjs
1.18KB -
options/dist/index.native.mjs
1.18KB -
options/dist/index.node.mjs
1.17KB -
accounts/dist/index.browser.mjs
1.17KB -
accounts/dist/index.native.mjs
1.17KB -
accounts/dist/index.node.mjs
1.16KB -
rpc-api/dist/index.browser.mjs
998B -
rpc-api/dist/index.native.mjs
997B -
rpc-api/dist/index.node.mjs
995B -
compat/dist/index.browser.mjs
969B -
compat/dist/index.native.mjs
968B -
compat/dist/index.node.mjs
966B -
rpc-spec-types/dist/index.browser.mjs
962B -
rpc-spec-types/dist/index.native.mjs
961B -
rpc-spec-types/dist/index.node.mjs
959B -
rpc-subscriptions-api/dist/index.native.mjs
871B -
rpc-subscriptions-api/dist/index.browser.mjs
870B -
rpc-subscriptions-api/dist/index.node.mjs
870B -
promises/dist/index.native.mjs
841B -
promises/dist/index.node.mjs
840B -
promises/dist/index.browser.mjs
839B -
plugin-core/dist/index.browser.mjs
820B -
plugin-core/dist/index.native.mjs
819B -
plugin-core/dist/index.node.mjs
817B -
assertions/dist/index.browser.mjs
783B -
instructions/dist/index.browser.mjs
771B -
instructions/dist/index.native.mjs
770B -
instructions/dist/index.node.mjs
768B -
fast-stable-stringify/dist/index.browser.mjs
726B -
fast-stable-stringify/dist/index.native.mjs
725B -
assertions/dist/index.native.mjs
724B -
fast-stable-stringify/dist/index.node.mjs
724B -
assertions/dist/index.node.mjs
723B -
programs/dist/index.browser.mjs
329B -
programs/dist/index.native.mjs
327B -
programs/dist/index.node.mjs
325B -
fs-impl/dist/index.browser.mjs
245B -
event-target-impl/dist/index.node.mjs
230B -
functional/dist/index.browser.mjs
154B -
functional/dist/index.native.mjs
152B -
text-encoding-impl/dist/index.native.mjs
152B -
functional/dist/index.node.mjs
151B -
codecs/dist/index.browser.mjs
145B -
codecs/dist/index.native.mjs
144B -
codecs/dist/index.node.mjs
142B -
event-target-impl/dist/index.browser.mjs
133B -
ws-impl/dist/index.node.mjs
131B -
text-encoding-impl/dist/index.browser.mjs
122B -
fs-impl/dist/index.node.mjs
120B -
text-encoding-impl/dist/index.node.mjs
119B -
ws-impl/dist/index.browser.mjs
113B -
crypto-impl/dist/index.node.mjs
111B -
crypto-impl/dist/index.browser.mjs
109B -
rpc-parsed-types/dist/index.browser.mjs
66B -
rpc-parsed-types/dist/index.native.mjs
65B -
rpc-parsed-types/dist/index.node.mjs
63B -

Total files change +7.45KB +1.41%

Final result: ✅

View report in BundleMon website ➡️


Current branch size history | Target branch size history

@mcintyre94

Copy link
Copy Markdown
Member Author

@trevor-cortex

@trevor-cortex trevor-cortex left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Collapses retrying into loading on ReactiveStreamStore, mirroring the action store's running, and widens loading to carry data: T | undefined + error: unknown so the prior outcome is preserved across reconnects (SWR). Reasonable shape change — the implementation in both stores becomes uniformly setState({ data: currentState.data, error: currentState.error, status: 'loading' }) and the existing setState identity guard naturally no-ops the loading → loading re-entry (already covered by the pre-existing 'does not notify subscribers on the loading → loading re-entry' test).

packages/subscribable/README.md is half-migrated and now self-contradictory. The PR only made minimal textual tweaks here, leaving stale retrying references that disagree with the implementation. Three spots:

  1. The ReactiveState<T> snippet in the README still lists a retrying variant (lines 38–42 of the new README). The diff just changed error: undefinederror: unknown on that line; the line should be removed outright, since retrying is no longer part of the type. As-is, the README contradicts the source.
  2. The paragraph just below (line 47) still says "transitions through retrying while preserving the last known value and the last error".
  3. Further down (around line 135), the createReactiveStoreFromDataPublisherFactory "Things to note" bullet wasn't touched at all and still reads: "From idle, the store transitions through loading; from any other status, through retrying while preserving the last known value." Same problem.

Inline comments on the first two. #3 isn't part of the diff so flagging it here.

Things to watch out for / verify:

  • Migration heuristic in the changeset (status === 'loading' && data !== undefined) misses the case where a retry was kicked off from error but no loaded had ever occurred before that error — then data === undefined and the loading is indistinguishable from a first-load from idle. The changeset already acknowledges this loosely ("or just status === 'loading' if you don't need to distinguish"), so this is fine, but worth keeping in mind for downstream callers that genuinely need the first-load-vs-reconnect distinction — the new model doesn't preserve it.
  • Test coverage looks good: kit's error → loading (preserves data + error), subscribable's error → loading and loaded → loading, and the loading → loading no-notify case are all covered. The notify-on-transition tests still assert the right thing (the status discriminator changes, so subscribers still fire).
  • Behavioural diff worth keeping in the back of the head for downstream consumers: previously, a connect() from error cleared error immediately on transition, so any UI gated on error !== undefined would clear at that moment. Now error survives across the transition until the next loaded (or a new error) replaces it. This is the intended SWR semantic and matches the changeset, just want it surfaced for review.

Notes for subsequent reviewers:

  • Pre-existing staleness elsewhere in the README that's outside the scope of this PR but worth a follow-up: the bottom createReactiveStoreFromDataPublisherFactory snippet still shows an abortSignal: property on the config and the bullet about "Triggering the caller's abortSignal disconnects the store permanently" — neither matches the current withSignal()-per-connect API. Not blocking this PR, but worth knowing while the file is open.
  • No other call sites in the repo reference 'retrying' as far as I can see; the type-system removal of the variant would surface any stragglers at compile time.

Comment thread packages/subscribable/README.md Outdated
Comment thread packages/subscribable/README.md Outdated
@github-actions

github-actions Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

Documentation Preview: https://kit-docs-i5tmvjnys-anza-tech.vercel.app

@mcintyre94

Copy link
Copy Markdown
Member Author

Oops missed some changes to the readme. Please re-review @trevor-cortex

@trevor-cortex

Copy link
Copy Markdown

Re-reviewed the README diff — all three stale retrying references from my previous pass are gone:

  • The ReactiveState<T> block drops the retrying variant and updates loading to { data: T | undefined; error: unknown }.
  • The narrative paragraph now describes the SWR semantic correctly (preserve last data + error on reconnect, loaded clears, error replaces).
  • The bottom "Things to note" bullet is rewritten to match.

Nice bonus: the pre-existing withSignal() / abortSignal: staleness I called out as out-of-scope last round has also been fixed — factory signature, example, and final bullet all reflect the current withSignal(signal).connect() API.

Nothing else to flag. LGTM. 👍

@mcintyre94 mcintyre94 marked this pull request as ready for review May 26, 2026 08:34
@mcintyre94 mcintyre94 added the major This would require a major version bump label May 26, 2026
@mcintyre94 mcintyre94 requested a review from lorisleiva May 26, 2026 08:42
@mcintyre94 mcintyre94 force-pushed the reactive-store/with-signal branch from 85182fc to 6169ab5 Compare May 26, 2026 09:11
@mcintyre94 mcintyre94 force-pushed the reactive-store/preserve-retrying-error branch from 2fd75aa to 38cc195 Compare May 26, 2026 09:11

@lorisleiva lorisleiva left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, one less state to worry about!

@mcintyre94 mcintyre94 force-pushed the reactive-store/preserve-retrying-error branch from 74b30a6 to abd86ff Compare May 29, 2026 10:36
@mcintyre94 mcintyre94 force-pushed the reactive-store/with-signal branch from 0c67325 to ade3616 Compare June 3, 2026 15:17
@mcintyre94 mcintyre94 force-pushed the reactive-store/preserve-retrying-error branch from abd86ff to a484aef Compare June 3, 2026 15:17
@mcintyre94 mcintyre94 force-pushed the reactive-store/preserve-retrying-error branch 3 times, most recently from d5542a7 to 8733cab Compare June 5, 2026 15:56
@mcintyre94 mcintyre94 force-pushed the reactive-store/with-signal branch from 1ec234a to 7615ebb Compare June 5, 2026 15:56
@mcintyre94 mcintyre94 force-pushed the reactive-store/preserve-retrying-error branch 2 times, most recently from c3ebc28 to fcfdbd1 Compare June 10, 2026 16:49
@mcintyre94 mcintyre94 added the do-not-close Add this tag to exempt an issue/PR from being closed by the stalebot label Jun 11, 2026
Mirrors the action store's `running` (which already merged "first call vs subsequent call"). `data` and `error` are preserved through `loading` for stale-while-revalidate UX.

`ReactiveState<T>` drops the `retrying` variant. `loading` widens from `{ data: undefined, error: undefined }` to `{ data: T | undefined, error: unknown }`. Both `createReactiveStoreFromDataPublisherFactory` and `createReactiveStoreWithInitialValueAndSlotTracking` transition every `connect()` through `loading`, preserving `currentState.data` and `currentState.error`.

Consumers that need to distinguish first-load from reconnect inspect `data === undefined` instead of relying on a separate `retrying` status. Bumps `@solana/subscribable` and `@solana/kit` major.

Tests in both packages updated: `retrying`-state assertions now assert `loading` carrying stale data + error. Downstream `useSubscription` (next commit) drops `retrying` from `SubscriptionResult.status` and passes data + error through the `loading` case.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-close Add this tag to exempt an issue/PR from being closed by the stalebot major This would require a major version bump

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants