<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>Juan P. Schwindt</title>
 <link href="http://www.schwindt.org/atom.xml" rel="self"/>
 <link href="http://www.schwindt.org/"/>
 <updated>2023-06-01T18:13:16+00:00</updated>
 <id>http://www.schwindt.org</id>
 <author>
   <name>Juan Schwindt</name>
 </author>

 
 <entry>
   <title>Arduino al rescate de una balanza</title>
   <link href="http://www.schwindt.org/posts/arduino-al-rescate-de-una-balanza"/>
   <updated>2016-08-27T00:00:00+00:00</updated>
   <id>http://www.schwindt.org/posts/arduino-al-rescate-de-una-balanza</id>
   <content type="html">&lt;h2 id=&quot;motivación&quot;&gt;Motivación&lt;/h2&gt;

&lt;p&gt;Al igual que la &lt;a href=&quot;/posts/pava-digital-para-mate&quot;&gt;Pava eléctrica&lt;/a&gt;, la balanza de cocina
dejó de funcionar al poco tiempo de uso, simplemente encendía y quedaba siempre en cero. La desarmé
para ver si se podía hacer algo, pero a simple vista no había nada que pareciera estar mal, y como se trataba de
un típico &lt;a href=&quot;https://en.wikipedia.org/wiki/Electronic_packaging#/media/File:Famicom_clone_PCB.jpg&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;chip-on-board&lt;/a&gt; las chances de repararla eran nulas.&lt;/p&gt;

&lt;p&gt;De los elementos que formaban la balanza, lo único que podía ser útil era el sensor de peso o “strain gauge”,
así que me propuse diseñar una balanza nueva.&lt;/p&gt;

&lt;h2 id=&quot;el-sensor-de-peso&quot;&gt;El sensor de peso&lt;/h2&gt;

&lt;p&gt;Resulta que el sensor de peso de 5 Kg que traía la balanza es bastante común y existe un módulo basado en el chip
&lt;a href=&quot;https://cdn.sparkfun.com/datasheets/Sensors/ForceFlex/hx711_english.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;HX711&lt;/a&gt;
que hace de amplificador de la tensión diferencial que se genera en el &lt;a href=&quot;https://es.wikipedia.org/wiki/Puente_de_Wheatstone&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;puente de Wheatstone&lt;/a&gt;
y además convierten de analógico a digital. La forma de conectarlo es la siguiente:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/sensor_de_peso.png&quot; alt=&quot;Cómo conectar el sensor de peso&quot; width=&quot;480px&quot; /&gt;&lt;/p&gt;

&lt;p&gt;También, gracias a la magia del código abierto, existe un &lt;a href=&quot;https://github.com/bogde/HX711&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;módulo de software&lt;/a&gt; para Arduino para comunicarse
con el HX711 y leer el dato del peso.&lt;/p&gt;

&lt;h2 id=&quot;arduino-pro-mini-y-el-display&quot;&gt;Arduino Pro Mini y el display&lt;/h2&gt;

&lt;p&gt;Para este proyecto necesitaba muchas puertas de I/O: 11 salidas para manejar los 4 dígitos del display de 7
segmentos que muestra el peso, 2 para clock y datos del HX711 y una entrada leer la tecla de puesta a cero (tara).
Al mismo tiempo tenía que ser lo suficientemente pequeño para caber dentro del gabinete de la balanza. Para esto
el &lt;a href=&quot;https://www.arduino.cc/en/Main/ArduinoBoardProMini&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Arduino Pro Mini&lt;/a&gt;
resultó no sólo ser barato (no tiene la interfaz serie ni la fuente de alimentación del Arduino Uno), sino que también
tiene la separación de pines que calza exactamente con los del cuádruple display de 7 segmentos, con lo cual el montaje
quedó super compacto:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/montaje_display0.jpg&quot; alt=&quot;Montaje del Arduino Pro Mini con el display&quot; width=&quot;480px&quot; /&gt;
&lt;img src=&quot;../images/montaje_display.jpg&quot; alt=&quot;Montaje del Arduino Pro Mini con el display&quot; width=&quot;480px&quot; /&gt;&lt;/p&gt;

