Como implementar el patrón FallBack con Polly

Cuando nos enfrentamos con procesos que son susceptibles de emitir excepciones existen varios patrones o estrategias que podemos seguir para gestionar el comportamiento ante el error. En este articulo vamos a ver como usando la librería Polly podemos llevar a cabo un patrón de Fallback para poder dar respuesta a nuestras necesidades.

¿Qué es Polly?

Polly es una librería Open Source para .NET que permite implementar ciertos patrones de resiliencia y gestión/recuperación de excepciones en aplicaciones .NET. Su uso simplifica el proceso permitiéndonos implementar los patrones como Retry, Timeout o Circuit-breaker de forma robusta y de forma muy simple a través de su sintaxis Fluent. Está pensada principalmente para procesos Web. Así veréis que muchos ejemplos disponibles se centran en temas relativos a peticiones Http. Sin embargo, Polly puede ser útil en una gran cantidad de situaciones y proyectos, aunque no estén en absoluto relacionados con la comunicación.

Patrón Fallback

Es un patrón de resiliencia para poder lidiar con fallos (latencia y caídas del sistema, entre otros). El patrón fallback proporciona un mecanismo a través del cual ofrecer una alternativa ante un servicio que está fallando.

Ejemplo

Por ubicar un ejemplo que podamos entender todos, tenemos una API de Avengers en el que dentro de esta API consulta un servicio que nos ha proporcionado Disney para devolver todos los Avengers que siguen en nómina. El flujo de nuestra aplicación seria el siguiente:

  1. Consultamos el servicio que nos proporciona Disney.
  2. a) si nos devuelve que la comunicación ha ido perfecta devolvemos los resultados que nos proporciona el servicio. b) si falla en lugar de atacar el servicio Disney, vamos a consultar unos datos que tenemos en una base de datos.

Este ejemplo implementa el patrón Fallback, pero existen muchos patrones que se pueden aplicar dependiendo de nuestras necesidades como por ejemplo, Timeout, Retry, Circuit-Breaker .. En algunos escenarios más complejos se pueden concatenar varios de estos patrones. El flujo quedaría de la siguiente forma: nuevo registro

Manos a la obra

Api Disney

En primer lugar nos vamos a crear la API de Disney, en esta API es un proyecto .NET Core de tipo API, sin mucho más misterio.

dotnet new webapi

Crearemos un fichero .json con los datos que va a devolver la API. A nivel de código tendremos un controlador de la siguiente forma.

using Disney.API.Model;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using System.Collections.Generic;

namespace Disney.API.API
{
    [Route("api/disney/[controller]")]
    [ApiController]
    public class AvengersController : ControllerBase
    {
        
        [HttpGet]
        public IEnumerable<Avenger> Get()
        {
            var file = System.IO.File.ReadAllText("./Data/data.json");
            var  result = JsonConvert.DeserializeObject<IEnumerable<Avenger>>(file);
            return result;           
        }
       
    }
}

Api Avengers

Al igual que en la anterior API, nos creamos un proyecto .NET Core de tipo API

dotnet new webapi

En este proyecto añadiremos el paquete de Polly

dotnet add package Polly

En primer lugar, vamos a implementar un método “GetAvengersDisney” que es el que va a llamar a la API de Disney, quedaría de la siguiente forma

  private  IEnumerable<Avenger> GetAvengersDisney()
        {
            var request = new HttpRequestMessage(HttpMethod.Get, "api/disney/avengers");
            var client = _clientFactory.CreateClient("disney");
            var response = client.SendAsync(request).Result;

            if (response.IsSuccessStatusCode)
            {
                using var responseStream = response.Content.ReadAsStreamAsync().Result;
                var avenger = System.Text.Json.JsonSerializer.DeserializeAsync<IEnumerable<Avenger>>(responseStream).Result;
                return avenger;
            }  
            else
            {
                throw new Exception();
            }           
        }

A continuación, nos crearemos el método que se va a ejecutar cuando la API de Disney no esté disponible. Por eso nos creamos el siguiente método “GetAvengersLocal”

 private IEnumerable<Avenger> GetAvengersLocal()
        {
            var file = System.IO.File.ReadAllText("./Data/data.json");
            var result = JsonConvert.DeserializeObject<IEnumerable<Avenger>>(file);
            return result;
        }

Con todo esto ya tenemos listo para poder ver cómo funciona la “magia” de Polly. En este caso hemos simplificado el uso de la librería para que pueda ser mucho más entendible en este ejemplo. Vamos a poner el siguiente código en el controlador

   [HttpGet]
        public IEnumerable<Avenger> Get()
        {
            IEnumerable<Avenger> result =null ;
            var fallBack = Policy
                        .Handle<Exception>()
                        .Fallback(() => { result = this.GetAvengersLocal(); });

            fallBack.Execute(() => { result = this.GetAvengersDisney(); });            
            return result;
        }

Que hace dicho código, nos definimos una Policy en la que vamos a indicar que en el caso de que se produzca una Excepción se lance el método que definamos en Fallback. Una vez definida la Policy lanzamos la “Action” que queremos hacer. ¿Como funciona? Primero se ejecuta la Action y en el caso de que se produzca una Excepción se lanzará la función que hemos puesto en el método Fallback.

¿Todo esto no lo podemos poner si nosotros controlamos la Excepción? La respuesta es que sí, si nos ceñimos a un caso básico, pero conforme nuestra aplicación tiene cada vez más complejidad y queremos que pueda adaptarse a los patrones cloud, hay que tener en cuenta cada vez más cosas y seguro que un simple try/catch se nos queda muy corto a las necesidades.

Resumen

Polly es una librería muy sencilla que nos permite poder implementar muchos patrones de resiliencia, de esta forma podamos tener nuestras aplicaciones más robustas y que se recuperen ante posibles imprevistos. En muchas de las aplicaciones que hacemos el tiempo en el que se recuperen ante errores es algo muy importante. Por citar un ejemplo que ha sido muy sonado últimamente las webs para poder atender a las reservas de las consolas de última generación sin que el sistema se caiga. Suponiendo que está alojado en Azure, y que por cada reserva se lanza un “evento” en background. Este evento podemos decir que es un mensaje en una cola de un service Bus. En la cola del Service Bus tenemos asociada una Function donde se encarga de realizar la reserva. Que ocurre cuando la cola del service Bus este llena. ¿Dejamos de atender “reservas”? Está claro que NO … pues con patrones como Fallback podemos implementar que cuando el Service Bus no funcione (por el motivo que sea), dejemos ese evento en otro “artefacto” por ejemplo una cola de un Storage, o simplemente se almacena en una base de datos. Podéis descargaros el ejemplo y ver el funcionamiento en el siguiente repositorio

Happy codding :)