Logo
Nuevos usuarios de RPC: 35% de descuento el primer mesVer oferta
OnFinality Learn
Guías de red y protocolo14 min de lectura

Solana getBlocks y slots omitidos: indexación de bloques sin lagunas

Un método determinista para recorrer rangos de slots de Solana con getBlocks, clasificando slots omitidos, bloques faltantes y lagunas de retención sin tratar cada null como un error.

TL;DR

Los números de slot de Solana no son números de bloque: los slots avanzan según un calendario fijo, pero un slot puede omitirse cuando su líder no produce un bloque, por lo que un rango de slots es una cota superior de bloques, nunca una igualdad. El método getBlocks devuelve solo los slots confirmados que contienen un bloque, en orden descendente, lo que significa que una brecha numérica entre los números de slot devueltos es evidencia de slots omitidos y no un error de la API. Un indexador sin lagunas recorre el rango en fragmentos, registra el conjunto de slots devueltos y clasifica cada slot como bloque presente, omitido o sin resolver, donde sin resolver significa que el recorrido no pudo probar ninguno de los dos estados. Los niveles de commitment y las ventanas de retención afectan lo que se puede resolver, por lo que un recorrido histórico reproducible fija un commitment y termina por debajo de la cabeza finalizada. El tamaño de fragmento, la ventana de retención y los límites por llamada son propiedades del proveedor y del clúster, documentadas y variables según el proveedor, no constantes del protocolo.

Los números de slot y los números de bloque son identificadores distintos

En las cadenas EVM, la altura de bloque es una secuencia densa: cada incremento corresponde a un bloque producido, por lo que latestBlock - fromBlock + 1 es igual al número de bloques en un rango. Solana rompe esa suposición. Los slots son unidades de tiempo programadas asignadas a líderes, y un slot puede omitirse cuando su líder no produce un bloque. La referencia oficial del método getBlocks de Solana documenta que el método devuelve los slots confirmados que contienen bloques, no todos los slots del intervalo solicitado.

La consecuencia práctica es que end_slot - start_slot + 1 es una cota superior del número de bloques en un rango, nunca una igualdad. Un indexador que asume densidad subcontará bloques o clasificará erróneamente lagunas legítimas como pérdida de datos. Si vienes de un flujo de reconciliación EVM, el modelo mental de reconciliación de indexadores EVM bloque a bloque no se traslada directamente; Solana requiere un recorrido consciente de los slots.

  • Un slot es una oportunidad programada para producir un bloque; un bloque es el artefacto producido.
  • Un slot omitido no tiene bloque y es un comportamiento esperado, no un error.
  • end_slot - start_slot + 1 es una cota superior de bloques, no un recuento.
  • getBlocks devuelve solo los slots que contienen un bloque, en orden descendente.

Semántica documentada de getBlocks y orden descendente

La referencia oficial documenta start_slot, end_slot y commitment como parámetros, con un rango inclusivo y un array descendente de números de slot confirmados que contienen bloques. Como el array es descendente, el slot devuelto más bajo es el cursor natural para la siguiente página cuando se recorre hacia atrás, y el slot devuelto más alto indica dónde comienza realmente el fragmento actual.

Una secuencia contigua de números de slot devueltos con una brecha numérica entre ellos es evidencia de slots omitidos, no un defecto de la API. Por ejemplo, una respuesta que contiene 100, 99, 97, 96 indica que el slot 98 se omitió. La brecha es la señal que quieres registrar, no suprimir. La guía de endpoints RPC cubre cómo la selección de endpoint y el comportamiento del proveedor pueden afectar qué slots se resuelven.

  • Los límites son inclusivos: se consideran tanto start_slot como end_slot.
  • Los resultados son descendentes, por lo que el slot devuelto más bajo es el siguiente end_slot exclusivo.
  • Una brecha numérica entre slots devueltos es evidencia de un slot omitido.
  • No se garantiza una página completa; nunca asumas que se devolvió el tamaño del fragmento.

Por qué falla un bucle ingenuo de getBlock por slot

Un bucle como for (let s = start; s <= end; s++) getBlock(s) es incorrecto de tres maneras concretas. Primero, trata un null como un error fatal, cuando un slot omitido legítimamente no tiene bloque y la referencia de getBlock documenta que un slot omitido devuelve null. Segundo, consume una solicitud por slot incluso para las brechas, lo que es derrochador en rangos grandes. Tercero, no puede distinguir un null causado por un slot omitido de un null causado por límites de retención o por un nivel de commitment incorrecto.

El enfoque correcto es usar getBlocks para descubrir qué slots contienen bloques y luego llamar a getBlock solo para esos slots. Esto reduce el volumen de solicitudes y hace explícita la clasificación. La misma disciplina de separar el descubrimiento de la recuperación aparece en paginación de Solana getSignaturesForAddress, donde el descubrimiento de firmas y la recuperación de transacciones son pasos distintos.

  • El null de getBlock es ambiguo sin contexto: omitido, fuera de retención o commitment incorrecto.
  • Los bucles por slot desperdician solicitudes en slots que nunca tendrán bloques.
  • El descubrimiento mediante getBlocks debe preceder a la recuperación mediante getBlock.
  • La ambigüedad debe registrarse como sin resolver, no descartarse silenciosamente.

