Uso de Patrones de Diseño en la nube - Parte I

Escrito por Francisco Ricardo Gil González - 13/11/2015

Patrones Vs Categorías

Como la gran mayoría de buenos desarrolladores saben, los patrones de diseño son paradigmas que aportan soluciones a problemas típicos y recurrentes que nos podemos encontrar a la hora de desarrollar una aplicación.  Seguramente que la aplicación que estamos desarrollando sea exclusiva, pero tendrá partes comunes con otras aplicaciones: acceso a datos, creación de objetos, operaciones entre sistemas etc. En lugar de reinventar la rueda, podemos solucionar problemas utilizando algún patrón, ya que son soluciones probadas y documentadas por multitud de programadores.

Como comentaba al principio, las 8 categorías que se han definido para tratar de englobar las áreas en la nube en las que podemos encontrar problemas comunes son:

  • Disponibilidad: Define la proporción de tiempo que el sistema es funcional. Se verá afectado por los errores del sistema, problemas de infraestructura, los ataques maliciosos y la carga del sistema. Por lo general se mide como un porcentaje del tiempo de funcionamiento. Las Aplicaciones deben ser diseñadas e implementadas de manera que garantice la máxima disponibilidad.
  • Gestión de datos:  Es el elemento clave de las aplicaciones de la nube, y la mayoría de las influencias de los atributos de calidad. Los datos son normalmente alojados en diferentes lugares y en varios servidores por razones como el rendimiento, la escalabilidad o la disponibilidad, y esto puede presentar problemas. Por ejemplo, la consistencia de datos se debe mantener, y los datos normalmente tendrán que ser sincronizados a través de diferentes lugares.
  • Diseño e Implementación: Un buen diseño abarca factores como la consistencia y la coherencia en el diseño de componentes y sus despliegues, facilidad de mantenimiento para simplificar la administración y el desarrollo, y para permitir la reutilización de componentes y subsistemas para su uso en otras aplicaciones y en otros escenarios. Las decisiones tomadas durante la fase de diseño e implementación tienen un gran impacto en la calidad y el costo total de las aplicaciones y servicios alojados en la nube.
  • Mensajería:  La naturaleza distribuida de las aplicaciones en la nube requiere una infraestructura de mensajería que conecte los componentes y los servicios, idealmente de una manera imprecisa con el fin de maximizar la escalabilidad. La Mensajería asíncrona es ampliamente usada, y proporciona muchos beneficios, pero también trae desafíos tales como el ordenar los mensajes, gestión de mensajes dudosos, etc.
  • Gestión y Seguimiento:  Las aplicaciones de la nube se ejecutan en un centro de datos remoto en el que no tiene el control total de la infraestructura o, en algunos casos, el sistema operativo. Esto puede hacer que la gestión y el seguimiento sea más difícil que un despliegue on-premises. Las solicitudes deben exponer información de tiempo de ejecución para que los administradores y los operadores pueden usar para administrar y supervisar el sistema, así controlar los requerimientos de cambio del negocio y las personalizaciones sin necesidad de que la aplicación sea detenida o redistribuida.
  • Rendimiento y Escalabilidad:  El rendimiento es un indicador de la capacidad de respuesta de un sistema para ejecutar cualquier acción dentro de un intervalo de tiempo estimado, mientras que la escalabilidad es la capacidad de un sistema, ya sea para manejar los aumentos de carga sin impacto en el rendimiento o para que los recursos disponibles incrementen fácilmente. Las aplicaciones en la nube generalmente se encuentran con cargas y picos de trabajo variables de actividad. La predicción de éstos, especialmente en un escenario multi-tenant, es casi imposible. En su lugar, las aplicaciones deben ser capaces de escalar dentro de los límites para satisfacer los picos de demanda, y la escala en que la demanda disminuye.
  • Resiliencia: La capacidad de un sistema para manejar con solvencia los fallos y recuperarse de los mismos. La naturaleza de cloud hosting donde las aplicaciones son a menudo multi-tenant, el uso compartido de los servicios de la plataforma, la competencia por recursos y ancho de banda, y el hecho de estar corriendo en el mismo hardware significa que hay una mayor probabilidad de que surjan fallos transitorios y más permanentes. La detección de fallos y la recuperación rápida y eficiente es clave en este escenario.
  • Seguridad: La capacidad de un sistema para evitar acciones maliciosas o accidentales fuera del uso diseñado, y para evitar la divulgación o pérdida de información. Las aplicaciones en la nube están expuestas en Internet, a menudo son expuestas con carácter público. Las aplicaciones deben ser diseñadas y desplegadas de una manera que estén protegidas de los ataques maliciosos, restringir el acceso sólo a los usuarios sólo permitidos, y proteger los datos sensibles.