&lt;p&gt;En cuanto al manejo del display usé una idea sencilla que permite ahorrar componentes: tradicionalmente se suele
poner un transistor, con su resistencia en la base) por cada dígito de manera de soportar la corriente de los 7 segmentos del dígito, y 7 resistencias más, una por cada segmento, para limitar la corriente de cada led. Pero si resignamos un poco de luminosidad y encendemos de a un segmento por vez y un display por vez, sólo necesitamos 4 resistencias, una por cada display ánodo común, ya que cada salida del controlador puede manejar la corriente necesaria para un led, lo cual simplifica mucho el conexionado.&lt;/p&gt;

&lt;p&gt;Así quedó el circuito:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://easyeda.com/jschwindt/Balanza_Digital-c0TN5nFzR&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;img src=&quot;../images/balanza-schematic.png&quot; alt=&quot;Circuito de la balanza digital&quot; width=&quot;640px&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;La alimentación del circuito se realiza mediante una fuente externa de 5V, que puede provenir de un simple cargador de celular, para lo cual le puse un conector micro-usb montada al gabinete. Así quedó todo dentro de la Arduino-balanza:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/montaje_completo.jpg&quot; alt=&quot;Montaje completo de la balanza&quot; width=&quot;480px&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;software&quot;&gt;Software&lt;/h2&gt;

&lt;p&gt;El soft es una clásica aplicación Arduino son su setup() y loop(), aunque decidí usar una interrupción
de timer para el multiplexado de los segmentos y dígitos. No conocía mucho del manejo de los registros
de los timers del microcontrolador, pero encontré un &lt;a href=&quot;http://www.instructables.com/id/Arduino-Timer-Interrupts/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;buen artículo en www.instructables.com&lt;/a&gt; con todo lo que necesitaba. La interrición de tiempo se genera cada 512 microsegundos y en ella lo que se hace es apagar el segmento que estaba encendido, calcular cuál es el próximo segmento y encenderlo, para lo cual hay que poner HIGH
en el ánodo del display correspondiente y LOW en el segmento. El ciclo completo dura 28 * 0.512 = 14 milisegundos, lo
que da una buena frecuencia de refresco.&lt;/p&gt;

&lt;p&gt;El código lo publiqué en Github: &lt;a href=&quot;https://github.com/jschwindt/BalanzaDigital/blob/master/Balanza.ino&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;código fuente de la balanza&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;La balanza terminada y funcionando:&lt;/p&gt;

&lt;iframe width=&quot;420&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/HvdiXwBKR9Q&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
</content>
 </entry>
 
 <entry>
   <title>Pava digital para mate</title>
   <link href="http://www.schwindt.org/posts/pava-digital-para-mate"/>
   <updated>2016-07-02T00:00:00+00:00</updated>
   <id>http://www.schwindt.org/posts/pava-digital-para-mate</id>
   <content type="html">&lt;h2 id=&quot;motivación&quot;&gt;Motivación&lt;/h2&gt;

&lt;p&gt;Compré una pava eléctrica con control de temperatura para el agua del mate, pero al poco tiempo el
termostato de 85ºC se rompió y no hubo forma de conseguir el repuesto. La molestia de estar siempre atento
a que el agua no hierva hizo que el problema se convirtiera rápidamente en una oportunidad para jugar con
Arduino en un proyecto concreto de “digitalizar” la pava.&lt;/p&gt;

&lt;h2 id=&quot;el-sensor-de-temperatura&quot;&gt;El sensor de temperatura&lt;/h2&gt;

&lt;p&gt;La primera idea fue usar el &lt;a href=&quot;http://www.ti.com/lit/ds/symlink/lm235.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;LM335&lt;/a&gt; ya que tenía uno a mano. Este
sensor entrega 10mV por ºK, es decir que para el rango de 10 a 90ºC del agua, la tensión que entrega es de
2,83 a 3,63V, es decir menos de 1V de variación, y como la idea es utilizar el ADC del Arduino, que mide de 0 a 5V, se
desperdiciaría el 80% del rango de conversión. Otro problema es que al tener un encapsulado plástico, el
contacto térmico y la inercia térmica complicaban el control.&lt;/p&gt;