Un algoritmo de detección de lagunas para rangos de slots

El algoritmo recorre el rango solicitado en fragmentos, registra el conjunto de slots devueltos y clasifica cada slot del rango solicitado en uno de tres estados: bloque presente, omitido o sin resolver. Bloque presente significa que getBlocks devolvió el slot. Omitido significa que el slot cae dentro de un fragmento que se devolvió correctamente pero el slot estaba ausente de la respuesta. Sin resolver significa que el recorrido no pudo probar ninguno de los dos estados, por ejemplo porque el fragmento falló, el endpoint devolvió un error o el slot está cerca del límite de retención.

Los slots sin resolver deben registrarse en lugar de descartarse silenciosamente. Un indexador sin lagunas no es el que reporta cero lagunas; es el que da cuenta de cada slot del rango solicitado con una clasificación defendible. Este es el mismo principio que subyace a consultar datos históricos de Solana por RPC, donde los límites de retención cambian lo que se puede probar.

  • Bloque presente: getBlocks devolvió el slot en un fragmento exitoso.
  • Omitido: el slot estuvo ausente de una respuesta de fragmento exitosa.
  • Sin resolver: el fragmento falló o el slot está cerca de los límites de retención.
  • Cada slot del rango solicitado debe recibir exactamente una clasificación.

Recorredor ejecutable en Node.js con resumen por fragmento

El siguiente script de Node.js recorre un rango de slots en fragmentos, llama a getBlocks e imprime un resumen por fragmento como tabla. Usa un tamaño de fragmento y un commitment configurables, y registra los slots sin resolver cuando un fragmento falla. Reemplaza la URL del endpoint por la de tu propio proveedor; los números que imprime son para que los midas contra tu propio endpoint.

El script no asume una página completa. Registra el slot devuelto más bajo y lo usa como el siguiente end_slot exclusivo, lo cual es correcto para el orden descendente documentado por la referencia de getBlocks.

const ENDPOINT = process.env.SOLANA_RPC_URL || 'https://your-endpoint.example.com';
const COMMITMENT = 'finalized';
const CHUNK_SIZE = 1000;

async function rpc(method, params) {
  const res = await fetch(ENDPOINT, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params })
  });
  const json = await res.json();
  if (json.error) throw new Error(JSON.stringify(json.error));
  return json.result;
}

async function walkRange(startSlot, endSlot) {
  const rows = [];
  let cursor = endSlot;
  while (cursor >= startSlot) {
    const chunkStart = Math.max(startSlot, cursor - CHUNK_SIZE + 1);
    const span = cursor - chunkStart + 1;
    let returned = [];
    let unresolved = 0;
    try {
      returned = await rpc('getBlocks', [chunkStart, cursor, { commitment: COMMITMENT }]);
    } catch (err) {
      unresolved = span;
    }
    const returnedSet = new Set(returned);
    let skipped = 0;
    for (let s = chunkStart; s <= cursor; s++) {
      if (!returnedSet.has(s)) skipped++;
    }
    rows.push({
      requestedSpan: `${chunkStart}-${cursor}`,
      spanSize: span,
      returnedCount: returned.length,
      lowestReturned: returned.length ? Math.min(...returned) : null,
      detectedGaps: skipped,
      unresolved
    });
    if (returned.length === 0) break;
    cursor = Math.min(...returned) - 1;
  }
  return rows;
}

(async () => {
  const rows = await walkRange(250000000, 250010000);
  console.table(rows);
})();

Tabla de resultados para tus propias mediciones de endpoint

Ejecuta el recorredor contra tu propio endpoint y completa la tabla a continuación con tus mediciones. Los valores no son constantes del protocolo; dependen de tu proveedor, tu clúster y el nivel de commitment que fijes. Indica explícitamente que el tamaño de fragmento, la ventana de retención y los límites por llamada son propiedades del proveedor y del clúster, documentadas y variables según el proveedor.

Usa la tabla para comparar endpoints o niveles de commitment. Si los recuentos sin resolver son altos, el rango puede estar cerca de la retención o el endpoint puede estar limitando la tasa. Si las lagunas detectadas son inesperadamente altas, verifica que no estés mezclando niveles de commitment entre fragmentos.

  • Intervalo solicitado: el rango de slots inclusivo del fragmento.
  • Recuento devuelto: número de slots que devolvió getBlocks.
  • Más bajo devuelto: el siguiente end_slot exclusivo para el recorrido.
  • Lagunas detectadas: slots ausentes de un fragmento exitoso.
  • Sin resolver: slots que el recorrido no pudo clasificar.

Niveles de commitment y la cabeza finalizada

