Conceptos Generales de SolrCloud

Las colecciones se componen de un o más shards. Los shards tiene una o más replicas. Cada replica es un core.

Un nodo es una máquina virtual de Java que ejecuta Solr, normalmente se le llama servidor. Cada core de Solr se puede considerar un nodo.

Cuando levantas un nuevo core en modo SolrCluod, se auto registra en Zookeper. Esto implica crear un nodo Efímero (Ephemeral), que se marcha si la instancia de Solr se cae, así como la información registrada sobre el core y como contactar con él. Los clientes inteligentes y los nodos del cluster pueden utilizar la información para determinar con quien necesitan hablar para completar la solicitud.

 

1.1          Clusters

Un cluster es un grupo de nodos de Sorl controlados por ZooKeeper como único elemento, siempre se puede hacer una solicitud al cluster y si es capaz de responderla, puedes estar seguro que se manejará de forma única y durable y no supondrá perdida de datos

1.2          Crear un Cluster

El cluster se crea tan pronto como registramos en Zookeper más de una instancia Solr

1.3          Leader

El concepto de leader es similar al de master en una réplica de Solr tradicional El leader es el responsable de asegurarse que los replicas están al día con la misma información almacenada en el líder. Sin embargo, en SolrCloud puede existir más de un master y uno o varios slaves, dependiendo del número de shards.

1.4          Importación de datos

El programa cliente, manda el documento a cualquier shard del cluster, para que lo indexe. Si el nodo que recibe la petición es una réplica, hace la query y manda el documento al leader para que lo procese. El leader añade el documento al log de transacciones, lo indexa y lo manda a todas las réplicas para que lo indexen. Si la actualización se realiza en el leader, pero falla en las réplicas, al cliente se le responde con status exitoso. Cuando las réplicas se recuperen, se sincronizarán con el leader. Si la réplica se ha quedado muy lejos de la sincronía, el sistema hace un recovery completo de la réplica. Si la actualización falla, porque el esquema se está recargando y algunos han acabado, pero otros no, el leader les dice a los nodos que la actualización ha fallado y comienza el procedimiento de recuperación

1.5          Balanceador de carga

SolrCloud automáticamente balancea la carga de las llamadas, y no necesita tener un balanceador de carga enfrente, como el caso de la arquitectura tradicional. Pero todavía es recomendable tener un balanceador enfrente o un cliente inteligente que haga la petición al cluster. Imagina que se hacen todas las peticiones al mismo nodo, que se encargara de pasarle la petición al leader y el leader determinara que shard tiene que procesar esa información y luego se la mandara al leader de ese shard.  Si la aplicación cliente sabe a qué shard debe hacer la petición la vuelta inicial se evita el cliente inteligente tiene un balanceador que distribuye la solitud basándose en principios round-robin

Si se mandan todas las peticiones al mismo shard y ese shard se cae, todas las peticiones se carean. Para contemplar es simple punto de fallo, SolrJ permite que se referencia al ZooKeeper en vez de al nodo Solr. Porque ZooKeeper siempre conoce el estado del cluster, nunca hará una petición a nudo que no esté disponible.

1.6          Recovery

Se crea un log de transacciones en cada nodo, en él se anota cada cambio. El log se utiliza para determinar que contenido del nodo se debe incluir en la réplica. Cuando se crea una nueva réplica, se referencia al Leader y al log de transacción para saber que contenidos debe incluir. Si cae lo reintenta. Como el log de transacción consisten en un registro de las actualizaciones, permite un índice más robusto porque permite rehacer lo cambios que no se guardado si el índice se interrumpe. Cada leader y replica tiene su propio archivo de log, también se le llama tlog, y se crea en directorio de los datos.

Solr añade todos los documentos al final del log transaccional y se reescribe en cada hard commit. Cuando la solicitud es de un update atómico, se escriben en el tlog el valor anterior y el actual, lo que permite hacer un roll back. Si el commit se después de mucho tiempo o los documentos se indexan a gran velocidad, los ficheros pueden crecer mucho y la recuperación puede ser costosa.

Si se cae un leader, puede que se mande a algunas réplicas y no a otras, así que cuando un nuevo líder potencial es identificado, se corre un proceso de sincronía contra las otras replicas, Si es correcta todo tiene que ser consistente, el líder se registra como activo y se continua con las labores normales. Si la réplica no es capaz de sincronizarse el sistema pregunta por un recovery completo. Si las actualizaciones fallan porque los cores están re balanceando los esquemas y algunos han terminado y otros no, el leader les dice a los nodos que la actualización ha fallado y comience el procedimiento de recovery.

 

1.7          SolrCloud and Replication