&lt;p&gt;Finalmente me sugirieron utilizar un NTC 100K a 25ºC con encapsulado en vidrio, que se usa en las impresoras 3D
y es muy barato. Con esto es muy fácil regular el rango de tensión cambiando la resistencia del divisor, la que
quedó en 22K. La respuesta del divisor de tensión no es lineal (&lt;a href=&quot;/images/tabla-ntc-100k.png&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ver tabla de resistencia del NTC&lt;/a&gt;), pero para el uso como termostato eso no es importante.&lt;/p&gt;

&lt;p&gt;Así quedó el termistor dentro del espacio del viejo termostato:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/termistor.jpg&quot; alt=&quot;Así quedó el termistor en el lugar del termostato&quot; width=&quot;300px&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;el-circuito-y-el-armado-con-el-attiny85&quot;&gt;El circuito y el armado con el ATtiny85&lt;/h2&gt;

&lt;p&gt;El circuito definitivo es muy sencillo ya que el microcontrolador no necesita ningún componente externo para funcionar:
&lt;a href=&quot;http://www.schematics.com/project/digital-pava-35838/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;img src=&quot;../images/pava-mate-digital.png&quot; alt=&quot;Circuito de la pava para mate&quot; width=&quot;640px&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Esta es la plaquetita terminada:
&lt;img src=&quot;../images/placa.jpg&quot; alt=&quot;Plaqueta terminada&quot; width=&quot;480px&quot; /&gt;&lt;/p&gt;

&lt;p&gt;La fuente de alimentación tenía que ser pequeña para que entrara en la base de la pava eléctrica, así que lo más fácil
fue usar un cargador de celular sin su encapsulado plástico.&lt;/p&gt;

&lt;p&gt;Y todo montado en la base de la pava:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/montaje.jpg&quot; alt=&quot;Montaje en la base de la pava eléctrica&quot; width=&quot;480px&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;software&quot;&gt;Software&lt;/h2&gt;

&lt;p&gt;El soft es muy sencillo, pero como hay que tener en cuenta la inercia térmica de la pava y del sensor, fue necesario
hacer una pequeña máquina de estado. El funcionamiento es el siguiente: primero calienta el agua hasta alcanzar aproximadamente
los 60ºC. A partir de ahí comienzan ciclos de 10 segundos de espera (para que se estabilice la medición de temperatura), y
7 segundos de calentamiento. Cuando se llega a los 85ºC buscados, se apaga la resistencia y se pasa a un estado de espera hasta que la temperatura pasa por debajo de los 80ºC, donde vuelve a empezar el ciclo.&lt;/p&gt;

&lt;p&gt;El LED se utiliza para saber en qué parte del ciclo se encuentra: si está encendido en forma permanente significa que el
agua se encuentra lista para preparar el mate. Si estamos en el proceso de calentamiento, el LED titila con una velocidad
variable y se acelera a medida que nos acercamos a los 85ºC. El código lo publiqué en Github: &lt;a href=&quot;https://github.com/jschwindt/PavaMateDigital/blob/master/src/PavaMate.cpp&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;código fuente de la pava&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Durante el desarrollo usé un Arduino Uno ya que de esa forma podía usar la interfaz serie para enviar mensajes de debug y de esa forma acelerar el proceso. Luego, cuando llegó el momento de programar definitivamente el ATtiny85, seguí las instrucciones
de estos tutoriales &lt;a href=&quot;https://www.youtube.com/watch?v=BexXvxmOGN8&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Video: How to make a DIY ATtiny Programmer&lt;/a&gt; y &lt;a href=&quot;http://highlowtech.org/?p=1695&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Programming an ATtiny w/ Arduino 1.6&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;La pava “electrónica” terminada:&lt;/p&gt;

&lt;iframe width=&quot;420&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/4MAwimaxqno&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
</content>
 </entry>
 
 <entry>
   <title>Balanceador de carga para HTTPS (SSL/TLS) con HAProxy</title>
   <link href="http://www.schwindt.org/posts/balanceador-de-carga-para-https-con-haproxy"/>
   <updated>2013-10-08T00:00:00+00:00</updated>
   <id>http://www.schwindt.org/posts/balanceador-de-carga-para-https-con-haproxy</id>
   <content type="html">&lt;h2 id=&quot;intro&quot;&gt;Intro&lt;/h2&gt;

