Logo
Nuevos usuarios de RPC: 35% de descuento el primer mesVer oferta
OnFinality Learn
Solución de problemas de RPC12 min de lectura

Tiempos de espera de RPC en Polkadot: Reconexiones de WsProvider, Límites de RPC de Bloques y Consultas Fiables

Aprende por qué ocurren los tiempos de espera de RPC en Polkadot, cómo maneja WsProvider de polkadot.js las desconexiones y cómo construir consultas resilientes con tiempos de espera, reconexiones y procesamiento por lotes.

TL;DR

Un tiempo de espera de RPC de Polkadot ocurre cuando una solicitud a un nodo a través de WebSockets o HTTP no recibe una respuesta dentro del límite de tiempo del cliente. Este artículo explica los comportamientos de tiempo de espera distintos en Substrate y polkadot.js, por qué WsProvider se desconecta silenciosamente y cómo construir consultas resilientes con tiempos de espera explícitos, manejo de reconexión y procesamiento por lotes.

Respuesta directa: ¿Qué causa un tiempo de espera de RPC en Polkadot?

Un tiempo de espera de RPC en Polkadot ocurre cuando una solicitud a un nodo a través de WebSockets (wss://) o HTTP (https://) no recibe una respuesta dentro del límite de tiempo del cliente, típicamente 60 segundos. En Polkadot, esto a menudo no es un problema de red, sino una consecuencia de que el nodo está ocupado, la solicitud es demasiado pesada o el cliente no maneja correctamente la reconexión. A diferencia de los tiempos de espera HTTP genéricos, la capa RPC de Substrate tiene comportamientos específicos: las suscripciones WebSocket pueden quedar obsoletas sin error, y las consultas de estado pesadas pueden exceder los límites de tiempo en endpoints públicos.

El escenario más común es un ApiPromise de polkadot.js que usa WsProvider y se desconecta silenciosamente sin reconectarse, dejando tu aplicación colgada. Esto ocurre porque el WsProvider predeterminado no lanza una excepción en caso de tiempo de espera; solo emite un evento 'disconnected' que muchos desarrolladores pasan por alto. Entender estos mecanismos es el primer paso para construir consultas fiables.

  • Los tiempos de espera de RPC HTTP son directos: la solicitud falla si no hay respuesta dentro del tiempo de espera.
  • Las suscripciones WebSocket pueden quedar obsoletas sin ningún error, especialmente si el nodo se reinicia o la conexión se cae.
  • Las llamadas pesadas como state_getPairs o state_getKeysPaged pueden agotar el tiempo en endpoints públicos debido a límites de tasa o restricciones de recursos.
  • Los tiempos de espera de completitud de bloque ocurren cuando el cliente espera un bloque finalizado que va por detrás del mejor bloque.

Cómo manejan los tiempos de espera de manera diferente Substrate y polkadot.js

La capa RPC de Substrate está construida sobre JSON-RPC sobre HTTP o WebSocket. Para HTTP, el servidor tiene un tiempo de espera predeterminado (a menudo 60 segundos) después del cual devuelve un error. Para WebSocket, la conexión es persistente y las solicitudes se emparejan por ID. Si una solicitud tarda demasiado, el servidor puede no responder y el mecanismo de tiempo de espera del cliente se activa.

El WsProvider de polkadot.js tiene un tiempo de espera incorporado de 60 segundos para llamadas RPC individuales. Cuando ocurre un tiempo de espera, no lanza un error por defecto; en su lugar, emite un evento 'disconnected' e intenta reconectarse. Sin embargo, si la conexión se pierde, el proveedor puede no volver a suscribirse automáticamente a las suscripciones existentes, lo que lleva a una obsolescencia silenciosa. Este es un comportamiento documentado en la documentación de la API de polkadot.js.

La diferencia entre los tiempos de espera HTTP y WebSocket es crucial: las solicitudes HTTP son de un solo disparo, por lo que un tiempo de espera es un fallo claro. Las suscripciones WebSocket son de larga duración, por lo que un tiempo de espera en una suscripción significa que la suscripción está muerta, pero el cliente puede no saberlo hasta que intente usarla.

  • HTTP: la solicitud falla con un error si no hay respuesta dentro del tiempo de espera.
  • WebSocket: la solicitud puede quedarse colgada y el proveedor puede desconectarse sin lanzar una excepción.
  • Suscripciones: chain_newHead y chain_subscribeFinalizedHeads pueden quedar obsoletas después de una reconexión.
  • El tiempo de espera predeterminado de polkadot.js es de 60 segundos, pero se puede configurar.

Por qué WsProvider se desconecta silenciosamente (y cómo detectarlo)

La pregunta de Substrate Stack Exchange '¿Por qué el error de tiempo de espera de WsProvider no se captura en try-catch?' destaca un error común: envolver una llamada RPC en try-catch no captura los tiempos de espera porque el error se emite como un evento, no se lanza. El WsProvider emite un evento 'disconnected' cuando se pierde la conexión y un evento 'error' para errores de protocolo. Si no escuchas estos eventos, tu aplicación se colgará.

Para manejar esto, debes adjuntar listeners de eventos al proveedor e implementar una estrategia de reconexión. El proveedor tiene una reconexión automática incorporada, pero no vuelve a suscribirse a las suscripciones anteriores. Debes volver a suscribirte manualmente después de una reconexión.

Aquí tienes un ejemplo mínimo de cómo escuchar desconexiones y errores:

  • Escucha los eventos 'disconnected' y 'error' en el proveedor.
  • Usa un indicador para rastrear el estado de la conexión.
  • Después de la reconexión, vuelve a suscribirte a todas las suscripciones activas.
  • Considera usar una biblioteca como @polkadot/api-contract que maneje la reconexión internamente.
const { ApiPromise, WsProvider } = require('@polkadot/api');

const provider = new WsProvider('wss://rpc.polkadot.io');
provider.on('disconnected', () => {
  console.log('Proveedor desconectado');
  // Establece un indicador para activar la re-suscripción
});
provider.on('error', (err) => {
  console.error('Error del proveedor:', err);
});

const api = await ApiPromise.create({ provider });
// ... tu código

Límites de RPC de bloques: Cabeza finalizada vs. retraso de la cabeza mejor

Los nodos de Polkadot exponen dos RPC relacionados con bloques: chain_getHeader (cabeza mejor) y chain_getFinalizedHead (cabeza finalizada). La cabeza finalizada va por detrás de la cabeza mejor por unos pocos bloques debido a la finalidad. Si tu aplicación espera un bloque finalizado que está muy por detrás, puedes encontrarte con un tiempo de espera si el nodo está sincronizando o si la red está congestionada.

Los endpoints públicos a menudo tienen límites de tasa en los RPC de bloques para prevenir abusos. Por ejemplo, un proveedor puede limitar el número de solicitudes por segundo. Si excedes esto, puedes obtener un tiempo de espera o un error. Este es un comportamiento documentado para muchos proveedores, pero los límites exactos varían. Por ejemplo, el servicio de API de OnFinality proporciona endpoints dedicados con límites más altos.

Para evitar tiempos de espera, usa el RPC apropiado para tu caso de uso: si necesitas el estado más reciente, usa la cabeza mejor; si necesitas finalidad, usa la cabeza finalizada. Además, considera usar suscripciones en lugar de sondeo para reducir la carga de solicitudes.

  • chain_getHeader devuelve el encabezado del mejor bloque.
  • chain_getFinalizedHead devuelve el encabezado del último bloque finalizado.
  • La cabeza finalizada va por detrás de la cabeza mejor por unos pocos bloques.
  • Los endpoints públicos pueden limitar la tasa de RPC de bloques; consulta la documentación del proveedor.

Llamadas RPC pesadas: state_getPairs y escaneos de archivo grandes

Llamadas como state_getPairs o state_getKeysPaged pueden ser extremadamente pesadas porque iteran sobre todo el almacenamiento. En una cadena grande como Polkadot, esto puede tomar minutos y casi con certeza agotará el tiempo en endpoints públicos. Incluso en un nodo dedicado, tales llamadas pueden bloquear el hilo RPC y causar que otras solicitudes agoten el tiempo.

El enfoque recomendado es usar los RPC de almacenamiento (state_getStorage) para lecturas ligeras de claves específicas, y usar el procesamiento por lotes para múltiples lecturas. Para iterar sobre el almacenamiento, usa state_getKeysPaged con un tamaño de página pequeño y procesa las páginas incrementalmente, pero ten en cuenta los límites de tasa.

Otra técnica es usar state_queryStorage para consultar el almacenamiento histórico en un bloque específico, lo que puede ser más eficiente que escanear todo el estado.

  • state_getPairs y state_getKeysPaged son pesados y pueden agotar el tiempo.
  • Usa state_getStorage para lecturas de una sola clave.
  • Usa state_getKeysPaged con paginación para escaneos grandes.
  • Considera state_queryStorage para consultas históricas.
  • Procesa múltiples lecturas por lotes usando el lote JSON-RPC o api.rpc.batch.

Ejemplo ejecutable: Bucle de tiempo de espera, reconexión y re-suscripción

El siguiente script de Node.js demuestra un patrón robusto: crea un ApiPromise con un tiempo de espera personalizado, escucha desconexiones y se vuelve a suscribir a chain_newHead después de una reconexión. También muestra cómo manejar los tiempos de espera en llamadas individuales usando Promise.race.

Para ejecutarlo, instala @polkadot/api y @polkadot/util-crypto, luego ejecuta con Node.js. El script registrará los nuevos encabezados de bloque y manejará las reconexiones con elegancia.

  • Instala las dependencias: npm install @polkadot/api @polkadot/util-crypto
  • Ejecuta con: node script.js
  • Salida esperada: registra nuevos números de bloque y mensajes de reconexión.
const { ApiPromise, WsProvider } = require('@polkadot/api');

const WS_URL = 'wss://rpc.polkadot.io';
const TIMEOUT = 60000; // 60 segundos

async function main() {
  const provider = new WsProvider(WS_URL, false); // autoConnect false para control manual
  provider.on('disconnected', () => {
    console.log('Desconectado, intentando reconectar...');
    // El proveedor se reconecta automáticamente, pero necesitamos re-suscribirnos
  });
  provider.on('error', (err) => {
    console.error('Error del proveedor:', err);
  });

  const api = await ApiPromise.create({ provider, timeout: TIMEOUT });
  await provider.connect();

  let unsubscribe;
  const subscribe = async () => {
    if (unsubscribe) await unsubscribe();
    unsubscribe = await api.rpc.chain.subscribeNewHeads((header) => {
      console.log(`Nuevo bloque #${header.number}`);
    });
  };

  await subscribe();

  // Ejemplo de un tiempo de espera en una llamada pesada
  const heavyCall = api.rpc.state.getPairs('0x');
  const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Tiempo de espera')), TIMEOUT));
  try {
    const result = await Promise.race([heavyCall, timeoutPromise]);
    console.log('Longitud del resultado de la llamada pesada:', result.length);
  } catch (err) {
    console.error('La llamada pesada falló:', err.message);
  }

  // Mantener el proceso vivo
  process.on('SIGINT', async () => {
    if (unsubscribe) await unsubscribe();
    await api.disconnect();
    process.exit(0);
  });
}

main().catch(console.error);

Fallos comunes y soluciones

El problema de paritytech/polkadot-sdk '[rpc] Los RPC de Polkadot assethub dejan de proporcionar aleatoriamente...' describe un escenario donde los endpoints RPC en Asset Hub dejan de responder intermitentemente. Esto a menudo se debe al agotamiento de recursos del nodo o problemas de red. La solución implica monitorear la salud del nodo, implementar reintentos y usar múltiples endpoints.

Otro fallo común es el 'Error al ejecutar la llamada RPC' de Substrate Stack Exchange, que a menudo ocurre cuando una llamada excede el tiempo de espera del servidor. La solución es dividir la llamada en partes más pequeñas o usar un método RPC diferente.

Para suscripciones WebSocket, un fallo común es que la suscripción no reciba actualizaciones después de una reconexión. La solución es volver a suscribirse en el evento 'connected', como se muestra en el ejemplo.

  • Paradas intermitentes de RPC: usa múltiples endpoints y conmutación por error.
  • Tiempos de espera de llamadas pesadas: usa paginación o RPC más ligeros.
  • Obsolescencia de suscripciones: vuelve a suscribirte al reconectar.
  • Límites de tasa: respeta los límites del proveedor y usa el procesamiento por lotes.
  • Problemas de sincronización del nodo: verifica la salud del nodo y usa un proveedor fiable.

Compensaciones y limitaciones

Si bien establecer un tiempo de espera personalizado e implementar lógica de reconexión mejora la fiabilidad, añade complejidad. Un tiempo de espera más corto puede causar falsos positivos en redes lentas, mientras que un tiempo de espera más largo puede retrasar la detección de errores. El tiempo de espera óptimo depende de tu caso de uso y las condiciones de la red.

El procesamiento por lotes reduce el número de viajes de ida y vuelta, pero puede aumentar la carga en el nodo. Los endpoints públicos pueden tener límites en el tamaño del lote. Siempre consulta la documentación del proveedor para conocer los límites específicos.

Usar un nodo dedicado o un servicio RPC premium como el servicio de API de OnFinality puede proporcionar un rendimiento más consistente y límites de tasa más altos, pero tiene un costo. La compensación es entre fiabilidad y gasto.

  • Tiempos de espera personalizados: equilibrio entre falsos positivos y errores retrasados.
  • Procesamiento por lotes: reduce los viajes de ida y vuelta pero puede alcanzar los límites de lote.
  • Endpoints públicos vs premium: fiabilidad vs costo.
  • WebSocket vs HTTP: WebSocket es mejor para suscripciones, HTTP para consultas puntuales.

Próximos pasos: Construye consultas fiables en Polkadot

Para profundizar, explora el tutorial de RPC WebSocket de Polkadot para patrones de suscripción, y consulta los endpoints RPC de Polkadot para una lista de endpoints públicos y premium. Si estás construyendo aplicaciones de producción, considera usar el servicio de API de OnFinality para endpoints dedicados y precios de RPC para planes rentables.

Recuerda siempre probar tu lógica de tiempo de espera y reconexión bajo condiciones de red reales. Usa el centro de aprendizaje de OnFinality para más tutoriales sobre Polkadot y otras cadenas. Para una comprensión más amplia de Polkadot, consulta la visión general de la red Polkadot.

Nunca te preocupes por la infraestructura nuevamente

OnFinality elimina la carga pesada de DevOps para que puedas construir de forma más inteligente y rápida.

Comenzar