La replicación asegura la redundancia de los datos y permite mandar un update a cualquier nodo del shard. Si ese nodo es una réplica, desviará la solicitud al leader, quien lo re direccionará a todas las réplicas existente, utilizando el versionado para asegurarse que cada replica tiene la versión más actualizada. Está arquitectura permite asegurar los datos en caso de desastre.

1.8          ZOOKEPER

Para que se utiliza zookeper:

  • Elección del Leader.
  • Administrar los ficheros de configuración:

Todos los ficheros de configuración dentro del directorio core/conf incluido el solrconfig.xml y el schema.xml, se mantiene en zookeper. Los nodos llaman a ZooKeeper para toda la configuración. Cualquier cambio se tiene que subir a zookeeper.

  • Planificar las rutas de las solicitudes de indexación o búsqueda.

ZooKeeper mantiene información del cluster, de sus colecciones de los nodos levantados, de los estados de la réplica, los cuales se revisan para la coordinación del cluster.

 

Son necesario un número impares de servidores de ZooKeeper. Para poder determinar la mayoría. Cuantos más servidores de zookeper tengamos más estabilidad tendremos. La mitad + 1 de los servidores de zookeper corriendo se le denomina quorum, si no hay quorum podremos hacer búsquedas, pero la indexación fallará.

 

Si no tienes suficiente quorum (la mita + 1) funcionará la búsqueda, pero fallará la indexación.

1.8.1         Utilizar ZooKeeper para controlar los archivos de configuración.

Con SolrCloud tus archivos de configuración se almacenan en ZooKeeper. Estos archivos se suben en los siguientes casos:

Cuando se inicia SolrCloud utilizando el script bin/solr script.

Cuando se crea una colección utilizando el script bin/solr script.

Cuando explícitamente la subimos ZooKeeper, con zkcli.sh

1.9          SolrCloud with Legacy Configuration Files

Cambios necesarios en los ficheros de configuracion para el modo SolrCloud.

  1. En schema.xml, se tiene que tener un campo _version_ definido:

<field name=»_version_» type=»long» indexed=»true» stored=»true»

multiValued=»false»/>

  1. En solrconfig.xml, se tiene que tener definido UpdateLog. Se define en la sección updateHandler

.

Apache Solr Reference Guide 5.5 572

<updateHandler>

<updateLog>

<str name=»dir»>${solr.data.dir:}</str>

</updateLog>

</updateHandler>

 

 

Sincronización en mongodb

Cuando un secundario opera de forma normal, elige un miembro conque sincronizarse y empieza a coger las operaciones de la colección local.oplog.rs . Cuando coge un una op hace los siguientes pasos:

  1. Aplica el op
  2. Escribe el op es su propio oplog (también oplog.rs)
  3. Solicita el siguiente op

Si la base de datos se cae entre el 1 y el 2, cuando se recupera piensa que no la ha hecho y lo vuelve a repetir. Lo hace de tal manera que se puede reaplicar.

Por ejemplo en un document como este {counter:1} si se hace algo así {$inc:{counter:1}} el documento se convertiría en algo así {counter:2} y en el oplog se almacena algo así {$set:{counter:2}}. Que es lo que se replicara a los secundarios.

  • w

Para asegurarnos que un cando está presente en los dos nodos utilizamos aslgo así:

> db.foo.runCommand({getLastError:1, w:2})

La sintaxis varia en función del lenguaje, pero lo que hace es asegurarse que este escrito en ambos nodos.

  1. Escribe en el primario.
  2. La escritura se escribe en el oplog del primario, con un campo ts que indica que la escrituta se a producido en tiempo t.
  3. {getLastError:1,w:2} se llama desde el primario, este ha haecho la escritura, pero le queda otro nodo w:2.
  4. El secundarioconsulta el oplog del primario para obtener el op
  5. El secundarioaplica el op desde el momento t
  6. El secundario pide las ops con {ts:{$gt:t}}al oplog del primario.
  7. El primarioactualiza que el secondario ha aplicado hasta has  t, porque está pidiendo ops > t.
  8. getLastError se da cuenta que ha sido actualizado en ambos nodos, así que w:2 se cumple y da por buena la inserción.
  • Starting up

Cuando se reinicia un nodo, revisa la colección local.oplog.rs para ver la última entrada. Esto se llama lastOpTimeWritten y es la última op que el secundario ha aplicado.

Para obtener está información via shell se puede utilizar el comando:

> rs.debug.getLastOpWritten()

El campo “ts” es el último optime escrito.

Si el miembro se reinicia y no hay entradas en el oplog, se iniciara un poceso de sincronía incial.

  • Con quien se sincroniza

Los servidores se sincronizan con el servidor más cercano en función de una media en el tiempo de respuesta de los servidores que están al día (sanos).

Se puede ver desde que servidor se sincroniza desde corriendo el comando db.adminCommand({replSetGetStatus:1}) y buscando el campo “syncingTo” (solamente presente en los secundarios).

  • Cambio de la sincronizacion…

