Je ne sais pas si c’est comme ça pour vous. Il y a quelques petits bouts de technologie que j’ai dans une liste que j’ai nommée « j’aimerais bien l’essayer un jour si le contexte s’y prête bien ». La liste est plus longue que d’occasions que j’ai de la mettre en oeuvre.
Au moins, la liste existe!
Cependant, il y a un peu d’espoir pour mes rêves. Dans le cadre d’un projet, j’ai eu à revisiter le scénario de la mise en cache des éléments récupérés d’un service externe. Ça a été la parfaite occasion de sortir l’utilitaire AsyncExpiringLazy de ma manche!
Puisqu’il s’agissait d’une liste de personnes utilisée au cœur de l’application, assez volumineuse et longue à récupérer. Je me devais de mettre en place un processus de mise en cache. De plus, par sa nature humaine, cette liste était amenée à changer avec le temps. Elle se devait d’être rafraîchie périodiquement avec des nouvelles données.
Ce qui est intéressant avec AyncExpiringLazy est justement dans son nom. À la base, il s’agit d’une classe qui a le même fonctionnement que la classe Lazy<T> de .NET. Son code d’accès aux données ne sera pas exécuté tant et aussi longtemps qu’elle n’est pas appelée. L’autre chose, c’est que, par configuration, sa mise en mémoire a une date de péremption. Une fois ce délai atteint, un rafraîchissement des données sera à nouveau exécuté.
Le bonus est que le tout tire profit du mécanisme async de .NET. Dans bien des contextes, avec ASP.NET, son utilisation est utile. C’est surtout que cela permet de permettre de rendre l’appel à mon API qui prend du temps moins bloquant pour les autres requêtes en traitement.
Comment ça marche?
Un gist vaut mille mots.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class CarsCache | |
{ | |
private AsyncExpiringLazy<List<Car>> Cars { get; set; } | |
public CarService CarService { get; set; } | |
public CarsCache() | |
{ | |
Cars = new AsyncExpiringLazy<List<Car>>(async metadata => | |
{ | |
return await Task.Run(() => | |
{ | |
return new ExpirationMetadata<List<Car>> | |
{ | |
Result = CarService.GetAllCars(), | |
ValidUntil = DateTimeOffset.UtcNow.AddHours(12) | |
}; | |
}); | |
}); | |
} | |
public Task<List<Car>> GetAllCars() | |
{ | |
return Cars.Value(); | |
} | |
} | |
// Par la suite, l'appel se fait ainsi | |
var cache = new CarsCache(); | |
var cars = await cache.GetAllCars(); |
Évidemment, le premier accès sera plus lent. Par la suite, la magie de AsyncExpiringLazy fera en sorte que vous pourrez accéder à vos données tant et aussi longtemps que le délai d’expiration ne sera pas atteint. Dans mon cas, je me suis aussi assuré que la cache soit toujours « chaude » en ajoutant une tâche avec FluentScheduler qui exécute périodiquement CarsCache().GetAllCars().
La dernière chose que vous devrez réaliser est d’utiliser votre librairie d’injection de dépendances afin de faire persister CarsCache à travers la durée de vie de votre application. Par la suite, le tour est joué!