martes, enero 25, 2011

Consejos para gestionar 100K TPS con menos de 1ms. de latencia

martes, enero 25, 2011 por Martín

Hace poco más de un mes publicaron en InfoQ una presentación muy interesante sobre como crear aplicaciones capaces de procesar un gran número de transacciones con una latencia muy pequeña.

Los autores de la presentación fueron Martin Thompson y Michael Barker y trabajan en LMAX. Esta copañía desarrolla lo que se llama un Trading Exchange. Este tipo de compañías ofrecen la posibilidad de comprar activos financieros al usuario final (i.e. nosotros) ofreciendo un pool de proveedores (agencias de trading, bancos, etc.) que compiten entre sí para ofrecerle al usuario el mejor precio. La idea es que si por ejemplo quiero comprar EUR/USD el sistema me va a ofrecer siempre la mejor oferta de entre los varios proveedores existentes en el trading exchange.

El gran reto de LMAX, como se menciona en la presentación, es que es una compañía (ellos anuncian que la primera) que se orienta al retail. Existen más empresas que hacen esto, pero normalmente se orientan a profesionales, agencias, fondos de inversión, etc. Es decir, que el potencial volumen de transacciones que puede recibir la aplicación es radicalmente mayor.

El equipo de LMAX surge como spinoff de Betfair. Ese es también su gran aval. Según ellos, Betfair procesa más transacciones por día que todas las bolsas europeas juntas. Aún así existen diferencias grandes entre el mundo del juego y las apuestas y el mundo del trading. En el juego la latencia no es relevante, mientras que en los mercados financieros la latencia es fundamental. Especialmente cuando operas con elementos con una alta volatibilidad como por ejemplo CFDs o FX, la latencia es un gran problema. Una alta latencia puede significar que tus clientes no obtengan los mejores precios, que vendan a peores precios de lo que podrían, o peor aún, que no puedan escapar a tiempo de grandes caídas porque sus órdenes de venta tardan en rellenarse y que pierdan gran parte de su capital. Básicamente:
Mayor Latencia = Mayor Incertidumbre = Mayor Riesgo

En la presentación comentan que al diseñar su arquitectura probaron con varios mecanismos. Primero probaron con SEDA, pero no funcionaba bien con grandes volúmenes de transacciones, después intentaron implementar un sistema basado en el Actor Model, pero ocurría lo mismo. Estos fracasos los llevó a la conclusión de que un modelo basado en colas es inadecuado para el procesado en tiempo real y de alto rendimiento. Básicamente las colas creaban mucha contención, había demasiados bloqueos, y la contención aumenta la latencia. Al final, para implementar el problema del productor/consumidor, en lugar de utilizar colas tomaron la decisión de crear una nueva estructura llamada Disruptor (parece que similar a los Phasers del JDK 1.7) cuyas caracteristicas principales serían:

  • Todo el proceso crítico (asumo que la entrada de precios y el relleno de órdenes de compra/venta) se hace en un único Thread. Al tener un hilo se elimina la contención provocada por tener múltiples hilos intentando acceder a la misma zona de memoria. Se eliminan también los posibles problemas de sincronización y potenciales "casos especiales".

  • Se utiliza una estructura de datos infinita y circular. Algo así como un array o circular buffer infinito donde nunca hay que controlar el tamaño. Ellos le llaman ring buffer.

  • No hay ordenación. No hay que andar cambiando y descambiando enlaces cada vez que se añade un elemento a la estructura.

  • No hay contención. Los datos llegan y se colocan al final (no hay problemas ya que es un único hilo). Los diferentes consumidores los consumen de manera secuencial.

  • Al utilizar un array y al forzar el acceso secuencial, se optimiza el aprovechamiento de las diferentes cachés L1,L2,L3 y también el uso de operaciones especiales en las CPUs de nuestros servidores.

  • Estos buffers se reservan al principio para evitar la recolección de basura.

  • Los buffers han de ser lo suficientemente grandes como para que no se sobreescriban los datos unos a otros. Comentan que ellos pre-reservan 20 millones de registros en algún buffer.


Ante todo buscar la simplicidad. Como ellos dicen en una parte de la presentación:
On a single thread you have ~3 billion instructions per second to play with: to get 10K+ TPS if you don't do anything too stupid.


Finalmente, nos recomiendan seis puntos para conseguir sistemas de alto rendimiento. Pero esto me lo guardo para el post siguiente :-)

comments

0 Respuestas a "Consejos para gestionar 100K TPS con menos de 1ms. de latencia"