&lt;p&gt;Cuando se trata de balancear la carga HTTP me gusta utilizar HAProxy ya que es muy liviano y flexible para configurar. Pero hace unos días tuvimos la necesidad de balancear HTTPS para una aplicación de Facebook desarrollada en RoR y es por eso que me puse a investigar cómo hacerlo. En este artículo les cuento primero cómo obtener un certificado válido de SSL/TLS en forma gratuita y luego cómo configurar el HAProxy para que use dicho certificado.&lt;/p&gt;

&lt;h2 id=&quot;certificado-ssltls-gratuito&quot;&gt;Certificado SSL/TLS gratuito&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Actualización 2016:&lt;/strong&gt; Hoy en día conviene utilizar &lt;a href=&quot;https://letsencrypt.org/&quot;&gt;Let’s Encrypt&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;El sitio &lt;a href=&quot;http://www.startssl.com/&quot;&gt;StartSSL&lt;/a&gt; emite certificados SSL/TLS legítimos y gratuitos (clase 1), pero el proceso para poder utilizarlos correctamente en el servidor web o en el balanceador de carga tiene sus vueltas y me llevó un tiempo hasta que logré hacerlo funcionar correctamente.&lt;/p&gt;

&lt;p&gt;Primero hay que registrarse en el sitio e instalar un certificado en el navegador. Ese certificado es el que nos permite luego ingresar al panel de control y gestionar todo lo necesario para obtener el certificado final. Una vez adentro, los pasos son los siguientes:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Ir a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Validations Wizard&lt;/code&gt; y elegir &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Domain Name Validation&lt;/code&gt;. Este procedimiento manda un mail a alguna de las cuentas del administrador del dominio con un código de validación, que luego debe cargarse en el formulario correspondiente.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Una vez validado el dominio se debe generar una clave privada y luego el certificado. Para esto utilizar el &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Certificates Wizard&lt;/code&gt; con la opción &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Web Server SSL/TLS Certificate&lt;/code&gt;. Utilizar los valores default de &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Keysize&lt;/code&gt; y &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Secure Hash Algorithm&lt;/code&gt; para asegurarse la compatibilidad con navegadores viejos. Luego guardar la clave privada (como &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssl.key&lt;/code&gt;, por ejemplo) y el certificado (como &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssl.crt&lt;/code&gt;, por ejemplo).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;Luego conviene eliminar el password de la clave privada para que no haga falta ingresarla cada vez que levante o reinicie el servidor web o el HAProxy. Esto se puede hacer de 2 formas: utilizando la opción &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Decrypt Private Key&lt;/code&gt; del &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Tool Box&lt;/code&gt; o mediante openssl en línea de comando:
    &lt;blockquote&gt;
      &lt;p&gt;openssl rsa -in ssl.key -out ssl.nokey&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;Este último paso es importante y corresponde al armado del archivo final que se va a utilizar en el servidor. Para esto es necesario descargar previamente 2 archivos desde el &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Tool Box&lt;/code&gt; opción &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StartCom CA Certificates&lt;/code&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StartCom Root CA (PEM encoded)&lt;/code&gt; y &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Class 1 Intermediate Server CA&lt;/code&gt; y concatenarlos todo en un único archivo:
    &lt;blockquote&gt;
      &lt;p&gt;cat ssl.crt ssl.nokey sub.class1.server.ca.pem ca.pem » ssl.full.crt&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;El archivo &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssl.full.crt&lt;/code&gt; es el que utilizaremos en HAProxy según se explica en el apartado siguiente.&lt;/p&gt;

&lt;h2 id=&quot;haproxy-con-ssltls&quot;&gt;HAProxy con SSL/TLS&lt;/h2&gt;

&lt;p&gt;Una gran ventaja de tener a HAProxy como punto de terminación de las conexiones SSL/TLS es que simplifica la configuración de los backend ya que la conexión hacia adentro se realiza por HTTP como siempre, y el único punto de configuración HTTPS resulta ser el proxy. Por otro lado eso también nos permite apuntar el proxy hacia diferentes aplicaciones de múltiples backends y todas compartiendo el mismo certificado.&lt;/p&gt;