Para cada una de estas categorías, veremos patrones comunes diseñados para ayudar a los desarrolladores a resolver los problemas a los que se enfrentan con regularidad.

Nosotros veremos, a lo largo de varios artículos, en que categorías se pueden utilizar, el contexto y problema que resuelve, la solución, consideraciones, cuando usarlo y un ejemplo de su uso. En este primer artículo veremos el primer patrón, para seguir en los siguientes artículos con los varios patrones más.

Patrón Cache-Aside

Categorías donde aplicarlo: Gestión de Datos y Rendimiento y Escalabilidad

​Cargar los datos mayoritariamente demandados en una memoria caché de un almacén de datos. Este patrón puede mejorar el rendimiento y también ayuda a mantener la coherencia entre los datos almacenados en la memoria caché y los datos en el almacén de datos subyacente.

Contexto y Problema

Las aplicaciones utilizan una caché para optimizar el acceso repetido a la información en un almacén de datos. Sin embargo, para ello hemos de estar seguros que los datos almacenados en caché tienen que ser consistentes con los datos en el almacén de datos. Las aplicaciones deben implementar una estrategia que ayuda a asegurar que los datos en la caché estén actualizados en la manera de lo posible, pero también pueden detectar y manejar situaciones que surjan cuando los datos de la caché estén obsoletos.

Solución

Muchos de los sistemas de almacenamiento en caché comerciales proporcionan lectura y escritura a través de las operaciones de lectura / escritura de la base de datos subyacente. En estos sistemas, una aplicación recupera los datos de la memoria caché. Si los datos no están en la caché, se recuperan de forma transparente del almacén de datos subyacente y se agrega a la caché. Cualquier modificación de los datos almacenados en la memoria caché se escriben automáticamente al almacén de datos también.

Para cachés que no proporcionan esta funcionalidad, es responsabilidad de las aplicaciones que utilizan la memoria caché para mantener los datos en la caché.

Una aplicación puede emular esta funcionalidad implementando el patrón Cache-Aside. Este patrón carga de forma efectiva datos en la caché bajo demanda.

Consideraciones

Hay que tener en cuenta los siguientes puntos en el momento de decidir la forma de aplicar este patrón:

         Vida útil de los datos almacenados en caché. Muchos cachés implementan una directiva de caducidad que hace que los datos sean invalidados y se eliminan de la memoria caché si no se accede por un período determinado. Para que este patrón sea efectivo, asegúrate de que la política de vencimiento coincide con el patrón de acceso para las aplicaciones que utilizan los datos. Recuerda que el almacenamiento en caché es más eficaz para los datos relativamente estáticos o datos que se leen con frecuencia.

         El vaciado de Datos. La mayoría de los cachés tienen un tamaño limitado en comparación con el almacén de datos de donde se originan los datos, y van a vaciar a los datos si es necesario. La mayoría de los cachés adoptan una política de uso de utilización más reciente para seleccionar los elementos a vaciar, pero esto se puede personalizar. Configure la propiedad global de vencimiento y otras propiedades de la memoria caché, y la propiedad de vencimiento de cada elemento almacenado en caché, para ayudar a asegurar que el caché es rentable. No siempre puede ser adecuado aplicar una política de vaciado global para todos los elementos de la caché. Por ejemplo, si un elemento de la caché es muy costoso de recuperar del almacén de datos, puede ser beneficioso mantenerlo en la memoria caché a expensas de otros elementos a los que son accedidos con mayor frecuencia, pero son menos costosos.

         Precargar la Caché. Muchas soluciones rellenan previamente la memoria caché con los datos que es probable que necesite como parte del proceso de inicio de una aplicación. Este patrón puede ser útil si algunos de estos datos expiran o son eliminados.

         Consistencia. Implementar este patrón no garantiza la coherencia entre el almacén de datos y la memoria caché. Un elemento en el almacén de datos se puede cambiar en cualquier momento por un proceso externo, y este cambio no se refleja en la memoria caché hasta la próxima vez que el elemento se cargue en la memoria caché. En un sistema que replica datos a través de almacenes de datos, este problema puede llegar a ser especialmente grave si se produce la sincronización con mucha frecuencia.

         Almacenamiento en caché local (In-Memory). Una caché podría ser local a una instancia de la aplicación y ser almacenada en memoria. El patrón puede ser útil en este entorno si una aplicación accede repetidamente a los mismos datos. Sin embargo, si una caché local es privada y las diferentes instancias de la aplicación tienen una copia de los mismos datos en la caché, los datos podrían convertirse rápidamente inconsistentes entre cachés, por lo que puede ser necesario revisar las políticas de cuando expiran los datos almacenados en una memoria caché privada y actualizarlos con mayor frecuencia. En estos escenarios puede ser apropiado el uso de un mecanismo de caché distribuida.

