Blog actualizado a WPMU 2.7 más algunos plugins









Acabo de actualizar los blogs a WordPress MU 2.7. Esperemos que no se haya roto nada. XD

Además he aprovechado para instalar algún plugin nuevo, porque llevo unos días jugando con Twitter (aún no me había subido a ese carro):

  • Twitme: genera un post en Twitter cada vez que se publica algo en el blog.
  • Twitter for WordPress: añade un widget con los últimos posts en Twitter.
  • LifeStream: genera una página en la que puedes agregar información de varios sitios, entre otros last.fm, facebook, picassa, flickr, google reader… es decir, agrega toda tu actividad on-line en un único sitio. Más o menos lo que comenté que venía haciendo yo con facebook, pero ahora en el blog.

UPDATE: he desactivado Twitme, no me ha acabado de gustar. Una cosa es el blog y otra twitter, y aparte teniéndolo todo integrado (twitter en el blog, y el blog y twitter en facebook) era muy redundante.

Seguridad "presencial" con Bluetooth en Linux









Estoy aprovechando estos días para “maquear” un portátil nuevo que me han dado en el curro, y he dado con un programa curioso: blueproximity.

¿Cuántas veces os habéis levantado de delante del ordenador sin bloquear la pantalla y algún compañero gracioso os ha gastado alguna jugarreta (enviar un mail con vuestra cuenta expresando vuestro amor incondicional por el resto de la plantilla, definir un alias ls=”rm -ri /”, o algo así)? ¿O estando en un cliente, se ha quedado información sensible en la pantalla mientras habéis ido a por el café? A mi ya me han gastado más de una de estas con lo que ya tengo el hábito de antes de levantarme, bloquear yo la pantalla. Sin embargo una solución automática no está de más.

La idea detrás de blueproximity es sencilla: bloquear automáticamente la pantalla si no estáis cerca, emparejándose con un dispositivo bluetooth (vuestro móvil) y en función de su proximidad activar o desactivar el salvapantallas. Simple y efectivo. Ahora el problema es si os olvidáis de coger el móvil al bajar a por el café. XD

Integración WordPress/Facebook









Llevaba bastante tiempo intentando integrar mi blog en WordPress y la cuenta de Friendface…digoooo… Facebook. El tema es que no entro tanto como pueda parecer en Facebook aunque el muro no pare de moverse: los enlaces compartidos son a través de Google Reader, también tengo integrada la cuenta de last.fm, al chat accedo por Adium (Mac) o Pidgin (Linux) que además permiten cambiar el estado de Facebook… al final uso Facebook como “agregador de actividad on-line” más que otra cosa. Para contar a los cuatro vientos mis pajas mentales ya tengo el blog y me resulta mucho más cómodo.

El problema era cómo integrar los posts del blog en Facebook. Durante un tiempo usé la aplicación de Facebook Simplaris Blogcast, pero tenía dos problemas:

  • no sé por qué no trataba bien acentos, eñes, etc. que salían como símbolos raros. Algún problema de encodings entre la aplicación y el blog.
  • tiene un nº límite de artículos, y cuando los llenas tienes que borrar alguno a mano. Quería algo completamente automático y ésta limitación me obligaba a hacer limpieza periódicamente.

Otra cosa que probé es con la utilidad de importación de RSS nativa de Facebook: los artículos se importan como notas, y eso no me gustaba por dos razones:

  • contenido duplicado en el blog y en Facebook. En Facebook quiero una reseña que apunte al blog.
  • comentarios también duplicados. En realidad, dos sitios independientes donde comentar: en las notas de Facebook y en el blog.

Hay otras aplicaciones tipo Simplaris, pero todas tenían distintas combinaciones de los problemas que encontré con estas dos que he comentado.

La semana pasada estuve un rato peleándome con Wordbook hasta que lo hice funcionar. Wordbook se diferencia de las otras dos alternativas en que, en lugar de ser simplemente una aplicación en Facebook que lee el RSS del blog periódicamente (con lo que la publicación no es inmediata), tiene una parte de aplicación en Facebook y un plugin para WordPress, de forma que en el momento que publicas un artículo en el blog, el plugin se conecta a la aplicación y se publica inmediatamente en Facebook. Además hace justo lo que quería: una reseña en el muro y una caja con la lista de los últimos artículos, en ambos casos con enlaces al blog con lo que ni el contenido ni los comentarios “se desperdigan” entre dos sitios.

