Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@indralukmana
Copy link

@indralukmana indralukmana commented Sep 26, 2025

Problem

Websocket subscriptions connection can fail and having fallback using http/rpc would improve the usage and reliability of handling data subscriptions/fetching.

Summary of Changes

Part of efforts in introducing watchers for gill. This PR is introduce a unified watcher which can be consumed by specific watcher e.g watch account and watch program logs.

In this PR two main functions are introduced:

  • agnostic (browser and node) unified-watcher which can be used to create a watcher with flexible handlings for what is being watched
  • watch-account which watch address through websocket with rpc/http fallback using unified-watcher as base
example usage
  • script watch-account-node.ts

    import { address } from '@solana/addresses'
    import { createSolanaClient, watchAccount, } from 'gill'
    
    const example = async () => {
      const rpcUrl = 'http://127.0.0.1:8899'
      const accountAddressStr = process.argv[2] || '9o69KbfuuVKyjcu27wcxdLoSA4xvmiVozEjUGCNgD2LX'
    
      const { rpc, rpcSubscriptions } = createSolanaClient({ urlOrMoniker: rpcUrl })
    
      const stop = await watchAccount({
        rpc,
        rpcSubscriptions,
        accountAddress: address(accountAddressStr),
        commitment: 'confirmed',
        pollIntervalMs: 5000,
        wsConnectTimeoutMs: 8000,
        onUpdate: (u) => {
          console.log('slot', u.slot)
          console.log('accountInfo', u.value)
        },
        onError: (e) => console.error('watch error:', e),
      })
    
      setTimeout(() => stop(), 600_000)
    }
    
    await example()
  • running the script

    npx tsx watch-account-node.ts EzeYRqxqpthNrqhHWYbu9N6TQkE5EMEUbQtUjLPPu7Tp

Potential improvements left out from this PR

  • Heartbeat polling while on WS
    • What: Occasionally poll even when WS is active.
    • Why: Catches missed WS events and handling for when having different http and websocket provider.
  • Exponential backoff for WS retries
    • What: Replace fixed retry delay with exponential.
    • Why: Reduces thundering herds where service is failing and larg amount of clients try to reconnect at the same time.
  • Explicit state machine
    • What: Model states (CONNECTING, STREAMING, POLLING, STOPPED) and transitions.
    • Why: Less edge-case risk, clearer logic as features grow (heartbeat, resume).
  • Polling only mode
    • What: Allow disabling WS entirely, in this PR we can disabling polling entirely as some subscriptions don't have kit based http polling alternative.
    • Why: Flexibility in specific setups
  • Telemetry support
    • What: onStateChange, onRetry, onFallback, onHeartbeat, counters.
    • Why: Observability for easier debugs and incident tracing

Fixes #281 (partial)

@changeset-bot
Copy link

changeset-bot bot commented Sep 26, 2025

🦋 Changeset detected

Latest commit: 51fb06b

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

This PR includes changesets to release 6 packages
Name Type
gill Minor
@gillsdk/examples-basics Patch
@gillsdk/examples-tokens Patch
@gillsdk/tests-e2e Patch
@gillsdk/react Patch
@gillsdk/solana-pay Patch

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


const closedRef = { value: false };

let pollTimer: NodeJS.Timeout | null = null;
Copy link
Collaborator

Choose a reason for hiding this comment

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

note: this type can't be used since it should be runtime agnostic

Copy link
Author

Choose a reason for hiding this comment

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

yes, would need to find better way to type it. functionally it works

};

// Main loop: attempts WS connection with retry; falls back to polling after max retries.
const run = async () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

thought: this is a complex function. how can we reduce complexity and improve performance?

Copy link
Author

Choose a reason for hiding this comment

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

I did move out the function into its own selve but the parameters became quite complex, might be able to extract out the stream parsing functionality, will need to investigate more

Copy link
Collaborator

Choose a reason for hiding this comment

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

question: is this heartbeat, retry, etc running on the connection or method level?

Copy link
Author

Choose a reason for hiding this comment

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

these should be on application / method and not on the connection / underlying protocol. As these are for handlings of fallback which would be from websocket protocol to http

@indralukmana
Copy link
Author

@catmcgee catmcgee requested a review from tobeycodes October 22, 2025 17:01
Copy link
Collaborator

@tobeycodes tobeycodes left a comment

Choose a reason for hiding this comment

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

Is this ready for review? It's in draft status

@indralukmana indralukmana marked this pull request as ready for review October 24, 2025 07:25
@indralukmana
Copy link
Author

Is this ready for review? It's in draft status

Sorry, forgot to mark as ready for review. Also, added some cleanups and rebase to latest master branch.

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.

[core] Watchers

2 participants