La salud de un nodo Polkadot vía RPC es un veredicto compuesto, no un único booleano. system_health devuelve peers, isSyncing y shouldHavePeers; system_syncState devuelve startingBlock, currentBlock y highestBlock; system_peers devuelve el detalle por par. Debido a que un nodo estancado puede seguir reportando isSyncing=false, la comprobación fiable compara system_syncState.currentBlock contra una cabeza de referencia (leída localmente vía chain_getHeader o desde un segundo endpoint) para derivar un delta de retraso, y luego lee system_health.peers para detectar un nodo sincronizado pero particionado. Este artículo fundamenta el contrato del método en la especificación JSON-RPC de Substrate, ofrece una sonda ejecutable de @polkadot/api que devuelve {healthy, reason} y muestra cómo integrar el veredicto en sondeo por intervalos, endpoints de salud y drenaje/failover.
La superficie de salud de Substrate y sus tres métodos autoritativos
Polkadot expone una superficie de salud nativa sobre JSON-RPC que precede y es independiente de cualquier capa de compatibilidad con EVM. La especificación JSON-RPC de Substrate define tres métodos que importan aquí: system_health, system_syncState y system_peers. Cada uno es autoritativo para un hecho distinto, y ninguno por sí solo constituye un veredicto completo de salud.
system_health es autoritativo para el número de pares y la intención de sincronización. Devuelve peers (el número de pares conectados), isSyncing (un booleano) y shouldHavePeers (un booleano). system_syncState es autoritativo para el progreso de bloques: startingBlock, currentBlock y highestBlock. system_peers es autoritativo para el detalle por par, incluidos el id del par, el rol, el mejor hash y el mejor número, lo que permite distinguir 'conectado a nadie' de 'conectado a pares que a su vez están retrasados'.
Antes de confiar en cualquiera de esos campos, lee el preámbulo de cordura: system_name, system_version y system_chain. Estos indican con qué software y con qué cadena estás hablando realmente. Una sonda que asume Polkadot pero aterriza en una testnet o en un nodo configurado de forma distinta producirá un veredicto sobre el sistema equivocado. Los accesores tipados para estas llamadas están documentados en la documentación de la API de Polkadot-JS, que es la referencia utilizada por el cliente de ejemplo a continuación.
- system_health: peers, isSyncing, shouldHavePeers — número de pares e intención de sincronización.
- system_syncState: startingBlock, currentBlock, highestBlock — progreso de bloques.
- system_peers: rol, mejor hash y mejor número por par — calidad de los pares.
- system_name / system_version / system_chain — preámbulo de cordura de identidad.
Llamadas JSON-RPC en crudo para los mismos campos de salud
El cliente tipado es cómodo, pero el transporte subyacente es JSON-RPC 2.0 simple, por lo que los mismos campos pueden leerse con una sola petición curl por método. Esto es útil cuando quieres confirmar qué está enviando realmente la librería, cuando depuras un proveedor que reescribe respuestas o cuando escribes una sonda en un lenguaje sin cliente de Substrate.
El fragmento siguiente emite las tres llamadas de salud más chain_getHeader contra un nodo local. Cada petición es un sobre JSON-RPC estándar con un nombre de método y un array de params vacío; la respuesta lleva el objeto result descrito en la especificación JSON-RPC de Substrate. Ejecuta las llamadas por separado para que un fallo en un método no oculte los demás, y compara el currentBlock devuelto con el número del header para derivar el mismo delta de retraso que calcula la sonda Node.js.
# system_health — peers, isSyncing, shouldHavePeers
curl -s -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"system_health","params":[]}' \
http://127.0.0.1:9933
# system_syncState — startingBlock, currentBlock, highestBlock
curl -s -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"system_syncState","params":[]}' \
http://127.0.0.1:9933
# system_peers — per-peer role, best hash and best number
curl -s -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"system_peers","params":[]}' \
http://127.0.0.1:9933
# chain_getHeader — local reference head for the lag delta
curl -s -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"chain_getHeader","params":[]}' \
http://127.0.0.1:9933Por qué isSyncing=false no es liveness
El error de producción más común es tratar system_health.isSyncing=false como prueba de que un nodo está sano. No lo es. isSyncing refleja si el nodo cree que está poniéndose al día activamente; un nodo que se ha estancado —por ejemplo, porque perdió sus pares, sufrió un problema de disco o está atascado en un bloque defectuoso— puede reportar isSyncing=false mientras su currentBlock deja de avanzar. La bandera describe intención, no progreso.
La señal fiable de liveness es un delta de retraso: compara system_syncState.currentBlock contra una cabeza de referencia. Puedes obtener la cabeza de referencia localmente con chain_getHeader (el header lleva el número de bloque), o externamente desde un segundo endpoint operado de forma independiente. La diferencia entre la cabeza de referencia y currentBlock es tu retraso. Un nodo cuyo retraso es estable y pequeño se mantiene al día; un nodo cuyo retraso crece monótonamente se está quedando atrás aunque isSyncing sea false.
El número de pares cierra la brecha restante. Un nodo puede estar completamente sincronizado y aun así estar particionado, sirviendo lecturas obsoletas porque ya no recibe bloques nuevos. Leer system_health.peers detecta ese caso: un nodo sincronizado con cero pares no es un objetivo de lectura saludable. Es la misma clase de problema descrito en Detección de retraso de cabeza y respuestas obsoletas en nodos RPC, pero expresado mediante campos de Substrate en lugar de números de bloque de EVM.
- isSyncing=false significa 'no está poniéndose al día ahora mismo', no 'está avanzando'.
- Delta de retraso = cabeza de referencia − system_syncState.currentBlock.
- Un delta de retraso creciente indica un nodo quedándose atrás.
- peers=0 en un nodo sincronizado indica una partición, no salud.
Advertencias específicas de Substrate: shouldHavePeers, highestBlock obsoleto y modos de nodo
shouldHavePeers tiene una semántica precisa que es fácil malinterpretar. Es false solo para roles genuinamente sin pares, como un cliente ligero, o un nodo configurado de forma que no participa en la red peer-to-peer. Para validadores y collators de parachain es true, porque se espera que esos roles mantengan pares. Si ves shouldHavePeers=false en un nodo que esperabas que fuera un nodo completo, trátalo como una señal de configuración, no como un error transitorio.
highestBlock de system_syncState puede estar obsoleto. Representa la visión del nodo sobre el mejor bloque de la red, que se deriva de sus pares; si el nodo está particionado o sus pares están retrasados, highestBlock subestima la verdadera cabeza de la red. Es exactamente por eso que el delta de retraso debe calcularse contra una cabeza de referencia independiente en lugar de contra highestBlock solo. Usar highestBlock como objetivo y como valor actual oculta el propio retraso que intentas detectar.
El modo del nodo cambia tus garantías de lectura. Un nodo completo poda el estado histórico, por lo que las lecturas de estado antiguo pueden fallar o no estar disponibles; un nodo de archivo conserva el estado histórico y puede servir esas lecturas. La documentación de Polkadot sobre operación de nodos y modos de sincronización es autoritativa sobre lo que significan 'syncing', 'full' y 'archive'. Tu sonda de salud debe registrar el modo que espera, porque un nodo completo perfectamente sano seguirá fallando consultas de estado histórico que un nodo de archivo sí serviría.
- shouldHavePeers=false: cliente ligero o configuración sin pares.
- shouldHavePeers=true: validadores y collators de parachain.
- highestBlock se deriva de los pares y puede subestimar la cabeza real.
- Los nodos completos podan el estado; los de archivo lo conservan — garantías de lectura distintas.
Una sonda Node.js ejecutable que devuelve {healthy, reason}
El ejemplo siguiente usa @polkadot/api, cuyos accesores tipados system.* están documentados en la documentación de la API de Polkadot-JS. Lee el preámbulo de identidad, llama a system.health(), system.syncState() y system.peers(), y luego lee una cabeza de referencia local con chain.getHeader(). Calcula el delta de retraso y devuelve un veredicto estructurado. La Especificación JSON-RPC 2.0 rige el sobre de petición/respuesta que la librería emite por debajo.
La sonda es deliberadamente conservadora: falla en cerrado ante campos faltantes, porque un endpoint gestionado por un proveedor puede ocultar u omitir algunos campos del sistema. Trata un campo faltante como 'desconocido', no como 'saludable'. Ejecútala contra tu propio endpoint y registra la salida en la tabla de resultados de la siguiente sección.
// probe.mjs — run with: node probe.mjs wss://your-endpoint
import { ApiPromise, WsProvider } from '@polkadot/api';
const endpoint = process.argv[2];
if (!endpoint) { console.error('usage: node probe.mjs <ws-endpoint>'); process.exit(2); }
const MAX_LAG = 5; // blocks of tolerance before flagging lag
const MIN_PEERS = 1; // a synced node with 0 peers is partitioned
async function probe(endpoint) {
const api = await ApiPromise.create({ provider: new WsProvider(endpoint) });
try {
// 1. Identity sanity preamble
const [name, version, chain] = await Promise.all([
api.rpc.system.name(),
api.rpc.system.version(),
api.rpc.system.chain(),
]);
// 2. Native health surface
const health = await api.rpc.system.health();
const sync = await api.rpc.system.syncState();
const peers = await api.rpc.system.peers();
// 3. Local reference head via chain_getHeader
const header = await api.rpc.chain.getHeader();
const referenceHead = header.number.toNumber();
const currentBlock = sync.currentBlock.toNumber();
const lag = referenceHead - currentBlock;
const peerCount = health.peers.toNumber();
const isSyncing = health.isSyncing.valueOf();
const shouldHavePeers = health.shouldHavePeers.valueOf();
let healthy = true;
let reason = 'ok';
if (lag > MAX_LAG) { healthy = false; reason = `lag=${lag} exceeds ${MAX_LAG}`; }
else if (peerCount < MIN_PEERS && shouldHavePeers) {
healthy = false; reason = `peers=${peerCount} but shouldHavePeers=true`;
} else if (isSyncing && lag > MAX_LAG) {
healthy = false; reason = 'syncing and behind reference head';
}
return {
healthy, reason,
identity: { name, version, chain: chain.toString() },
health: { peers: peerCount, isSyncing, shouldHavePeers },
sync: {
startingBlock: sync.startingBlock.toNumber(),
currentBlock,
highestBlock: sync.highestBlock.toNumber(),
},
referenceHead,
lag,
peerSample: peers.slice(0, 3).map((p) => ({
peerId: p.peerId.toString(),
role: p.role.toString(),
bestNumber: p.bestNumber.toNumber(),
})),
};
} finally {
await api.disconnect();
}
}
probe(endpoint)
.then((r) => { console.log(JSON.stringify(r, null, 2)); process.exit(r.healthy ? 0 : 1); })
.catch((e) => { console.error('probe failed:', e.message); process.exit(3); });Tabla de resultados para completar contra tu propio endpoint
Ejecuta la sonda anterior contra tu propio endpoint en varias horas del día y registra los valores a continuación. El objetivo no es una única lectura sino una línea base: quieres saber cómo son el retraso y el número de pares normales para tu endpoint, de modo que un umbral de alerta sea significativo. No copies umbrales de otro proveedor; mide los tuyos.
Rellena una fila por ejecución. Si falta un campo, escribe 'desconocido' en lugar de adivinar, y anótalo — un endpoint gestionado por un proveedor que oculta campos del sistema cambia lo que puedes afirmar.
- Timestamp | Endpoint | chain | version | peers | isSyncing | shouldHavePeers | currentBlock | highestBlock | referenceHead | lag | healthy | reason
- Fila de ejemplo: 2026-09-20T00:00Z | wss://… | Polkadot | <version> | <n> | false | true | <n> | <n> | <n> | <n> | true | ok
- Repite al menos tres veces en una ventana de 24 horas para ver la varianza del retraso.
- Registra el modo del nodo (completo o archivo) junto al endpoint.
Composición del veredicto compuesto
Un solo método no puede producir un veredicto fiable, así que compónlos en un orden fijo. Primero, confirma la identidad con system_name, system_version y system_chain. Segundo, lee system_syncState y calcula el retraso contra una cabeza de referencia. Tercero, lee system_health.peers y shouldHavePeers para detectar partición. Cuarto, muestrea system_peers para ver si tus pares están ellos mismos cerca de la cabeza.
La lógica de decisión es pequeña: no saludable si el retraso supera tu tolerancia medida; no saludable si shouldHavePeers es true y peers es cero; no saludable si isSyncing es true y el retraso crece en sondeos consecutivos. Todo lo demás es saludable. Esto refleja el enfoque agnóstico de cadena de Monitoreo de nodos RPC: métricas, alertas y failover, pero los campos son nativos de Substrate en lugar de contadores de Prometheus.
- Identidad → retraso de syncState → pares de health → muestra de peers.
- Falla si el retraso supera la tolerancia, si hay partición o si el retraso crece mientras sincroniza.
- Mantén el veredicto como {healthy, reason} para que quienes llaman puedan registrar una causa.
Arquitectura de control: sonda por intervalos, endpoint de salud y failover
En producción, ejecuta la sonda en un intervalo y expón el veredicto en un endpoint de salud interno. Tu aplicación debe leer ese endpoint antes de enrutar tráfico, y drenar el endpoint cuando el veredicto cambie a no saludable. Es el mismo patrón de control usado para endpoints de EVM, pero los campos de disparo son el retraso y el número de pares de Substrate en lugar de la altura de bloque de EVM.
Configura el failover de modo que un drenaje mueva el tráfico a un segundo endpoint, y luego vuelve a sondear el endpoint drenado antes de devolverlo al grupo. Debido a que un nodo puede recuperar su número de pares rápidamente pero seguir retrasado, exige dos sondeos saludables consecutivos antes de readmitir un endpoint. Las características de latencia de los endpoints entre los que eliges se cubren por separado en Latencia RPC de Polkadot: medición y optimización.
- La sonda por intervalos escribe {healthy, reason} en un endpoint de salud interno.
- La aplicación lee la salud antes de enrutar; drena si no es saludable.
- Failover a un segundo endpoint; exige dos sondeos saludables para readmitir.
- Registra la cadena de reason para que los incidentes sean diagnosticables.
Modos de fallo y lista de verificación de solución de problemas
Cuando la sonda reporta no saludable, recorre la causa en lugar de reiniciar a ciegas. Un retraso que crece mientras peers está saludable suele significar que el nodo está limitado por CPU o disco. Un retraso que crece mientras peers es cero significa una partición de red. Un nodo que reporta isSyncing=true durante mucho tiempo está genuinamente poniéndose al día, lo cual es esperable tras un tiempo de inactividad pero no en estado estable.
Si la propia sonda no logra conectarse, eso es un problema de transporte, no un veredicto de salud — consulta Errores de timeout en RPC de Polkadot para esa clase de fallo. Si las lecturas de estado finalizado se comportan de forma extraña, revisa Finalidad en Polkadot: justificaciones GRANDPA y cabeza finalizada, porque la finalidad y el progreso de la cabeza son señales relacionadas pero distintas.
- Retraso creciente + pares saludables: sospecha saturación de recursos en el nodo.
- Retraso creciente + cero pares: sospecha partición de red.
- isSyncing=true persistente: el nodo se está poniendo al día tras inactividad.
- Fallo de conexión de la sonda: problema de transporte, no un veredicto de salud.
- Campos del sistema faltantes: el endpoint gestionado por el proveedor los oculta; marca desconocido.
Limitaciones y compensaciones
La superficie de salud nativa no está exenta de compensaciones. Sondear cuesta un viaje de ida y vuelta por método, así que una sonda de cuatro llamadas son cuatro peticiones; agrupa o reduce la frecuencia si sondeas muchos endpoints. Algunos nodos requieren que el servidor RPC esté expuesto externamente (el flag estilo rpc-external) antes de que estos métodos sean alcanzables, lo cual es una decisión de despliegue con implicaciones de seguridad.
Los endpoints gestionados por proveedores pueden ocultar u omitir algunos campos del sistema, lo que significa que tu sonda debe tratar los datos faltantes como desconocidos en lugar de saludables. Por último, la superficie de salud te informa sobre el nodo, no sobre la corrección de los datos que devuelve; un nodo puede estar sano y aun así estar en una bifurcación que no quieres. Para garantías a nivel de cadena, combina esta sonda con comprobaciones de finalidad.
- Cada método es un viaje de ida y vuelta; agrupa o reduce la frecuencia de sondeo.
- Puede requerirse exposición externa del RPC para que estos métodos sean alcanzables.
- Los endpoints gestionados por proveedores pueden ocultar campos — trata lo faltante como desconocido.
- La salud no es corrección; combínala con comprobaciones de finalidad.
Próximos pasos y lectura relacionada
Empieza ejecutando la sonda contra un endpoint de Polkadot y completando la tabla de resultados para tu propio entorno. Si necesitas un endpoint contra el que probar, consulta la página de la red Polkadot y la guía RPC de Polkadot para detalles de conexión. Para precios y opciones de servicio, revisa Precios de RPC y el servicio de API.
Para profundizar, lee los patrones de monitoreo agnósticos de cadena en Monitoreo de nodos RPC: métricas, alertas y failover, la discusión de retraso enmarcada en EVM en Detección de retraso de cabeza y respuestas obsoletas en nodos RPC, y el método de medición de latencia en Latencia RPC de Polkadot: medición y optimización. El hub de aprendizaje de OnFinality reúne el resto de la serie de infraestructura.
- Ejecuta la sonda, completa la tabla, fija umbrales desde tu propia línea base.
- Conéctate vía la página de la red Polkadot.
- Compara con Monitoreo de nodos RPC.
- Revisa Precios de RPC y el servicio de API.