La opción replSetSyncFrom, permite cambiar desde que miembro te quieres resincronizar.

> db.adminCommand({replSetSyncFrom:»otherHost:27017″})

 

Fuente:

 

https://dzone.com/articles/get-familiar-mongodb-replica

 

spawn

Hoy me encontrado un pequeño problema, estaba ejecutando el sqldeveloper de manera remota y me preguntaba por unos passwords (ya sé que la manera más fácil es guardar los passwords en la conexión, pero por un pequeño error esto era imposible ver https://cajondesastreoracle.wordpress.com/2016/12/25/severe-java-sql-sqlrecoverableexception-io-error-connection-reset-linux-system/) así que hasta encontrar la solución definitiva estuve intentando el pasarle los passwords de manera dinámica con spawn.

Esta era la salida de la ejecución:

./sdcli unittest -run -suite -name "repos_prueba_suit" -repo "repos_owner" -db "repos_prueba"
 Oracle SQL Developer
 Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.

Password for repos_owner?

Password for repos_prueba?

Command Completed.

 

Esta es la forma de poder pasarle los passwords desde un scripts.

#!/usr/bin/expect -d

cd /opt/app/sqldeveloper/sqldeveloper/sqldeveloper/bin

log_user 1

set timeout -1

set PasswordOwner "repos_owner"

set PasswordUser "repos_prueba"

spawn ./sdcli unittest -run -suite -name "repos_prueba_suit" -repo "repos_owner" -db "repos_prueba"

expect "Password for repos_owner?"

send -- "$PasswordOwner\r"

expect -exact "Password for repos_prueba?"

send -- "$PasswordUser\r"

expect "Command Completed."

spawn ./sdcli reports generate -report "junit_suite_report" -db "repos_owner" -file "/home/hudson/REPORT_SUIT" -bind "suite_name=repos_prueba_suit"

expect "Password for repos_owner?"

send -- "$PasswordOwner\r"

expect "Command Completed.

SEVERE java.sql.SQLRecoverableException: IO Error: Connection reset – Linux system

Después de varios días volviéndonos locos y de sufrir con dos síntomas, por un lado, cuando creábamos una conexión en sqldeveloper aunque le pedíamos que guardara el password no lo hacía y con errores intermitentes de java, con timeout en las conexiones, Descubrimos que hay un problema con los poles de generación número aleatorios en las máquinas virtuales. Esto se puede solucionar instalando y levantando haveged que permite generar de manera más rápida los números random,con esto se acelera la generación de llaves SSL y hace la encriptación más efectiva.

Según Oracle cuando hay una diferencia muy grnade entre el número de estos dos ficheros, siendo mucho menor el segundo:

$ cat /proc/sys/kernel/random/poolsize

$ cat /proc/sys/kernel/random/entropy_avail

Puede haber problemas que se solucionan instalando y levantando havenged.

 

# yum install haveged

# systemctl start haveged

# systemctl enable haveged

Precalentar colecciones en memoría

Prueba para cargar en memoria los ficheros en mongodb con wiretiger. Con wiretiger desaparece la opción de touch y como ya comentamos en post anteriores se puede copiar en memoria los ficheros.

Precargando los datos en memoria en Mongodb

Queríamos hacer la prueba de que mejor suponía cargar los ficheros en memoria al reinicio de la máquina. Y se nos ocurrió hacer la siguiente prueba. Crear un fichero que obtuviera un find de todos los _id de la colección. Desordenar todos los datos. Y hacemos tres consultas:

 

  1. Con todos los datos en memoria
  2. Reiniciando y sin precalentar las colecciones
  3. Reiniciando y precalentado las colecciones.

 

Estos son los resultados finales

Crear el fichero sessions.js

db = db.getSiblingDB('Prueba');

db.SESION.find({}).forEach(function (SESIONES) {

printjson("db. SESION.find({\"_id\": ObjectId(\"" + SESIONES._id + "\")})" );

})

 

Lo ejecutamos en la base de datos:

mongo xx.xx.xx.xx:27017/admin -u admin -p xxxx  sessions.js > sesiones.js

Le quitamos la primera y segunda linea que tienen la conexión a mongdb y le quitamos los \

cat sesiones.js | sed 's/^.\|.$//g' |  sed 's/\\//g' | sed '1d' | sed '1d'  >sesionesnew.js

Lo desodernamos

cat sesionesnew.js | sort -R > sesionesdesordenadas.js

Hacemos un split porque el fichero es demasiado grande. El fichero contenía 5000000 de líneas y lo dejamos en 5 ficheros de 1000000

wc -l sesionesdesordenadas.js
5238482 sesionesdesordenadas.js

split -l 1000000 sesionesdesordenadas.js

 

 

 

Busquedas tipo like en mongodb

A partir de mongodb 2.6 aparecen los índices tipo text que permiten hacer búsquedas tipo like. Hay que tener en cuenta que esto índices no indexaran algunas palabras dependiendo del idioma. Os adjunto un pequeño ejemplo:

Creamos la colección foo con dos documentos:

db.foo.insert({desc: "This is a string with text"});

db.foo.insert({desc:"This is a another string with Text"});

Creamos un indice sobre desc tipo text, sin especificar idioma.

db.foo.ensureIndex({"desc":"text"});

db.foo.getIndexes()

[

        {

                "v" : 1,

                "key" : {

                        "_id" : 1

                },

                "name" : "_id_",

                "ns" : "admin.foo"

        },

        {

                "v" : 1,

                "key" : {

                        "_fts" : "text",

                        "_ftsx" : 1

                },

                "name" : "desc_text",

                "ns" : "admin.foo",

                "weights" : {

                        "desc" : 1

                },

                "default_language" : "english",

                "language_override" : "language",

                "textIndexVersion" : 2

        }

]

Como vemos por defecto lo crea en ingles.

Hacemos algunas búsquedas, como vemos por las stop words no encontramos resultados:

db.foo.find({ $text:{ $search:"this" } });

db.foo.find({ $text:{ $search:"text" } });

{ "_id" : ObjectId("5810773579d890f4da883876"), "desc" : "This is a another string with Text" }

{ "_id" : ObjectId("5810772c79d890f4da883875"), "desc" : "This is a string with text"

Borramos el índice y lo recreamos para español

db.foo.dropIndex({ "_fts" : "text",

...                         "_ftsx" : 1})

{ "nIndexesWas" : 2, "ok" : 1 }

db.foo.ensureIndex({"desc":"text"}, { default_language: "spanish" })

{

        "createdCollectionAutomatically" : false,

        "numIndexesBefore" : 1,

        "numIndexesAfter" : 2,

        "ok" : 1

}

db.foo.find({ $text:{ $search:"is" } });

{ "_id" : ObjectId("5810773579d890f4da883876"), "desc" : "This is a another string with Text" }

{ "_id" : ObjectId("5810772c79d890f4da883875"), "desc" : "This is a string with text" }

db.foo.find({ $text:{ $search:"This" } });

{ "_id" : ObjectId("5810773579d890f4da883876"), "desc" : "This is a another string with Text" }

{ "_id" : ObjectId("5810772c79d890f4da883875"), "desc" : "This is a string with text" }

Al hacer las mismas búsquedas obtenemos resultados diferentes, porque las stop words dependen del idioma.

crs-1718: the css daemon is unable to continue due to a failure in required component gpnp.

Hoy instalando el grid de Oracle 12, nos hemos encontrado con un pequeño error:

crs-1718: the css daemon is unable to continue due to a failure in required component gpnp.

Mirando en con más detalle el los del css:

/opt/app/12.1.0/grid/log/hostname/cssd

Hemos visto que había un core del css

/opt/app/12.1.0/grid/log/cluster2-01/cssd

2016-10-24 11:30:29.054: [ default][1]Cannot get GPnP profile. Error CLSGPNP_NO_DAEMON (GPNPD daemon is not running).

Normalmente esto se debe a un error de comunicación del interconect, o no está bien levantada la interfaz o hay un error en la interfaz indicada.

CORS en SOLR 5.5

  1. Añadir un filtro en el web.xml

En nuestro caso /solr/server/solr-webapp/webapp/WEB-INF

<filter> \

<filter-name>cross-origin</filter-name> \

<filter-class>org.eclipse.jetty.servlets.CrossOriginFilter</filter-class> \

<init-param> \

<param-name>allowedOrigins</param-name> \

<param-value>*</param-value> \

</init-param>  \

<init-param>  \

<param-name>allowedMethods</param-name>  \

<param-value>GET,POST,OPTIONS,DELETE,PUT,HEAD</param-value>  \

</init-param>  \

<init-param>  \

<param-name>allowedHeaders</param-name>  \

<param-value>origin, content-type, accept</param-value>   \

</init-param>  \

</filter> \

\

<filter-mapping> \

<filter-name>cross-origin</filter-name> \

<url-pattern>/*</url-pattern> \

</filter-mapping>’ $RUTA/web.xml >

 

  1. Que en el classpath de solr exista el jar donde está CrossOriginFilter

En nuestro caso se solucionó con una link

 

Para verificar que la llamada es correcta:

curl -H «Origin: http://example.com» –verbose \

http://xxxxxxx:2020/solr/search?

 

En la cabecera se tiene que ver

< Access-Control-Allow-Credentials: true

< Access-Control-Allow-Origin: http://example.com