3 mejores prácticas para usar la API de reindexación y solucionar problemas con esta

blog-thumb-search-results-white.png

Al usar Elasticsearch quizá quieras mover los datos de un índice a otro, o incluso de un cluster de Elasticsearch a otro cluster de Elasticsearch. Existen diversas variaciones y características que se podrían usar, y la API de reindexación es una de ellas.

En este blog, veremos la API de reindexación, cómo saber si funciona, qué puede provocar potenciales fallas y cómo solucionar problemas.

Al finalizar el blog, comprenderás las opciones que ofrece la API de reindexación y cómo ejecutarla con confianza.

La API de reindexación es una de las API más útiles en distintos casos de uso:

  • Transferencia de datos entre clusters (Reindex from a remote cluster [Reindexación desde un cluster remoto])
  • Redefinición, cambio o actualización de mapeos
  • Procesamiento e indexación a través de pipelines de ingesta
  • Eliminación de documentos borrados para recuperar espacio de almacenamiento
  • División de índices grandes en grupos más pequeños a través de filtros de búsqueda

Al ejecutar la API de reindexación en índices medianos o grandes, es posible que la reindexación completa lleve más de 120 segundos, eso significa que no tendrás la respuesta final de la API de reindexación, no sabrás cuándo terminó, si funcionó o si hubo fallas.

Echemos un vistazo.

Síntoma: En las herramientas de desarrollo de Kibana, "Backend closed connection"

Cuando ejecutas la API de reindexación con índices medianos o grandes, la conexión entre el cliente y Elasticsearch agotará el tiempo de espera, pero eso no significa que la reindexación no se ejecutará.

Problema

El cliente cerrará los sockets inactivos tras N segundos, en Kibana, por ejemplo, si la operación de reindexación no se pudo completar en menos de 120 segundos (valor predeterminado de server.socketTimeout en la versión 7.13), verás el mensaje "Backend closed connection" (Conexión cerrada de backend).

Solución 1: Obtener la lista de tareas en ejecución en el cluster

No es un verdadero problema, incluso si ves este mensaje en Kibana, Elasticsearch estará ejecutando en segundo plano la API de reindexación.

Puedes hacer un seguimiento de la ejecución de la API de reindexación y ver todas las métricas con la API _task:

GET _tasks?actions=*reindex&wait_for_completion=false&detailed

Esta API te mostrará todas las API de reindexación que actualmente se están ejecutando en el cluster de Elasticsearch; si no ves tu API de reindexación en esta lista, significa que ya finalizó. 

Como puedes ver en la imagen, tenemos detalles sobre los documentos creados o actualizados, e incluso sobre los conflictos.

Solución 2: Almacenar el resultado de reindexación en _tasks

Si sabes que la operación de reindexación tomará más de 120 segundos (120 segundos es el tiempo de espera de las herramientas de desarrollo de Kibana), puedes almacenar los resultados de la API de reindexación usando el parámetro de búsqueda wait_for_completion= false; esto te permitirá obtener el estado al finalizar la API de reindexación con la API _task (también puedes obtener el documento desde el índice ".tasks", como se explica en la documentación de wait_for_completion=false).

POST _reindex?wait_for_completion=false
{
  "conflicts": "proceed",
  "source": {
    "index": "<source-index-name>"
  },
  "dest": {
    "index": "<dest-index-name>"
  }
}

Cuando ejecutas la reindexación con "wait_for_completion=false", la respuesta será similar a lo siguiente:

{
  "task" : "a9Aa_I_ZSl-4bjR5vZLnSA:247906"
}

Tendrás que mantener la tarea que se proporciona aquí para buscar los resultados de reindexación (verás la cantidad de documentos creados, conflictos o incluso errores, y al finalizar, verás el tiempo que demoró, la cantidad de batches, etc.):

GET _tasks/a9Aa_I_ZSl-4bjR5vZLnSA:247906

Síntoma: La API de reindexación no está en la lista de la API _task

Si con la API mencionada antes no puedes encontrar la operación de API de reindexación, podrían ser distintos problemas, los veremos uno por uno.

Problema

Si la API de reindexación no está incluida en la lista, significa que finalizó porque no había más documentos para reindexar o porque hubo un error.

Usaremos la API _cat count para ver la cantidad de documentos almacenados en ambos índices, si el número no es el mismo, significa que hubo alguna falla en la ejecución de la API de reindexación.

GET _cat/count/<source-index-name>?h=count
GET _cat/count/<dest-index-name>?h=count

Tendrás que reemplazar / con los nombres de índices que usas en tu API de reindexación.

Solución 1: Es un problema de conflicto

Uno de los errores más frecuentes son los conflictos; de forma predeterminada, la API de reindexación interrumpirá el proceso si encuentra algún conflicto.

Ahora tenemos dos opciones:

  1. Configurar "conflicts" como "proceed", lo que permitirá a la API de reindexación ignorar los documentos que no se pudieron indexar e indexar el resto.
  2. O tenemos la opción de solucionar los conflictos para poder reindexar todos los documentos.

La primera opción de configuración de conflictos se verá así:

POST _reindex
{
  "conflicts": "proceed",
  "source": {
    "index": "<source-index-name>"
  },
  "dest": {
    "index": "<dest-index-name>"
  }
}

En la segunda opción, buscaremos y solucionaremos los errores que producen el conflicto:

  • La mejor práctica que te permitirá evitar este problema es definir un mapeo o plantilla en el índice de destino. En el 99 % de los casos, esos errores son tipos de campo que no coinciden entre el índice de origen y el de destino.
  • Si el problema perdura incluso después de definir el mapeo o una plantilla, significa que algunos documentos no pudieron indexarse y el error no se registrará de forma predeterminada. Debemos permitir al logger ver los errores en el log de Elasticsearch.
PUT /_cluster/settings
{
  "transient": {
    "logger.org.elasticsearch.action.bulk.TransportShardBulkAction":"DEBUG"
  }
}
  • Con el logger habilitado, debemos ejecutar la API de reindexación una vez más; de ser posible, configura "conflicts" como "proceed" la cantidad de veces que tengas más de un campo con conflictos entre el índice de origen y el de destino.
  • Ahora que la API de reindexación se está ejecutando, usaremos grep/search en los logs con el mensaje "failed to execute bulk item" (no se pudo ejecutar el elemento en masa) o "MapperParsingException".
failed to execute bulk item (index) index {[my-dest-index-00001][_doc][11], source[{
  "test-field": "ABC"
}

O

 "org.elasticsearch.index.mapper.MapperParsingException: failed to parse field [test-field] of type [long] in document with id '11'. Preview of field's value: 'ABC'",
                "at org.elasticsearch.index.mapper.FieldMapper.parse(FieldMapper.java:216) ~[elasticsearch-7.13.4.jar:7.13.4]",

Con este rastreo de pila ya tenemos suficiente información para comprender cuál es el conflicto, en mi API de reindexación, el índice de destino tiene un campo denominado [test-field] de tipo [long], y la API de reindexación intenta configurar este campo como una cadena 'ABC' ('ABC' se reemplazará con tu campo de contenido propio).

En Elasticsearch, puedes definir tipos de datos de campo, puedes configurarlos durante la creación del índice o usando plantillas. No puedes cambiar los tipos una vez que el índice se haya creado, tendrás que eliminar el índice de destino primero y luego configurar el nuevo mapeo corregido con las opciones proporcionadas antes.

  • Una vez que hayas arreglado los errores, recuerda pasar el logger a un modo menos detallado:
PUT /_cluster/settings
{
  "transient": {
    "logger.org.elasticsearch.action.bulk.TransportShardBulkAction":NULL
  }
}

Solución 2: No hay errores de conflicto, pero la reindexación continúa fallando

Si durante la ejecución de la reindexación encuentras este rastreo en tus logs de Elasticsearch:

'search_phase_execution_exception', 'No search context found for id [....]')

Puede haber varios motivos:

  1. El cluster tiene algunos problemas de inestabilidad y algunos nodos se reiniciaron o no estaban disponibles durante la ejecución de la reindexación.
    Si este es el motivo, antes de ejecutar la reindexación, asegúrate de que el cluster esté estable y de que todos los nodos de datos funcionen bien.
  2. Si realizarás una operación de reindexación de forma remota y sabes que la red entre los nodos no es confiable:
    • La API de snapshot es una excelente opción (como se describe en la conclusión de este blog).
  3. Podemos intentar desglosar manualmente la API de reindexación, esta operación te permitirá dividir el proceso de solicitud en partes más pequeñas (esta es una opción cuando usamos la API de reindexación dentro del mismo cluster).
  4. Si tu cluster de Elasticsearch tiene problemas de exceso de shards, alto uso de recursos o problemas de recolección de basura, podría agotarse el tiempo de espera durante la consulta de búsqueda scroll. El valor del tiempo de espera predeterminado de scroll es 5 minutos, por lo que podrías probar la configuración de scroll en la API de reindexación con un valor más alto.

POST _reindex?scroll=2h
{
  "source": {
"index": "<source-index-name>"
  },
  "dest": {
"index": "<dest-index-name>"
  }
}

Síntoma: "Node not connected" en tus logs de Elasticsearch

Siempre aconsejamos ejecutar la API de reindexación con un cluster estable y con estado verde, y el cluster necesitará suficiente capacidad para ejecutar acciones de búsqueda e indexación.

Problema

NodeNotConnectedException[[node-name][inet[ip:9300]] Node not connected]; ]

Si aparece este error en tus logs, significa que el cluster tiene problemas de estabilidad y conexión, y que no es solo una falla de la API de reindexación.

Imaginemos que conoces los problemas de conectividad, pero necesitas ejecutar la API de reindexación.

Solución

Soluciona los problemas de conectividad.

Imaginemos que conocemos los problemas de conectividad, pero necesitas ejecutar la API de reindexación; podríamos reducir las posibilidades de falla, pero no es una solución y no funcionará en todos los casos.

  • Mueve los shards del índice de origen o destino (primarios o réplica) fuera de los nodos con el problema de conectividad. Usa la API de filtrado de asignación para mover los shards.
  • También podrías quitar las réplicas en el índice de destino (solo en el índice de destino); esto acelerará la API de reindexación, y si la reindexación se ejecuta más rápido, habrá menos probabilidad de que se enfrente a una falla.