&lt;p&gt;Para utilizar HAProxy con HTTPS en necesario instalar la versión de desarrollo ya que el soporte para SSL/TLS recién comenzó a partir de la 1.5-dev12, pero ya tuvo varias revisiones y la versión actual (1.5-dev19) es muy estable y no tuve problemas en producción.&lt;/p&gt;

&lt;p&gt;La instalación es muy sencilla y pueden ver los pasos necesarios para Debian/Ubuntu en &lt;a href=&quot;http://dylancopeland.com/posts/setting-up-and-configuring-haproxy&quot;&gt;http://dylancopeland.com/posts/setting-up-and-configuring-haproxy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;En cuanto a la configuración, la directiva para habilitar el proxy de HTTPS se realiza mediante &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bind&lt;/code&gt; en donde se especifican los datos necesarios, por ejemplo:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;frontend web
  bind 0.0.0.0:80
  bind 0.0.0.0:443 ssl crt /etc/haproxy/certs/ssl.full.crt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;donde &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssl.full.crt&lt;/code&gt; es el certificado que armamos en el apartado anterior. El resto de las directivas son las usuales para este tipo de utilización. Aquí se puede ver un ejemplo completo de la configuración: &lt;a href=&quot;https://gist.github.com/jschwindt/6902596&quot;&gt;https://gist.github.com/jschwindt/6902596&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finalmente, una vez que el HAProxy está corriendo es importante probar que toda la cadena de certificados y la compatibilidad con los distintos navegadores esté funcionando correctamente. Para eso hay varias herramientas online, por ejemplo: &lt;a href=&quot;http://www.sslshopper.com/ssl-checker.html&quot;&gt;http://www.sslshopper.com/ssl-checker.html&lt;/a&gt;. Un problema que tuve durante las pruebas es que con Firefox aparecía un error de certificados y eso se debía a que dentro del certificado final faltaba el de la autoridad certificante intermedia, cosa que no se manifestaba con el resto de los navegadores.&lt;/p&gt;

&lt;h2 id=&quot;conclusión&quot;&gt;Conclusión&lt;/h2&gt;

&lt;p&gt;Dio un poco de trabajo y la información estaba un poco dispersa, pero es perfectamente posible utilizar HTTPS sin invertir dinero en comprar un certificado y utilizando las herramientas de código abierto existentes.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Redis Cluster con Ruby</title>
   <link href="http://www.schwindt.org/posts/redis-cluster-con-ruby"/>
   <updated>2013-08-22T00:00:00+00:00</updated>
   <id>http://www.schwindt.org/posts/redis-cluster-con-ruby</id>
   <content type="html">&lt;h2 id=&quot;intro&quot;&gt;Intro&lt;/h2&gt;

&lt;p&gt;En &lt;a href=&quot;http://comenta.tv&quot;&gt;Comenta.Tv&lt;/a&gt; procesamos gran cantidad de tweets, que obtenemos
a partir de la &lt;a href=&quot;http://redis.io/&quot;&gt;API de streaming&lt;/a&gt; de Twitter, y generamos estadísticas
y reportes en tiempo real.&lt;/p&gt;

&lt;p&gt;Para lograr escalar a cientos de mensajes por segundo y para poder generar los reportes
con tiempos de respuesta rápidos, nos valemos principalmente de &lt;a href=&quot;http://redis.io/&quot;&gt;Redis&lt;/a&gt;,
que demostró ser una herramienta ideal para logar nuestra meta.&lt;/p&gt;

&lt;p&gt;El “problema” de Redis (y también su virtud) es que toda la base de datos tiene que caber
en la memoria RAM, y rápidamente esto se convierte en un recurso escaso. La solución ideal
sería disponer ya de &lt;a href=&quot;http://redis.io/topics/cluster-spec&quot;&gt;Redis Cluster&lt;/a&gt;, pero como todavía
no está disponible hubo que encarar una solución más casera para particionar el contenido
en distintos servidores.&lt;/p&gt;

&lt;h2 id=&quot;redisdistributed&quot;&gt;Redis::Distributed&lt;/h2&gt;

&lt;p&gt;Según &lt;a href=&quot;http://redis.io/topics/partitioning&quot;&gt;la documentación&lt;/a&gt; de Redis, hay distintas
opciones para particionar los datos en múltiples servidores y una de las opciones es
utilizar directamente el cliente de &lt;a href=&quot;https://github.com/redis/redis-rb&quot;&gt;Redis en Ruby&lt;/a&gt;
utilizando la opción “distributed”.&lt;/p&gt;