El único problema es que no me acababa de funcionar: no publicaba y la configuración se reseteaba tras cada publicación, algo que también le pasaba a más gente. Investigando vi que era porque la comunicación plugin->app es a traves de JSON y no se “sanitizan” las cadenas eliminando saltos de línea. La solución fue tan sencilla como modificar las líneas 1127 y 1128 de wordbook.php (en el plugin para WordPress) para eliminar los saltos de línea con la función nl2br:

‘post_title’ => nl2br($post_title),
‘post_excerpt’ => wordbook_post_excerpt(nl2br($post_content),

Aparte, también he instalado el plugin Facebook Connector, que lo que hace es habilitar una especie de “single sign-on” basado en Facebook: integra tu cuenta de Facebook en el blog para que no tengas que logarte en un sitio u otro a la hora de hacer comentarios. La verdad para un blog personal como el mío me parece una chorrada pero total, para lo que me cuesta… :-) para una página en plan comunidad puede estar muy bien.

Optimizando el SQL de osCommerce









  • english
  • spanish

An entry about how the way SQL queries are built affects their performance, something I think many people don’t pay attention to:

For the last couple of days I’ve been struggling with the database of an osCommerce-based on-line shop. Everything ran perfectly until after four years of existence we have a DB with over 1500 categories and 16000 products, and the web is getting increasingly slower over time. We use a heavily modified osCommerce 2.2 installation, so moving to osCommerce 3 is out of the question (besides, it’s still alpha and this is a production site). After some hw and os-level optimization that didn’t help to improve performance, I had to start digging into the code and the DB.

First of all: activate the log-slow-queries option on mySQL’s config. This creates a new log with all the queries that take longer than expected to run (def. more than 10 seconds). With this information, we can concentrate on those queries that are hitting the bigger performance penalties and start the optimization process there.

To get you on context, this is the query that extracts all the products from a given category:

SELECT pd.products_name, pd.products_description, m.manufacturers_name,
p.products_image, p.products_quantity,  p.products_date_added,
p.products_id, p.manufacturers_id, p.products_price,
p.products_tax_class_id, IF(s.STATUS, s.specials_new_products_price, NULL)
AS specials_new_products_price, IF(s.STATUS, s.specials_new_products_price,
p.products_price) AS final_price
FROM products_description pd, products p
LEFT JOIN manufacturers m ON p.manufacturers_id = m.manufacturers_id
LEFT JOIN specials s ON p.products_id = s.products_id,
products_to_categories p2c
WHERE
p.products_status = ’1′ AND p.products_id = p2c.products_id AND
pd.products_id = p2c.products_id AND pd.language_id = ’3′ AND
p2c.categories_id = ’114′
ORDER BY p.products_date_added DESC;

On the first place for those unfamiliar with osCommerce, there are a heap of tables involved. Six on this example: categories, products, products_to_categories, products_description, manufacturers and specials (products with discounted prices). According to mysql-slow.log, that query was taking around 12 seconds to run. The log also read “Rows_examined: 1903433″. No wonder it was taking that long. :-D

Why so many “rows_examined”? Going back to DB theory on the University, ;-D if memory serves and leaving each particular DB engine’s optmizations aside, when selecting from several tables the DB engine does the cartesian product (product set) of all their tuples, this is, every possible combination of each tuple on one table with every other tuple on the other tables. After that, those combinations matching the WHERE criteria are selected (and I say engine optimizations aside because I don’t get the math here: 16k products, 16k descriptions, 1k5 categories, and just 1903433 combinations?). On the other hand with JOIN the related data is appended to those tuples already selected by the WHERE clause and ONLY TO THOSE.

So, why using more than one table on the where clause if it isn’t strictly necessary? On the previous example, we want to extract all the info of the products on a given category, but we select from the products, products_description and products_to_categories tables. Wouldn’t it be better using only the products_to_categories table on the from clause, reducing the result set to only the category we are interested on, and then get all the products’ information with JOIN? It’s the same principle as when piping data through several commands on a shell-script, using first grep and then cut: first reduce the data set, so that on each following step there is less data to spend CPU cycles with, increasingly reducing the overall processing time.

With this modification, the resulting query looks like:

SELECT pd.products_name, pd.products_description, m.manufacturers_name,
p.products_image, p.products_quantity,  p.products_date_added,
p.products_id, p.manufacturers_id, p.products_price,
p.products_tax_class_id, IF(s.STATUS, s.specials_new_products_price, NULL)
AS specials_new_products_price, IF(s.STATUS, s.specials_new_products_price,
p.products_price) AS final_price
FROM products_to_categories p2c
LEFT JOIN products p ON p.products_id=p2c.products_id
LEFT JOIN products_description pd ON p.products_id=pd.products_id AND pd.language_id=’3′
LEFT JOIN manufacturers m ON p.manufacturers_id = m.manufacturers_id
LEFT JOIN specials s ON p.products_id = s.products_id
WHERE p2c.categories_id = ’114′ AND p.products_status = ’1′
ORDER BY p.products_date_added DESC

