Elasticsearch – MergeMappingException[Merge failed with failures {[mapper [_timestamp] has different store values]}]

Elasticsearch

Recientemente nos encontramos con este error trabajando con dos versiones de ES diferentes. La versión 1.4.x la teniamos instalada en la maquina de producción y integración, mientras que la versión 1.3.1 es la que teníamos en la maquina de desarrollo. En desarrollo mantuvimos la versión 1.3.1 ya que hacia unos meses que se preparo el entorno para la creación del primer prototipo del proyecto. Finalmente cuando se decidio realizar por fin el proyecto, creamos los nuevos entornos de producción e integración instalando las últimas versiones de ES.

El error nos lo encontramos cuando estabamos aplicando unos cambios en el entorno de integración. Los cambios eran tan simples como añadir un nuevo campo a uno de los documentos de nuestro índice, y en nuestro caso queríamos además indicar el mapping para ese nuevo campo.

Por tanto, el error solo nos lo encontramos en la version 1.4.4 de ES y no en la versión 1.3.1. Esto es así porque a partir de la versión 1.4.0 desde ES se introdujo unos cambios referentes a la incidencia https://github.com/elasticsearch/elasticsearch/pull/7614.

Descripción del error

El error MergeMappingException[Merge failed with failures {[mapper [_timestamp] has different store values]}] se produce cuando añadimos un mapping nuevo para un nuevo campo de nuestro documento. En las versiones de ES anteriores a la 1.4 se hace el merge del nuevo mapping junto con la definición de mapping actual del resto de campos del documento. En este caso al ser un nuevo mapping, el merge se hace automáticamente sin nigún tipo de problemas.

En versiones de ES a partir de la 1.4, además de hacer el merge del mapping de los campos de nuestro documento, también se hace el merge de otros campos, como el _timestamp, _all, _dynamic etc. En este caso es cuando podemos encontrarnos con este error.

Para entender cual es el problema, vamos a reproducirlo con un caso real y dar la solución al problema.

Sin embargo, debemos mencionar que al ser ES un producto en constante evolución, es posible que en versiones superiores a la 1.4 se cambie este comportamiento, y por tanto no se vuelva a reproducir.

Reproducción del error

Para reproducir el error vamos a crear un indice, unos documentos y el mapping correspondiente.

Primero vamos a realizar las pruebas sobre ES 1.3 y posteriormente repetiremos lo mismo sobre ES 1.4.

ES 1.3 – Creación del índice ‘myindex’ y el mapping para el tipo de documento ‘user’

Queremos hacer uso de campo _timetamp proporcionado por ES, de tal manera que cada alta o actualizaciones del documento se vea reflejada la fecha de creación o actualización.

Por defecto _timestamp esta deshabilitado (enabled) y no se almacena (store).

  • Crear el índice y mapping del document
    $ curl -XPUT 'http://localhost:9200/myindex?pretty'
    $ curl -XPUT 'http://localhost:9200/myindex/user/_mapping?pretty' -d '{
    "user" : {
            "_id": {
                    "path" : "user_id"
            },
            "_timestamp" : {
                    "enabled" : true,
                    "store" : true
            },
            "properties" : {
                            "user_id" : {
                                    "type" : "string",
                                    "index" : "not_analyzed"
                            }
            }
    }}'
    
  • Una vez creado el indice y el mapping para el tipo documento user podemos verificarlo con
    $ curl 'http://localhost:9200/myindex/_mapping/user?pretty'
    {
      "myindex" : {
            "mappings" : {
              "user" : {
                    "_id" : {
                      "path" : "user_id"
                    },
                    "_timestamp" : {
                      "enabled" : true,
                      "store" : true
                    },
                    "properties" : {
                      "name" : {
                            "type" : "string"
                      },
                      "user_id" : {
                            "type" : "string",
                            "index" : "not_analyzed"
                      }
                    }
              }
            }
      }
    }
    
  • Indexamos un documento ‘user’
    $ curl -XPUT 'http://localhost:9200/myindex/user/A001?pretty' -d '{
      "user_id": "A001",
      "name" : "Michael Jordan"
    }'
    
  • Consultamos el documento indexado
    $ curl 'http://localhost:9200/myindex/user/A001?pretty&fields=_timestamp,_source'
    
    # Respuesta
    #
    {
      "_index" : "myindex",
      "_type" : "user",
      "_id" : "A001",
      "_version" : 1,
      "found" : true,
      "_source":{
      "user_id": "A001",
      "name" : "Michael Jordan"
    },
      "fields" : {
            "_timestamp" : 1424956536469
      }
    }
    
  • Ahora vamos a añadir el mapping de un nuevo campo e indexar un nuevo documento
    # Modificamos el mapping del nuevo campo enabled
    $ curl -XPUT 'http://localhost:9200/myindex/_mapping/user' -d '{
            "user" : {
                    "properties" : {
                            "enabled" : {
                                    "type" : "boolean",
                                    "index": "not_analyzed"
                            }
                    }
            }
    }'
    
    # Indexamos un nuevo documento
    $ curl -XPUT 'http://localhost:9200/myindex/user/A002?pretty' -d '{
      "user_id": "A002",
      "name" : "Michael Jordan",
      "enabled" : true
    }'
    
  • Consultamos el documento
    curl ‘http://localhost:9200/myindex/user/A002?pretty&fields=_timestamp,_source’