&lt;p&gt;La documentación para utilizar &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Redis::Distributed&lt;/code&gt; es casi nula y encontré pocos ejemplos
y demasiado básicos como para tomarlos en serio. Luego de cometer algunos errores que me
llevaron a andar moviendo millones de claves de un servidor a otro, decidí escribir este
post para tratar de evitar que otros pasen por lo mismo.&lt;/p&gt;

&lt;h3 id=&quot;cómo-funciona&quot;&gt;¿Cómo funciona?&lt;/h3&gt;

&lt;p&gt;Normalmente para utilizar Redis desde Ruby con un sólo servidor hacemos algo así:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'redis://127.0.0.1:6379/0'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;clave1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Hello!&quot;&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# &quot;OK&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;clave1&quot;&lt;/span&gt;                  &lt;span class=&quot;c1&quot;&gt;# &quot;Hello!&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;del&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;clave1&quot;&lt;/span&gt;                  &lt;span class=&quot;c1&quot;&gt;# 1&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Cuando utilizamos &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Redis::Distributed&lt;/code&gt; el uso es similar, pero el cliente se inicializa
con un array de servidores Redis en lugar de un único servidor:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;REDIS_ARRAY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'redis://127.0.0.1:6379/0'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'redis://127.0.0.1:6380/0'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Redis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Distributed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;REDIS_ARRAY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;clave1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Hello!&quot;&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# &quot;OK&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;clave1&quot;&lt;/span&gt;                  &lt;span class=&quot;c1&quot;&gt;# &quot;Hello!&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Como vemos ahora tenemos múltiples server, pero el funcionamiento del los comandos es
similar. Ahora el servidor de destino se elige en base al valor de la clave:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;node_for&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;clave1&quot;&lt;/span&gt;             &lt;span class=&quot;c1&quot;&gt;# &amp;lt;Redis client v3.0.4 for redis://127.0.0.1:6379/0&amp;gt;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;node_for&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;clave2&quot;&lt;/span&gt;             &lt;span class=&quot;c1&quot;&gt;# &amp;lt;Redis client v3.0.4 for redis://127.0.0.1:6380/0&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;limitaciones&quot;&gt;Limitaciones&lt;/h3&gt;

&lt;h4 id=&quot;múltiples-claves&quot;&gt;Múltiples claves&lt;/h4&gt;

&lt;p&gt;El hecho de que cada clave se encuentre en un servidor distinto impone algunas limitaciones,
por ejemplo: todas las operaciones que impliquen múltiples claves no son posibles:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Redis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Distributed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;REDIS_ARRAY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;clave1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;val1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;clave2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;val2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Redis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Distributed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CannotDistribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Redis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Distributed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CannotDistribute&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Esto no significó un problema para nosotros porque no utilizamos ninguna de dichas
operaciones ya que todo lo realizamos en base a claves independientes.&lt;/p&gt;

&lt;h4 id=&quot;agregar-servidores&quot;&gt;Agregar servidores&lt;/h4&gt;

&lt;p&gt;Una vez que uno tiene el cluster de servidores funcionando y con contenido, no es posible
agregar un nuevo servidor al array porque eso alteraría automáticamente la distribución
de claves y las ya existentes quedaría inaccesibles.&lt;/p&gt;

&lt;p&gt;Entonces, ¿cómo hacer cuando nos volvemos a quedar sin memoria? La primera opción sería
agregar memoria a los servidores actuales, pero esto no siempre es posible.&lt;/p&gt;

&lt;p&gt;La solución sugerida es que en el armado del cluster tengamos la previsión de tener
muchos servidores Redis corriendo, aunque inicialmente todos los procesos pueden estar
en el mismo servidor físico y a medida que necesitamos, vamos migrando el contenido
a nuevos servidores físicos. La cantidad de servidores no debemos cambiarla nunca y
no hay que tener miedo de tener muchos servidores Redis corriendo en el mismo server
físico ya que la utilización de memoria y CPU es muy baja.&lt;/p&gt;