The result sets of both queries are identical, but while the first one took around 12 seconds to run, the second one only takes a couple of milliseconds. :-) I’ve found several similar cases already on my osCommerce installation, that have gone from more than 10 seconds to milliseconds after rearanging the query in a similar fashion.

Another important point are indexes: I’ve found some queries that couldn’t be further optimized, they already used only one table on the from clause, the more restricting one, and then some joins. On these cases what I’ve found is that some table lacked an index on the foreign key column, and after adding it the execution time has gone down again from seconds to milliseconds.

CrossOver de gratis y otras ¿apuestas? ¿o marketing?









CodeWeavers, los creadores de CrossOver, una versión comercial de Wine que permite ejecutar aplicaciones y juegos nativos de Windows en Linux y MacOS X hicieron una apuesta hace unas semanas: si el precio de la gasolina bajaba de cierto límite, durante un día iban a dar licencias gratis de su software. El precio ha bajado y ese día es hoy. El software se puede bajar de aquí y pedir la licencia aquí, que debido a la sobrecarga de los servidores no se podrá activar hasta mañana (y por lo visto, sólo mañana).

Yo la verdad este tipo de emuladores al final sólo los uso para poder ejecutar Internet Explorer en el Mac y probar cómo se ve alguna web o acceder a otra que le pegue una patada a los estándares y no funcione correctamente con otros navegadores. Y a pesar de que IE se puede instalar también con la versión libre de Darwine e incluso un tío se curró un script para automatizarlo, CrossOver está bastante más pulido y toda la instalación es más simplona, click-click-click y ya tienes el Explorer (o el Word, o el Acrobat, o …) en Mac.

Y esto me recuerda a esa empresa de refrescos que apostó que si Guns N’ Roses cumplía la fecha de noviembre de 2008 para el lanzamiento de su nuevo disco iba a regalar un refresco a cada estadounidense. ¿Qué es ésto? ¿Una nueva forma de márqueting viral? ¿Una forma de conseguir titulares (y entradas en blogs ;-D) de forma gratuita? El tema con CodeWeavers lo veo claro, total es la discusión de siempre, copiar un software no cuesta NADA y menos si se distribuye a través de Internet con lo que no hay un soporte físico, así que dar el software, durante un día… tal vez sirve para hinchar los números de usuarios reales, o para hacerse una idea de los usuairos que realmente tienen (¿cuántos que tienen CrossOver pirata lo “legalizarán” hoy?). Sin embargo los de los refrescos si que van a tener que dar las latas, una a una. Ahí si que hay un gasto. ¿Realmente se esperaban que Axl y compañía cumplieran, o les ha salido el tiro por la culata? Aunque aún está por ver que el disco salga a tiempo, que después de 15 años a ver quién se fia ahora. ;-)

Validar campos de verificación en CakePHP









  • english
  • spanish

When designing a registration form for a web site, it’s usually a good idea to include verification fields for both the password and the e-mail address: a typo on the password would leave the user unable to log into the system, while one on the e-mail address would prevent us from reaching the user. Asking twice for these fields allows us and the user to verify that there are no typos and that both values are correct, thus avoiding future problems.

With CakePHP these verification fields can’t be validated automatically with the model’s definition rules, it has to be done programmatically on the controller as there is no model verification rule to check that an object’s field’s value equals that of another field (equalTo compares against a string as a literal, not against a variable).

By defining the following function on the model we are defining a custom verification rule that will allow us to do this verification properly on the model (or download it here, the plugin I use for code highlighting or wpmu itself keep trashing all > symbols):

<pre>        /**
         * Verifies that the field beeing validated matches the value of the
         * field in the second parameter. If omitted and the field beeing
         * validated’s name is NAME_verification, the value will be compared
         * to that of the NAME field.
         *
         * Examples:
         * <code>
         * var $validate(
         *      ’password_check’ =&gt; array(
         *              ’rule’ =&gt; array(‘verifies’, ‘password’),
         *              ’message’ =&gt; ‘The passwords don\’t match’
         *      ),
         *      ’email_verification’ =&gt; array(
         *              ’rule’ =&gt; array(‘verifies’),
         *              ’message’ =&gt; ‘The email addresses don\’t match’
         *      ),
         * )
         * </code>
         *
         * The second case will verify automagically against the "email" field.
         */

        function verifies($data, $field=null) {
                $keys = array_keys($data);
                $key = $keys[0];
                if(!is_string($field)) {
                        if( ($pos = strpos($key, "_verification")) === FALSE ) {
                                return FALSE;
                        }
                        $field = substr($key, 0, $pos);
                }
                return ($data[$key] == $this-&gt;data[$this-&gt;name][$field]);
        }</pre>

