Django + Elasticsearch

Problemas y soluciones

Miguel González Nieto (@scenting)

Miguel Sánchez Rodríguez (@miguelsr1987)

  • Miguel González Nieto
  • @scenting
  • Back-end engineer en Acceso
  • Miguel Sánchez Rodríguez
  • @miguelsr1987
  • Full-stack web engineer en Acceso

AccesoHub - Stack

¿Qué necesitábamos?

  • Búsqueda full-text
  • Gran volumen de documentos (ahora, +50M)
  • Búsqueda More Like This
  • Asignación inversa (índices sobre búsquedas)
  • ¿Estadísticas? ¿Datos agregados?

¿Por qué ElasticSearch?

Posibles Alternativas

  • ¿Core?
    • Transversal a la empresa, sin conocimiento de cada aplicación
  • ¿PostgreSQL?
    • Preferimos BDs sin guardar el texto
    • No tenemos More Like This
    • No escala para nuestro volumen de busquedas
  • ¿SolR?
    • Esquema muy plano, sin jerarquias
    • Búsquedas poco expresivas
    • En general: ElasticSearch mejora a SolR

Winning Points de Elasticsearch

  • Todo es configurable vía API. Respuestas JSON.
  • Esquemas dinámicos.
  • Altamente configurable desde shell.
  • Posibilidad de esquema con jerarquías.
  • More Like This y Percolator out-of-the-box.
  • Fácilmente extensible (plugins, analizadores).
  • Librería completa para Python (¡y Haystack!).
  • Herramienta de moda, comunidad potente!

Historia de un buscador

Indexado (I)

  • ¿Cómo indexamos nuestros documentos?
    • Definimos un JSON con el mapeo de cada campo a indexar (¡respetaremos las IDs!)
    • ES es esencialmente plano, pero soporta jerarquía:
      • Inner objects (Sin queries)
      • Nested documents (Overkill en update)
      • Parent/child (Menos rápido)
      • Denormalization (Demasiados documentos)
    • En nuestro caso, parent/child, por rendimiento en las actualizaciones.
    • Se recomienda nested documents para un set de documentos actualizado poco frecuentemente.

Puesta en producción

  • Ya tenemos claro el esquema pero ¿cómo desplegarlo?
  • A priori, con un nodo puede ser suficiente.
  • Con dos nodos, nos beneficiamos de mayor resiliencia y actualizaciones en caliente.
  • Para mayor seguridad y mitigar split-brains se recomiendan al menos 3
  • Con la configuración por defecto de sharding nos es suficiente

¿Cómo nos conectamos?

  • Al principio, directamente desde el navegador, a través de la API con el cliente JS de Elasticsearch.
    • Poco seguro
    • Problemático detrás de un proxy
    • Over-engineering en front-end
  • Mejor hacerlo todo desde nuestro back-end Django
    • JS sólo para presentación
    • Autenticación compartida con la App.
    • Librería para Python de ES muy completa.
    • Atomicidad transaccional con nuestros modelos.

Indexado (II)

  • Ya tenemos nuestro ES desplegando y funcionando
  • ¿Primer problema?

Muuuuy lento. Según la documentación, ¡hasta un 10x más!

 

Indexado (III)

  • Un índice por cada mes y tipo de documento.
  • Búsquedas muchísimo más rápidas con filtrado.
  • Puedes buscar en varios indices y despues unir
  • Configuracion dinamica de shards
  • Distribución más o menos homogénea por meses.
  • Los indices son transparentes
  • Se pueden crear automáticamente con templates

Particionado (I)

  • ¡Horror! Nuevos problemas con el particionado
  • Las noticias pueden cambiar de fecha (wtf?) y por tanto, de indice
  • Aparecen documentos duplicados en diferentes indices
  • ¿IDs repetidos? ¿Es posible?
  • Sí, el ID es único, ¡pero por índice!

Debemos borrar los datos viejos cada vez que cambiemos un documento de índice.