&lt;h3 id=&quot;armado-del-cluster&quot;&gt;Armado del cluster&lt;/h3&gt;

&lt;h4 id=&quot;identificar-los-servidores-por-id&quot;&gt;Identificar los servidores por ID&lt;/h4&gt;

&lt;p&gt;En los ejemplos que había visto con &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Redis::Distributed&lt;/code&gt; la inicialización se realizaba
siempre especificando un array de urls de redis, tal como se ven en los ejemplos
anteriores, pero para mi sorpresa la distribución de claves no se realiza en base a
la posición del servidor dentro del array sino en base a su ID, que por defecto es la
url del servidor. Por lo tanto, si cambiamos la IP o el puerto de un servidor redis, ¡la
distribución de claves cambia!&lt;/p&gt;

&lt;p&gt;La solución es especificar el array de servidores de la siguiente forma:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;REDIS_ARRAY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;id:  &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'redis_0'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;url: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;redis://127.0.0.1:6379/0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;id:  &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'redis_1'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;url: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;redis://127.0.0.1:6380/0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# more servers&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;id:  &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'redis_n'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;url: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;redis://127.0.0.1:63xx/0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;De esta forma, cuando agregamos un nuevo servidor físico para aumentar
la capacidad del cluster, lo único que tenemos que hacer es &lt;a href=&quot;http://redis.io/topics/replication&quot;&gt;replicar la base
de datos de Redis&lt;/a&gt; y luego cambiar sólo
la url correspondiente en el REDIS_ARRAY.&lt;/p&gt;

&lt;h4 id=&quot;script-de-arranque-para-ubuntu-1204&quot;&gt;Script de arranque para Ubuntu 12.04&lt;/h4&gt;

&lt;p&gt;Para simplificar el arranque y detención de los múltiples servidores Redis que
corren en el mismo servidor físico, modifiqué el script alojado en /etc/init.d/
para agregar fácilmente múltiples servidores en distintos puertos. En el siguiente
&lt;a href=&quot;(https://gist.github.com/jschwindt/6312036#file-redis-__port__)&quot;&gt;gist&lt;/a&gt; se puede ver el archivo
&lt;a href=&quot;https://gist.github.com/jschwindt/6312036#file-redis-__port__&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/init.d/redis-__port__&lt;/code&gt;&lt;/a&gt;
que sirve de base para crear los servidores.&lt;/p&gt;

&lt;p&gt;Una vez que tenemos dicho archivo en &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/init.d/&lt;/code&gt; y con los permisos de ejecución correspondientes,
lo único que hace falta hacer es crear links simbólicos cuyo nombre tiene que contener el port
en el que queremos que el servidor Redis esté escuchando:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; /etc/init.d/

&lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; redis-__port__ redis-6380
&lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; redis-__port__ redis-6381
&lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; redis-__port__ redis-6382
&lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; redis-__port__ redis-6383

update-rc.d redis-6380 defaults
update-rc.d redis-6381 defaults
update-rc.d redis-6382 defaults
update-rc.d redis-6383 defaults

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;También es necesario tener el archivo &lt;a href=&quot;https://gist.github.com/jschwindt/6312036#file-_common-conf&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/redis/_common.conf&lt;/code&gt;&lt;/a&gt;
que contiene toda la configuración común a todas las instancias. Opcionalmente también se
pueden especificar configuraciones particulares a cada instancia creando el archivo correspondiente
en &lt;a href=&quot;https://gist.github.com/jschwindt/6312036#file-_6380-conf&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/redis/_\[port\]&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Al arrancar un servidor, por ejemplo con:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/etc/init.d/redis-6380 start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;el script se encarga de crear la configuración correspondiente en &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/redis/redis-6380.conf&lt;/code&gt; y luego
arrancar la instancia con dicha configuración, que como ejemplo va a tener el siguiente contenido:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;port 6380
pidfile /var/run/redis/redis-6380.pid
logfile /var/log/redis/redis-6380.log
dbfilename dump-6380.rdb
include /etc/redis/_common.conf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Si además detecta que existe el archivo &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/redis/_6380.conf&lt;/code&gt;, la configuración tendrá además
incluido dicho archivo antes del &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_common.conf&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;migración-de-claves&quot;&gt;Migración de claves&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;TODO&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 

</feed>