Y como vemos en ES 1.3 no hemos tenido ningún problema.

ES 1.4 – Creación del índice ‘myindex’ y el mapping para el tipo de documento ‘user’

Vamos a repetir el mismo procedimiento realizado para ES 1.3 hasta el paso donde obtenemos el error que estamos comentado.

  • Añadir el mapping de un nuevo campo e indexar un nuevo documento
    $ curl -XPUT 'http://localhost:9200/myindex/_mapping/user?pretty' -d '{
            "user" : {
                    "properties" : {
                            "enabled" : {
                                    "type" : "boolean",
                                    "index": "not_analyzed"
                            }
                    }
            }
    }'
    

    y una vez lo ejecutamos obtenemos el error

    {
      "error" : "MergeMappingException[Merge failed with failures {[mapper [_timestamp] has different store values]}]",
      "status" : 400
    }
    
  • ¿y esto a que es debido?

    A que en las vesiones de ES 1.4 en adelante, se ha incluido los campos _timestamp, _all, _dynamic etc en el merge a la hora de modificar y añadir el mapping.

    Es decir se hace el merge de los mapping de las propiedad o campos de nuestro documento ‘user’, y ademas de los campos _timestamp etc antes mencionados. En el momento de hacer el merge ocurre un conflicto y el merge no se puede realizar automaticamente y ES nos avisa de esta situación.

    Lo que ocurre es lo siguiente, para nuestro documento, ES intenta hace el mapping de los campos de nuestro documento, y también del campo _timestamp.

    En el caso del mapping de las campos de nuestro documento, el merge se puede hacer automaticamente, ya que no existe ningún conflicto:

        # Mapping actual
        "name" : {
                "type" : "string"
        },
        "user_id" : {
                "type" : "string",
                "index" : "not_analyzed"
        }
    
        # Nuevo mapping
        "enabled" : {
                "type" : "boolean",
                "index": "not_analyzed"
        }
    

    Pero en el caso del mapping del campo _timestamp es cuando aparece el conflicto. Internamente ES mira el mapping actual para _timestamp de nuestro document, que es:

        "_timestamp" : {
          "enabled" : true,
          "store" : true
        }
    

    y lo compara con el _timestampo por defecto, el cual es enable a false y store a false. Es en ese momento cuando aparece el conflicto y obtenemos el error.

    En realidad no deberíamos obtener este error, ya que cuando pasamos el nuevo mapping no hacemos mención del campo _timestamp.

    Es por ese motivo por el que pensamos que puede tratarse de un BUG de ES 1.4, y seguramente terminemos poniendo una incidencia en https://github.com/elasticsearch/elasticsearch/, para que nos puedan aclarar esto.

Soluciones

Como sabemos cual es el problema, podemos decirle a ES que ignore los conflictos, y termine realizando el mapping de los campos que puede. Esto lo haremos con el parámetro ignore_conflicts=true:

$ curl -XPUT 'http://localhost:9200/myindex/_mapping/user?pretty&ignore_conflicts=true' -d '{
        "user" : {
                "properties" : {
                        "enabled" : {
                                "type" : "boolean",
                                "index": "not_analyzed"
                        }
                }
        }
}'

Y ya podremos indexar nuevos documentos y seguir funcionando correctamente.

También en vez de utilizar el parámetro ignore_conflicts podemos modificar el mapping indicando el mismo mapping para el campo _timestamp:

$ curl -XPUT 'http://localhost:9200/myindex/_mapping/user?pretty' -d '{
        "user" : {
                "_timestamp" : { "enabled" : true, "store": "true" },
                "properties" : {
                        "confirm_new_user" : {
                                "type" : "boolean",
                                "index": "not_analyzed"
                        }
                }
        }
}

En este caso el conflicto no existe porque el mapping para _timestamp es el mismo que el que le estamos pasando, y por eso el conflicto no existe.

Es por ese motivo por el que decimos que seguramente se trate de un bug de ES, y que terminaremos creando un incidencia o consulta en https://github.com/elasticsearch/elasticsearch/.

You can follow any responses to this entry through the RSS 2.0 feed.You can skip to the end and leave a response. Pinging is currently not allowed.

Leave a Reply

--------