Cuando usar este patrón:

         La caché no facilita operaciones nativas de lectura y escritura.

         La demanda de recursos es impredecible. Este modelo permite a las aplicaciones cargar datos bajo demanda. Se predicen qué datos de una aplicación requerirá de antemano.

Este patrón podría no ser adecuado:

         Cuando el conjunto de datos en caché es estático

         Para el almacenamiento en caché de la información de estado de sesión en una aplicación web alojada en una granja de servidores web. En este entorno, se debe evitar la introducción de dependencias basadas en la afinidad de cliente-servidor.

Ejemplo:

En Windows Azure se puede usar Windows Azure Caché para crear una memoria caché distribuida que puede ser compartida por varias instancias de una aplicación. El método GetMyEntityAsync en el siguiente ejemplo de código muestra una implementación de este patrón basado en Windows Azure caché. Un objeto se identifica mediante el uso de un ID con valor de  número entero como clave. El método GetMyEntityAsync genera un valor de cadena con base en esta clave (la API de Windows Azure caché utiliza cadenas de valores de clave) y trata de recuperar un elemento con esta clave de la caché. Si se encuentra un elemento coincidente, lo devuelve. Si no hay ninguna coincidencia en la caché, el método GetMyEntityAsync recupera el objeto de un almacén de datos, lo agrega a la caché, y luego la devuelve (el código que realmente recupera los datos del almacén de datos se ha omitido debido a que es el almacén de datos dependiente). Hay que tener en cuenta que el elemento en caché está configurado para expirar a fin de evitar de que se quede obsoleto si se actualiza en cualquier otro lugar.

private DataCache cache; 
...
public async Task<MyEntity> GetMyEntityAsync(int id) {
// Define una
clave única para este método y sus parámetros.
var key = string.Format("StoreWithCache_GetAsync_{0}", id);
var expiration = TimeSpan.FromMinutes(3);
bool cacheException = false;
try
{
// Intentamos
obtener la clave de la caché.
var cacheItem = cache.GetCacheItem(key);
if (cacheItem != null)
{
return cacheItem.Value as MyEntity;
}
}
catch (DataCacheException)
{
// si hay
algún problema lanzará una excepción 
//  y evitará el uso de la caché para el resto de
la llamada.
cacheException
= true;
}
// Si hay un
error de caché, obtenemos el id del almacén original y // lo almacenaremos en
la caché.
// el código
ha sido omitido por ser de un almacén dependiente este // ejemplo. 
var entity = ...;
if (!cacheException)
{
try
{
// Evitamos
el almacenamiento en caché de un valor nulo
if (entity != null)
{
// Ponemos el
elemento con tiempo de caducidad dependiendo de la 
// criticidad
que pueda ser tener datos obsoletos
cache.Put(key, entity, timeout: expiration);
}
}
catch (DataCacheException)
{
// If there is a cache related issue, ignore it
// and just return the entity.
}
}
return entity;
 }
El método UpdateEntityAsync que voy a mostrar a continuación muestra cómo invalida un objeto en la caché cuando el valor es modificado por la aplicación. Este es un ejemplo de un enfoque de escritura simultánea. El código actualiza el almacén de datos original y luego elimina el elemento de la caché de la caché mediante una llamada al método Remove, especificando la clave (el código para esta parte de la funcionalidad se ha omitido, ya que será el almacén de datos dependiente).
public async Task UpdateEntityAsync(MyEntity entity) {
// Actualizar
el objeto en el almacén de datos original.
await this.store.UpdateEntityAsync(entity).CongureAwait(false);
// Obtener la
clave correcta para el objeto en caché.
var key = this.GetAsyncCacheKey(entity.Id);
// Entonces,
Eliminamos el actual objeto de la caché
this.cache.Remove(key); }
private string GetAsyncCacheKey(int objectId) {
return string.Format("StoreWithCache_GetAsync_{0}", objectId); }

En el siguiente artículo continuaremos viendo patrones de diseño, que a buen seguro nos serán de gran utilidad a la hora de diseñar e implementar soluciones en la nube.

 

Francisco Ricardo Gil González
MVP CLUSTER | Especialista en SharePoint & Office 365
francisco.gil@fiveshareit.es
Linkedin
http://www.mvpcluster.es

***