Cluster de correo escalable con software libre









  • english
  • spanish

At my previous job I was responsible for the MTA of a group of companies, handling around 3000 e-mail accounts spread over 20 domains. This MTA received around 150,000 mails daily, and over 95% of them was discarded/marked because it was identified as SPAM or viruses (as of last year, don’t know how this evolved since I left). We used a homegrown cluster of seven servers, which enabled us to scale as needed. And it was based on free software.

This is not an step-by-step installation guide with technical details and configuration files, but rather the story of the evolution of the service, the various problems that we faced, how we solved them, and the design decisions in each case.

Migration

The first incarnation of the server was in 2001 when we had to migrate the old server, which was starting to give lots of trouble, to more current software and hardware. I seem to remember it was a mail server from Netscape (!?) that stored the account information in an LDAP directory, but can’t recall the exact name or version of the product. The server we chosed for the migration was qmail-ldap, mainly because of the good reviews we read about its stability, reliability and security, ease of setup (personally I still think qmail is much simpler than eg sendmail) and because it also used an LDAP directory. The latter may seem a silly reason, but in the end the migration had to be done in extremis at a time that the original server wouldn’t even boot most of the times, and we got away with it with a simple ldapsearch and a little script that “translated” the LDAP scheme of one server into that of the other one. Over time the choice of qmail-ldap proved to be the right one, because thanks to its modular design it allowed us to progressively move from a one server deploy to the cluster that I refered about in the introduction.

This first server was a rack-mounted one, with redundant power supplies and hw RAID5, so that all the data was secure (or so we thought back then). We also rolled qmail-scanner and the Kaspersky anti-virus (there was no ClamAV yet, we moved to it some years later). The same server held the SMTP, POP, IMAP and WebMail (SquirrelMail) services.

Active/Passive backup

We had to do the first architectural upgrade a couple of months after the migration: a RAID5 hiccup lead to a corrupted filesystem which was quite difficult to fix. It became clear that the RAID discs and the redundant power supplies were not enough to ensure the data integrity and service availability, so we installed another server exactly like the first one, and synchronized the configuration and mailboxes using rsync and cron jobs. The switching from the primary to the backup server was manual back then, using NAT at the router.

Over time the server was upgraded to new models several times, but we kept the active/passive backup structure. The syncronization between both servers was also improved, with DRBD for the mailboxes and csync2 for the configuration, AV bases, and so on. Master-backup monitoring and service switch was automatized with heartbeat.

The SPAM flood, specialization by resources

Sometime around 2002-2003 viruses ceased beeing e-mail’s biggest problem: the increasing number of SPAM messages received every day was way worse. So we threw SpamAssassin into the mix. Over time this lead to an ever-increasing CPU and memory consumption, slowing the server to a crawl. At first it seemed that the only option was to migrate every year to a new, more powerful server (and what would we do with the old one then?), or have multiple servers and distribute all the domains among them in an attempt to distribute the load.

Finally we realized that we had two different kinds of resource needs, with different growth patterns:

  • HD space for the mailboxes: the number of mailboxes in our system was fairly stable and the vast majority of our users downloaded their e-mails using POP, so HD scalability wasn’t really that big of a problem for us. We could easily afford to upgrade disk every few years, moving the service to the backup server while we were upgrading the master one.
  • CPU for the filtering: SPAM was growing at an exponential rate, we basically needed to double the CPU power each year.

So, why not specialize our servers into storage servers and a filtering farm? We moved the SMTP service from the main servers to a front-line of SMTP servers with the follwing characteristics:

  • they were off-the-shelf PCs and their configuration was practically identical (no variations appart from hostnames and IP addressess). We prepared a system image we could easily dump in a matter of minutes to a new PC, in case one of the servers went down or we needed more raw CPU power because of an increase in SPAM.
  • we had a router load-balaincing port 25 among all these servers.
  • all these SMTP servers were independent from the central ones, except for the final step of delivering the already analized mail to its destination mailbox: each server had a local copy of the LDAP directory (synchronized with slurpd), a copy of all the configuration files and all the AV bases and the SpamAssassin bayesian database (synchronized with csync2), and a DNS resolver/cache (dnscache).
  • they did local logs, but also sent them to a centralized syslog server for easier analysis.
  • they didn’t store the mails locally for later delivery, in other words they had no delivery queue: e-mails were analyzed on the fly during the SMTP session and if one of them met certain anti-SPAM/AV criteria (blacklisted IP, a number of RBL hits, certain keywords, etc.) it was immediatelly rejected with an SMTP error and the connection was closed; on the other hand if the mail was let through (it was either legitimate, or marked as possible SPAM), it was sent to the central server on the spot, and the filtering server never gave the OK to the origin MTA until the mailboxes server acknowledged the delivery. This is done quite simply with qmail by means of replacing the qmail-queue binary with the qmail-qmqpc one. By doing this we were able to guarantee that no mail would be lost in the event that a filtering server crashed, as the origin MTA wouldn’t receive the OK from us and would re-try the delivery after a couple of minutes.