PUT /<dest-index-name>/_settings
{
  "index" : {
    "number_of_replicas" : 0
  }
}

Si ambas acciones no te permitieron lograr que la API de reindexación se complete correctamente, debes solucionar primero el problema de estabilidad. 

Síntoma: Sin errores en los logs, pero el recuento de documentos de ambos índices no coincide

En ocasiones, la API de reindexación se completa, pero el recuento de documentos en el origen no coincide con el de destino.

Problema

Si intentamos reindexar desde varias fuentes en un destino (combinar varios índices en uno), el problema podría ser la _id que asignaste a esos documentos.

Imagina que tenemos 2 índices de origen:

  • Índice A, con _id: 1 y mensaje:"Hello A"
  • Índice B, con _id: 1 y mensaje:"Hello B"

La combinación de ambos índices en C será:

  • Índice C, con _id: 1 y mensaje:"Hello B"

Ambos documentos tendrán la misma _id, por lo que el último documento indexado anulará el anterior.

Solución

Tienes distintas opciones como los pipelines de ingesta o puedes usar painless en tu API de reindexación. En este blog, usaremos la opción de script con "painless" en el cuerpo de la solicitud.

Es muy sencillo, simplemente usaremos la _id original y agregaremos el nombre del índice de origen:

POST _reindex
{
  "source": {
    "index": "<source-index-name>"
  },
  "dest": {
    "index": "<dest-index-name>"
  },
  "script": {
    "source": "ctx._id = ctx._id + '-' + ctx._index;"
  }
}

Y si tomamos nuestro ejemplo anterior:

  • Índice A, con _id: 1 y mensaje:"Hello A"
  • Índice B, con _id: 1 y mensaje:"Hello B"

La combinación de ambos índices en C será:

  • Índice C, con _id: 1-A y mensaje:"Hello A"
  • Índice C, con _id: 1-B y mensaje:"Hello B"

Conclusión

La API de reindexación es una excelente opción cuando necesitas cambiar el formato de algunos campos. Aquí incluiremos algunos aspectos clave que harán que la API de reindexación funcione lo mejor posible:

  1. Crea y define un mapeo (o una plantilla) para tu índice de destino.
  2. Ajusta el índice de destino de modo que la API de reindexación pueda indexar documentos lo más rápido posible. Tenemos una página de documentación con todas las opciones que te permitirán ajustar y acelerar la indexación.
    https://www.elastic.co/guide/en/elasticsearch/reference/current/tune-for-indexing-speed.html
  3. Si el índice de origen es de tamaño mediano o grande, define la configuración "wait_for_completion=false" para que los resultados de la API de reindexación se almacenen en la API _tasks.
  4. Divide tu índice en grupos más pequeños, puedes definir distintos grupos con una búsqueda (rango, términos, etc.) o divide la solicitud en partes más pequeñas usando la característica de desglose.
  5. La estabilidad es clave al ejecutar la API de reindexación, los índices involucrados en la API de reindexación deben tener estado verde (o amarillo, en el peor de los casos), luego asegúrate de que no haya GarbageCollections grandes en los nodos de datos y de que el uso de la CPU y el disco tengan valores normales.

A partir de la versión 7.11, lanzamos una característica nueva que te permitirá evitar la necesidad reindexar tus datos, se denomina "campos de tiempo de ejecución". Esta API nos permite corregir errores sin necesidad de reindexar los datos, dado que puedes definir campos de tiempo de ejecución en el mapeo de índice o en la solicitud de búsqueda. Ambas opciones te permitirán tener la flexibilidad de alterar el esquema de un documento tras la ingesta y generar campos que existan solo como parte de la consulta de búsqueda.

Un buen ejemplo de las capacidades de los campos de tiempo de ejecución es la posibilidad de crear un campo de tiempo de ejecución con el mismo nombre de un campo que ya existe en el mapeo; el campo de tiempo de ejecución refleja el campo mapeado, para probarlo, solo tienes que seguir los pasos indicados aquí.

Puedes ver más detalles sobre los campos de tiempo de ejecución en nuestra documentación o en el blog de primeros pasos.

Cuando intentes migrar datos de un cluster a otro, puedes usar la API de restauración de snapshot. Con los snapshots, puedes migrar los datos más rápido, dado que el cluster no necesita buscar para reindexar los datos. Debes  asegurarte de que ambos clusters tengan acceso al mismo repositorio de snapshots; aquí puedes encontrar más detalles sobre la API de snapshot.

Vimos las preguntas frecuentes sobre reindexación y las soluciones a errores comunes. Si en este punto estás atascado en la resolución de un problema, no dudes en ponerte en contacto. Estamos aquí para ayudarte ¡y nos complace hacerlo! Puedes ponerte en contacto con nosotros a través del debate de Elastic, el Slack de la comunidad de Elastic, consultoría, capacitación y soporte.