QubicTypeScript

createNodePool

Connection pool with round-robin peer selection and automatic failover across multiple Qubic nodes.

For production use, createNodePool is what you want over createNodeConnection. It manages a set of node addresses, distributes requests across them in round-robin order, and silently moves to the next peer whenever one fails.

createNodePool(peers, options?)

import { createNodePool } from "@qubic.org/tcp"

const pool = createNodePool(
  ["node1.qubic.org", "node2.qubic.org", "node3.qubic.org"],
  { port: 21841, timeoutMs: 10_000, maxRetries: 3 }
)

try {
  const { tick } = await requestCurrentTickInfo(pool)
  console.log("Tick:", tick)
} finally {
  pool.close()
}

Parameters

NameTypeDescription
peersreadonly string[]Hostnames or IP addresses of nodes to connect to. Must be non-empty.
options.portnumberTCP port. Default: 21841
options.timeoutMsnumberPer-request timeout in milliseconds. Default: 10000
options.maxRetriesnumberMaximum peer failures per request before throwing. Default: peers.length

Returns NodePool{ request, close }

Throws Error — synchronously, if peers is empty.


Failover behavior

On each request, the pool picks the next peer in round-robin order. If that peer fails (connection refused, timeout, or protocol error), it closes the connection to that peer, advances to the next, and retries — up to maxRetries times.

With the default maxRetries = peers.length, every peer gets exactly one attempt per request. If all fail, the pool throws QubicTcpConnectionError.

// 3 peers, maxRetries = 3 (default)
// request 1 → node1 (ok)
// request 2 → node2 (ok)
// request 3 → node3 (timeout) → retries node1 (ok)
// request 4 → all three fail → QubicTcpConnectionError

pool.request(type, payload?, options?)

Sends a raw TCP request and returns all response payloads (everything before the END_RESPONSE frame).

import { MESSAGE_TYPE } from "@qubic.org/tcp"

const packets = await pool.request(
  MESSAGE_TYPE.REQUEST_CURRENT_TICK_INFO,
  undefined,
  { signal: AbortSignal.timeout(5000) }
)
// packets: Uint8Array[] — response payloads
NameTypeDescription
typenumberMessage type constant from MESSAGE_TYPE
payloadUint8Array | undefinedOptional request payload
options.signalAbortSignal | undefinedCancels the request and all retries

pool.close()

Closes all open TCP sockets. Always call this when done — open sockets block process exit.

const pool = createNodePool(peers)
try {
  // use pool
} finally {
  pool.close()
}

In long-running processes (HTTP servers, daemons), create one pool at startup and reuse it — creating a pool per request is expensive.

// Server example: create once, reuse forever
const pool = createNodePool(["node1.qubic.org", "node2.qubic.org"])

app.get("/tick", async (req, res) => {
  const info = await requestCurrentTickInfo(pool, { signal: AbortSignal.timeout(5000) })
  res.json(info)
})

process.on("SIGTERM", () => pool.close())

AbortSignal for total request budget

AbortSignal.timeout(ms) bounds the total time across all retries, not just a single peer attempt.

const { tick } = await requestCurrentTickInfo(pool, {
  signal: AbortSignal.timeout(3000), // give up after 3 seconds total
})

Without a signal, a hung peer can hold the request until timeoutMs expires for each retry.

On this page