Mailboxes, the POP and IMAP services, the LDAP master, webmail, and the remote queue remained in the central server, although most of them could have been moved to independent servers if needed, but we never needed to.

Specialization by type of client

The next problem we faced came about 2-3 years ago when image- and PDF-based SPAM became popular: we added an SpamAssassin plugin which re-composed animated GIF images and did OCR to all image attachments. This extra analysis greatly increased our CPU needs (we had to go from 2 or 3 filtering servers to 5 in a couple of days) and even so there were times when a server got overloaded for some 5-10 minutes and an e-mail could take not less than 2 minutes to be processed, delivered and SMTP-OK’d. When this happened and the sending party was another MTA it represented no bigger issue, as in the event of a timeout or disconnection the remote server would re-try the delivery several times; however, if the sender was an end-user with his MUA, a longer-than-usual delivery time or (God forbid) an error message from Outlook because of an eventual dropped connection lead to a phone call to the IT team because “the mail wouldn’t work.” :-)

The solution was splitting the SMTP and analysis farm into two: one for external mail and another for internal ones, for our users. The first farm is the one the DNS’ MX records pointed to, and had all the SPAM filtering options activated; while the second one retained the domain name end users used as the SMTP server in their MUAs, had all the heavy-weight lifting filters disabled and required SMTP authentication (wouldn’t accept non-authenticated sesions even for local domains). This way all external e-mail coming from remote MTAs would go through all the filters, and our users went to the privileged servers with somewhat lesser filering capabilities (but enough for internal mail) and great response times.

The big picture

mpstat









  • english
  • spanish

One of the most basic variables to monitor on a system is CPU usage, something we usually do interactively with top or with vmstat when we want to script the process.

But there’s a problem with multi-CPU systems, with either physical or virtual CPUs, because in these cases vmstat shows the average usage across all CPUs. Depending on the software or hardware architecture, a CPU at 100% can become a bottleneck and produce process blockings ever if the other CPUs are completely idle, while vmstat would show us just a 50% CPU usage. This is quite typical with single CPU systems with HyperThreading technology.

An alternative to vmstat for these situations is mpstat, from the sysstat package, that shows the individual per-CPU usage rates. Very useful when writing a script for displaying graphs with Cacti or raising alarms with Nagios.

# mpstat -P ALL
Linux 2.6.9-023stab046.2-enterprise (domain.com) 	25/09/08
20:39:02     CPU   %user   %nice %system %iowait    %irq   %soft   %idle    intr/s
20:39:02     all    0,79    0,00    0,17    5,74    0,00    0,00   93,30      0,00
20:39:02       0    0,87    0,01    0,19    6,85    0,00    0,00   92,09      0,00
20:39:02       1    0,87    0,00    0,18    5,94    0,00    0,00   93,00      0,00
20:39:02       2    0,74    0,00    0,16    5,14    0,00    0,00   93,96      0,00
20:39:02       3    0,68    0,00    0,16    5,02    0,00    0,00   94,15      0,00

iostat & iotop: I/O debugging









  • english
  • spanish

A couple of months ago, we had an interesting issue at a customer: an application wasn’t performing well, but the system had more than 20% CPU idle and wasn’t swapping memory, so it wasn’t a lack of resources. After a deeper look into vmstat, we saw a constant 30% of CPU in I/O state. We had some kind of I/O bottleneck.

To discover the root of the issue we used two programs:

  • iostat (comes with the sysstat package): similar to vmstat or ifstat, but shows I/O operations per device and partition, updating its output every X seconds.
  • iotop: like the classic top, sorting the processes according to their I/O rate.

By using these two utilities it’s quite easy to discover which process is creating the I/O bottleneck, and on which particular device.

In our case, the problem was a RAID controller that was giving a terrible writing performance, coupled with a process that was doing around 15 small, random access writes per second.