Métodos de publicación para tus feeds

Facebook, Twitter, Instagram e incluso LinkedIn. La mayoría de aplicaciones web sociales que usamos hoy en día tienen el mismo prototipo de estructura base:

  • Un sistema de amigos / contactos que relaciona usuarios entre sí.
  • Un feed de las últimas publicaciones (texto, foto, vídeo, enlaces…) de tus contactos o amigos.

El feed incluso puede (suele) ser más complejo y no basarse únicamente en la temporalidad de los eventos sino que puede tener en cuenta un algoritmo de puntuación. Dicho algoritmo puede ser tan complejo cómo se quiera, pero, por ahora, no me centraré en este aspecto.

PUBLISHING METHODS

En lo que si me gustaría centrarme es en el diseño de la gestión de las publicaciones de los usuarios, lo que significa cuál es el proceso por el que una publicación de un usuario llega al timeline de alguien que le sigue o es amigo suyo.

Evidentemente, en un aplicación con poca complejidad y pocos usuarios, la cosa es relativamente sencilla y prácticamente cualquier implementación que se nos venga a la cabeza nos va a servir.

Sin embargo, si nos paramos a pensar en la cantidad ingente de personas que hay mirando el timeline de su red social de forma concurrente, lo primero que se nos debería venir a la cabeza es un océano de consultas a base de datos algo complejas y contra tablas con muchísimos registros. Eso se traduce en cero escalabilidad y, según la solución tomada, incluso inviabilidad.

Entonces, cómo se suelen resolver este tipo de problemáticas?

PUSH & PULL (EMPUJAR Y JALAR… xD)

Principalmente hay dos tipos de publishing methods: el método push (o Fan-out-on-write) y el método pull (o Fan-out-on-load).

El primero de los casos consiste en la distribución de la persistencia de las publicaciones, lo que significa tener persistencia a nivel de feed de usuario.

Cómo podéis imaginar, éste modelo añade complejidad (y coste) en las operaciones de escritura, pues una nueva publicación, una modificación o una eliminación implicarán múltiples escrituras en base de datos.

Por otro lado, el método pull consiste en la recopilación de todas las publicaciones en tiempo de ejecución (cuándo el usuario entra a su feed). A diferencia del anterior, en éste modelo la escritura es una operación simple en el sistema, sin embargo, la lectura se puede volver lenta y tediosa (a pesar de la denormalización y las cachés). Al fin y al cabo, lo más probable es que tengamos que hacer muchísimas consultas con joins en tablas con muchísimas filas.

TRADE-OFFS

Cómo un primer enfoque, puede resultar lógico centrarse en simplificar las operaciones de lectura, pues si pensamos en el uso de la mayoría de éstas aplicaciones, el ratio de operaciones de lectura es muy favorable respecto a las operaciones de escritura.

Pero, cómo siempre, no hay una solución única y válida para todos los casos, pues si nos paramos a pensar detenidamente, nos daremos cuenta de qué, por ejemplo, la publicación de un usuario con N millones de usuarios, puede implicar N millones de operaciones de escritura en un período de tiempo extremadamente corto. Lo cuál, cómo veremos a continuación, puede llegar a tumbar un sistema cuál ataque DoS.

LA ANÉCDOTA

A modo de anécdota: se dice y se comenta que cuándo Twitter empezó a tener usuarios celebrities con muchos seguidores, la empresa tuvo que establecer acuerdos con los usuarios y sus representantes para que, antes de publicar un nuevo tweet, éstos les avisarán y así pudieran escalar temporalmente su infraestructura.

Curioso, gracioso y probablemente real, pero sobretodo totalmente representativo de la complejidad que conlleva tomar decisiones en éste tipo de ámbitos.

SO MERGE…

Entonces, cuál es la solución?  Pues cómo en la mayoría de casos, una solución híbrida que se adapte a nuestras necesidades. Concretamente en este caso, podríamos basar nuestra solución en función de las características del emisor, de las del receptor, o de las de ambos.

Por un lado, si nos centramos en el emisor, lo suyo será aplicar el método de push en aquellos usuarios que publiquen poco y pull en los que publiquen mucho. Por otro lado, si nos centramos en el receptor, lo lógico sería aplicar un modelo push en aquellos usuarios que consulten muchas veces su feed. Finalmente, si nos centramos en ambos, podemos buscar soluciones específicas para aquellos usuarios que realicen pocas publicaciones pero que a su vez tengan muchos seguidores.

Finalmente os dejo este enlace dónde se habla sobre el diseño de este tipo de sistemas y dónde se hace referencia a bibliografía muy interesante:
http://highscalability.com/blog/2013/10/28/design-decisions-for-scaling-your-high-traffic-feeds.html