Skip to main content
Track and compare block indexing latency between Portal and external RPC providers. Use case: Monitor real-time indexing performance and compare Portal vs RPC latency.
import { formatBlock } from '@subsquid/pipes'
import { solanaPortalSource, solanaRpcLatencyWatcher } from '@subsquid/pipes/solana'

/**
 * This example demonstrates how to track and compare block indexing latency
 * between the Subsquid Portal and external RPC providers.
 * It listens for new block heads and measures the time until blocks are
 * observed on the client side through both RPC endpoints and the Portal.
 *
 ******************************************************************************
 * ⚠️ Important:
 * - The measured values INCLUDE client-side network latency.
 * - For RPC, only the *arrival time* of the block is measured — this does NOT
 *   capture the node's internal processing or response latency if queried directly.
 *****************************************************************************
 *
 * In other words, the results represent end-to-end delays as experienced by the client,
 * not the pure Portal latency or RPC processing performance.
 */

async function main() {
  // Create a stream of new blocks from Solana mainnet portal
  const stream = solanaPortalSource({
    portal: 'https://portal.sqd.dev/datasets/solana-mainnet',
    query: { from: 'latest' }, // Start from the latest block
  }).pipe(
    solanaRpcLatencyWatcher({
      rpcUrl: ['https://api.mainnet-beta.solana.com'], // RPC endpoints to monitor
    }).pipe({
      profiler: { id: 'expose metrics' },
      transform: (data, { metrics }) => {
        if (!data) return // Skip if no latency data

        // For each RPC endpoint, update the latency gauge metric
        for (const rpc of data.rpc) {
          metrics
            .gauge({
              name: 'rpc_latency_ms',
              help: 'RPC Latency in ms',
              labelNames: ['url'],
            })
            .set({ url: rpc.url }, data.rpc[0].portalDelayMs)
        }

        return data
      },
    }),
  )

  // Iterate over the stream, logging block and RPC latency data
  for await (const { data } of stream) {
    if (!data) continue // Skip if no block data

    // Log block number and timestamp
    console.log(`-------------------------------------`)
    console.log(`BLOCK DATA: ${formatBlock(data.number)} / ${data.timestamp.toString()}`)
    // Log RPC latency table for the block
    console.table(data.rpc)

    /**
    EXAMPLE OUTPUT:
    -------------------------------------
    BLOCK DATA: 369,377,455 / Fri Sep 26 2025 15:31:36 GMT+0400 (Georgia Standard Time)
    ┌───┬─────────────────────────────────────┬──────────────────────────┬───────────────┐
    │   │ url                                 │ receivedAt               │ portalDelayMs │
    ├───┼─────────────────────────────────────┼──────────────────────────┼───────────────┤
    │ 0 │ https://api.mainnet-beta.solana.com │ 2025-09-26T11:31:37.075Z │ 358           │
    └───┴─────────────────────────────────────┴──────────────────────────┴───────────────┘
    -------------------------------------
    BLOCK DATA: 369,377,457 / Fri Sep 26 2025 15:31:37 GMT+0400 (Georgia Standard Time)
    ┌───┬─────────────────────────────────────┬──────────────────────────┬───────────────┐
    │   │ url                                 │ receivedAt               │ portalDelayMs │
    ├───┼─────────────────────────────────────┼──────────────────────────┼───────────────┤
    │ 0 │ https://api.mainnet-beta.solana.com │ 2025-09-26T11:31:37.830Z │ 297           │
    └───┴─────────────────────────────────────┴──────────────────────────┴───────────────┘
     */
  }
}

// Start the main function
void main()

Custom Metrics Integration

Export latency metrics to Prometheus or other monitoring systems.
import { solanaPortalSource, solanaRpcLatencyWatcher } from '@subsquid/pipes/solana'
import { Registry, Counter } from 'prom-client'

const registry = new Registry()
const latencyCounter = new Counter({
  name: 'portal_latency_ms_total',
  help: 'Total Portal latency in milliseconds',
  registers: [registry],
})

const stream = solanaPortalSource({
  portal: 'https://portal.sqd.dev/datasets/solana-mainnet',
  query: { from: 'latest' },
}).pipe(
  solanaRpcLatencyWatcher({
    rpcUrl: ['https://api.mainnet-beta.solana.com'],
  }).pipe({
    transform: (data) => {
      if (!data) return

      // Record latency metrics
      for (const rpc of data.rpc) {
        latencyCounter.inc(rpc.portalDelayMs)
      }

      return data
    },
  }),
)

// Expose metrics endpoint
import http from 'http'
const server = http.createServer(async (req, res) => {
  if (req.url === '/metrics') {
    res.setHeader('Content-Type', registry.contentType)
    res.end(await registry.metrics())
  } else {
    res.end('OK')
  }
})
server.listen(9090)

for await (const { data } of stream) {
  // Process blocks
}