[Este post también aparece en el blog de github de Dustin].
Hemos creado un par de nuevas operaciones de protocolo para los desarrolladores de aplicaciones. El objetivo general de añadir una operación es mantenerla ortogonal a otros comandos y, al mismo tiempo, mejorar la funcionalidad de forma que te permita hacer cosas que antes no se podían hacer, o que al menos eran comunes y difíciles de hacer de forma eficiente.
Aquí tienes una descripción de los nuevos comandos y una idea de cómo podrían utilizarse.

Sincroniza
El otro concepto nuevo que hemos introducido es un sincronizar comando para proporcionar una barrera en la que se espera a que los datos de una aplicación cambien de estado de formas específicas, como que un elemento cambie de un valor conocido o alcance un nivel especificado de durabilidad.
Un breve resumen de cómo funciona esto en membase (para la que hemos implementado sincronizar para empezar): El motor de Membase tiene lo que es efectivamente un espacio de aire entre la interfaz de red y el disco. Casi todas las operaciones se procesan desde y hacia la RAM y luego se replican y persisten de forma asíncrona. Los elementos entrantes están disponibles para ser solicitados inmediatamente tras el retorno de su comando de mutación (es decir, la siguiente solicitud de una clave dada devolverá el elemento que se acaba de establecer), pero la replicación y la persistencia se producirán pronto.
La membrana sincronizar es similar a fsync o quizás msync en el sentido de que primero puedes cargar libremente elementos en membase y comprobar que los ha aceptado en el nivel más bajo de disponibilidad. Cuando hayas almacenado un conjunto de elementos críticos, podrás emitir un sincronizar con el conjunto de sus claves críticas y el nivel de durabilidad requerido y el servidor bloqueará hasta que se alcance este nivel (o suceda algo que nos impida hacerlo).
Hubo discusiones sobre diferentes semánticas (como un modo totalmente sincronizado o un modo específico). set+sync ). Mientras que un set+sync sería un viaje de ida y vuelta menos que hacer un configure y sincronizarEn la práctica, la diferencia es mínima, ya que el efecto típico de un sincronizar es un retraso. Esto, sin embargo, tiene el coste de hacer muy difícil cualquier tipo de sincronización práctica. Uno puede sincronizar después de cada comando, después de un lote grande, o en elementos seleccionados dentro de un lote grande.
¿Con qué se puede sincronizar?
La especificación permite supervisar un determinado conjunto de claves para uno de los siguientes cambios de estado:
- Esperar a la replicación
- Esperar la persistencia
- Esperar a la replicación y Persistencia
- Esperar a la replicación o Persistencia
- Esperar a la mutación
También hay espacio para una bandera ligeramente discutida de "cualquiera vs. todas" para las claves donde puedes entregar al servidor un conjunto de claves y ser informado tan pronto como cualquiera de ellas cambie al estado deseado en lugar de esperar a todas ellas.
Ejemplo
Dada una bolsa gigante de artículos, con una mezcla de artículos importantes (que queremos guardar) y artículos realmente importantes (que debemos garantizar que se guardan antes de devolverlos), hagamos lo correcto.
"""Almacena una colección de elementos.
Los elementos se almacenarán de forma asíncrona y, a continuación, los elementos importantes
se sincronizarán antes de volver."""
importante = []
para i en artículos:
mc.configure(i.clave, i.exp, i.banderas, i.valor)
si i.importante:
importante.añadir(i)
mc.sync_replication_or_persistence(importante)
(tenga en cuenta que un cliente python admite estas funciones, pero no exactamente con esta API, pero esto debería darle la idea básica)
Del mismo modo, se pueden limitar las inserciones para que los elementos no entren más rápido de lo que pueden escribirse en el disco.
"""Almacena una colección de objetos sin construir un gran
retraso en la replicación."""
para n, i en enumerar(artículos, 1):
mc.configure(i.clave, i.exp, i.banderas, i.valor)
si (n % sync_every) == 0:
mc.sync_replication(i)
Cada sync_every (por defecto 1000) espera a que la sincronización se ponga al día. Configuración de sync_every a uno nos haría sincronizar completamente cada elemento.
Toque
Hemos oído decir a bastantes propietarios de proyectos que les gustaría tener la posibilidad de tener elementos con una ventana deslizante de caducidad. Por ejemplo, en lugar de que un elemento caduque a los cinco minutos de mutar (que es como se especifica actualmente el tiempo de vida de un objeto), nos gustaría que caducara a los cinco minutos de inactividad.
Si estás familiarizado con las cachés LRU (como memcached), debes tener en cuenta que esto es semánticamente muy diferente de LRU. Con una LRU, efectivamente no nos preocupamos por los datos antiguos. Los casos de uso de toque nos obligan a desactivar activamente el acceso a los datos inactivos según un calendario definido por el usuario.
En toque se puede utilizar para ajustar la expiración de una clave existente sin tocar el valor. Utiliza el mismo tipo de definición de caducidad que todos los comandos de mutación, pero sin tocar los datos.
Similar a toque añadimos un gat (llegar y tocar) que devuelve los datos y ajusta la caducidad al mismo tiempo. Para la mayoría de los casos de uso, gat es probablemente más apropiado que toquepero realmente depende de cómo construyas tu aplicación.
Ejemplo de uso
Utilización de toque y gat son bastante sencillas. Un patrón muy común podría ser el almacenamiento de datos de sesión en el que queremos que los datos "inactivos" se eliminen rápidamente, pero que los datos activos permanezcan mientras estén activos.
"""Obtener un objeto de sesión válido para el ID de sesión dado.
Las sesiones sólo durarán cinco minutos.
Unauthenticated se lanzará si la sesión
no se puede cargar.""
s = mc.gat(session_id, edad_máxima_sesión)
si no s:
throw No autenticado()
devolver s
Este ejemplo mostraba un simple cargador de sesiones que mantiene la sesión viva y envía las sesiones de misión a otra parte de la pila de aplicaciones que puede ocuparse de los inicios de sesión y esas cosas.
Disponibilidad
Lo hemos estado utilizando, pero aún no hemos conseguido la disponibilidad universal.
Servidores
Membase 1.7 proporciona este completo toque y gat funcionalidad y parcial sincronizar funcionalidad.
Para sincronizarSólo se admite la espera de replicación, y sólo de una única réplica. El protocolo permite el seguimiento de hasta 16 réplicas, pero membase como clúster utiliza replicación transitiva, por lo que no es posible realizar un seguimiento cuando la segunda réplica está completa desde el host primario (¡y mucho menos la decimosexta!).
Del mismo modo, hemos escrito la mayor parte del código para la sincronización en la persistencia, pero antes de nuestras estrategias de almacenamiento 2.0, pensamos que podría ser más perjudicial que útil en la mayoría de las aplicaciones. Incluso con nuestras estrategias 2.0, es probable que no sea tan apropiado como el seguimiento de la replicación para todos los datos, salvo los absolutamente más importantes.
Memcached 1.6(ish) tiene soporte para toque y gat en el motor por defecto (que también se incluye en Membase).
Clientes
Además de mc_bin_client.py (que es una especie de cliente de referencia/juego que se envía con membase y con el que escribimos muchas de las herramientas), aún tenemos soporte en dos clientes, pero estamos considerando la función como "en evolución", ya que estamos tratando de encontrar la mejor manera de hacerlo. Cualquier comentario será más que bienvenido.
Java
spymemcached 2.7 tiene soporte para toque, gaty sincronizar.
C Agudo
El cliente enyim C# para memcached tiene soporte para toque, gaty sincronizar en un lanzamiento que debería llegar a las estanterías muy pronto.
Fantástico. Revisaré el nuevo código y le daré mi opinión tal y como me ha pedido: ¡gracias por todo el trabajo!
Creemos que podría ser más perjudicial que útil en la mayoría de las aplicaciones. Incluso con nuestras estrategias 2.0,