Los slots confirmados y finalizados pueden diferir para el fragmento más reciente, porque el commitment refleja distintos niveles de acuerdo del clúster. Un recorrido histórico reproducible debe fijar un commitment y terminar el rango por debajo de la cabeza finalizada, de modo que la clasificación sea estable entre ejecuciones. La guía de niveles de commitment y confirmación de transacciones en Solana explica cómo se relacionan estos niveles con la confirmación.

Si recorres los slots más recientes con commitment confirmado, un slot que parece omitido puede resolverse más tarde a medida que se produce un bloque o avanza el clúster. Para la indexación, fijar el commitment finalizado y terminar por debajo de la cabeza finalizada evita esta ambigüedad. Documenta el commitment que usaste junto a tu tabla de resultados.

  • Fija un solo commitment para todo el recorrido.
  • Termina el rango por debajo de la cabeza finalizada para reproducibilidad.
  • Confirmado y finalizado pueden diferir para el fragmento más reciente.
  • Registra el commitment usado junto con tus mediciones.

Límites de retención y clasificación fuera de retención

Los slots antiguos dejan de resolverse eventualmente porque el endpoint ya no los retiene. Un slot que no devuelve bloque porque está fuera de retención debe registrarse como fuera de retención, no como omitido. Confundir ambos corrompe tus estadísticas de lagunas y puede ocultar pérdida de datos real.

Las ventanas de retención son propiedades del proveedor y del clúster, documentadas y variables según el proveedor. Antes de recorrer un rango histórico, confirma la ventana de retención de tu endpoint. La página de la red Solana y el servicio de API describen cómo estructura el acceso OnFinality, mientras que la página de precios de RPC cubre consideraciones a nivel de plan. Para un tratamiento más amplio del acceso histórico, consulta consultar datos históricos de Solana por RPC.

  • Fuera de retención es una clasificación distinta de omitido.
  • Las ventanas de retención varían según el proveedor y el clúster.
  • Confirma la retención antes de recorrer rangos históricos.
  • Registra explícitamente los slots fuera de retención en tu salida.

Limitaciones y compensaciones del recorrido por rangos de slots

El recorrido por fragmentos reduce el volumen de solicitudes en comparación con los bucles de getBlock por slot, pero aún requiere una llamada a getBlocks por fragmento. Los fragmentos más grandes reducen el número de llamadas pero aumentan la probabilidad de alcanzar los límites por llamada, que son propiedades del proveedor y del clúster. Los fragmentos más pequeños son más resistentes pero más lentos.

La clasificación es tan buena como las respuestas del endpoint. Si un endpoint devuelve errores para un fragmento, esos slots quedan sin resolver y el recorrido no puede probar su estado. No existe una garantía a nivel de protocolo de que cada slot de un rango sea clasificable en una sola pasada. El centro de aprendizaje de OnFinality reúne guías relacionadas sobre indexación y comportamiento de RPC.

  • Los fragmentos más grandes reducen llamadas pero arriesgan límites por llamada.
  • Los fragmentos más pequeños son resistentes pero más lentos.
  • Los slots sin resolver son un resultado legítimo, no un fallo.
  • Ninguna pasada única garantiza la clasificación completa.

Solución de problemas comunes de detección de lagunas

Si tu recorrido reporta muchas lagunas, primero verifica si estás mezclando niveles de commitment entre fragmentos. Un fragmento confirmado seguido de un fragmento finalizado puede producir lagunas aparentes que en realidad son diferencias de commitment. Segundo, verifica si el rango se extiende más allá de la retención; los slots fuera de retención aparecerán como lagunas si no los clasificas por separado.

Si tu recorrido reporta muchos slots sin resolver, busca limitación de tasa o errores del endpoint. Reduce el tamaño del fragmento y reintenta los fragmentos fallidos. Si un fragmento falla de forma consistente, regístralo como sin resolver y continúa; no lo descartes silenciosamente. La guía de endpoints RPC cubre la selección de endpoints y consideraciones de failover.

  • Niveles de commitment mezclados pueden crear lagunas aparentes.
  • Los slots fuera de retención deben clasificarse por separado.
  • La limitación de tasa aumenta los recuentos sin resolver.
  • Reintenta los fragmentos fallidos; nunca los descartes silenciosamente.

Próximos pasos para indexadores en producción

Para la indexación en producción, persiste la clasificación de cada slot del rango solicitado, incluidos los estados sin resolver y fuera de retención. Programa re-recorridos para rangos sin resolver y para slots cerca de la cabeza finalizada. Fija un commitment y documéntalo junto a tus resultados.

Si estás construyendo sobre OnFinality, revisa la página de la red Solana y el servicio de API para opciones de endpoint, y consulta los precios de RPC para detalles a nivel de plan. Para patrones de indexación relacionados, consulta paginación de Solana getSignaturesForAddress y reconciliación de indexadores EVM bloque a bloque.

  • Persiste cada clasificación de slot, incluidos los sin resolver.
  • Re-recorre los rangos sin resolver según un calendario.
  • Fija y documenta tu nivel de commitment.
  • Revisa la retención del proveedor y los límites por llamada antes de escalar.

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