Problemas de Integridad (I)

  • No tenemos monitorización de datos, confiamos en que todo está sincronizado.
  • Nuestros users nos desmuestran que no.
  • ¿Cómo podemos garantizar la integridad de datos?
    • Nuestras escrituras a BD son transaccionales y fuertemente ligadas con ES mediante señales.
    • Aun así, en algún momento se crea un desacoplamiento.
    • ES no es ni pretende ser una base de datos.
  • Necesitamos un Sincronizador!

Problemas de Integridad (II)

  • Creamos un sincronizador que se ejecuta de manera periódica.
    • Comando de django-admin, periodic Task de Celery.
    • Comprueba que el conteo de documentos por tipo coincide.
    • Dado que los IDs en Elastic y pgSQL coinciden, hacemos scaneos de los indices y comprobamos por su equivalente en fechas en nuestra BD.
      • ¡Sorprendentemente rápido!

Problemas de Integridad (III)

  • No es suficiente.
  • Puede darse el caso de que los documentos mantengan la ID pero cambian en algún atributo.
  • Añadimos una opción a nuestro Sincronizador para que chequee ciertos campos, en caso de necesidad.
    • ¡Sorprendentemente rápido! (de nuevo)

Problemas de Integridad (y IV)

  • Alta concurrencia en las escrituras
  • Un mismo documento puede intentar actualizarse con datos desfasados.
  • Para solucionarlo, empleamos versiones externas basadas en el timestamp de la última actualización.
    • Se genera un timestamp al principio del proceso, y si es menor que la versión en ES, se descarta el cambio.

ES & Beyond

  • Todo esto nos da cierta tranquilidad, pero todavía carecemos de monitorización formal.
  • ¿Posibilidades?
    • Marvel: Monitorización del cluster.
    • Watcher: Monitorización y alerta por contenido.
    • Kibana: Visualizacion de estadísticas.
    • Logstash: ETL de logs

Rendimiento (I)

/_all/newsitem/_search?q=body:motril

{
    took: 1550,
    timed_out: false,
    _shards: {
        total: 99,
        successful: 99,
        failed: 0
    },
    hits: {
        total: 19869,
        max_score: 4.725038,
        hits: [
            {
            _index: "accesonews_2014_07",
            _type: "newsitem",
            _id: "2130936",
            _score: 4.725038,
            _source: {
                body: "@djnieto76 @raphapeugeot motril mejorrr",
                source_name: "Twitter / Bolsonblanco",
                author: [ ],
                klout_score: 50,
                title: "@djnieto76 @raphapeugeot motril mejorrr",
                creation_time: "2014-07-26T23:24:19.584741+00:00",
                remote_code: "/i/g/t493174869069406210",
                client: [
                21
                ],
                project: [
                    43
                ],
    

Rendimiento (II)

/accesonews_2015_10/newsitem/_search?q=body:volkswagen

{
    took: 5,
    timed_out: false,
    _shards: {
        total: 1,
        successful: 1,
        failed: 0
    },
    hits: {
        total: 22659,
        max_score: 2.687686,
        hits: [
            {
                _index: "accesonews_2015_10",
                _type: "newsitem",
                _id: "17973385",
                _score: 1.6798037,
                _source: {
                    body: "#Bankia Comienzan los problemas financieros para Volkswagen http://t.co/YE9xcEZkM6",
                    source_rank: null,
                    source_name: "Twitter / Bankia_Acciones",
                    publication_date: "2015-10-01T16:22:53+00:00",
                    author: [ ],
                    url: "http://twitter.com/Bankia_Acciones/statuses/649620508716077056",
                    klout_score: null,
                    title: "#Bankia Comienzan los problemas financieros para Volkswagen http://t.co/YE9xcEZkM6",
                    creation_time: "2015-10-01T16:23:18.849Z",
                    remote_code: "/i/g/t649620508716077056",
                    source_url: "http://www.twitter.com/Bankia_Acciones",
                    

Muchas gracias.

¿Preguntas?

deck

By migueles