Aller au contenu
BackJavaquarkusResilience

La taverne sous contrôle : maîtriser les flux avec Quarkus

Quand les aventuriers affluent et que les tournées s’enchaînent, même la meilleure des tavernes peut vaciller. Avec Quarkus et SmallRye Fault Tolerance, découvrez comment imposer discipline, absorber les échecs et maintenir un service digne, même sous pression.

Quarkus

Dans toute bonne taverne médiévale, il y a un moment où l’ordre vacille. Les aventuriers affluent, les chopes se tendent en rafale, la cave devient un lieu de chaos, et le pauvre tavernier tente de suivre la cadence sans renverser son service.

Dans une application moderne, le scénario est étrangement similaire. Un service exposé sans garde-fous finit vite saturé, instable, ou carrément inutilisable.

C’est précisément là que Quarkus, avec l’extension SmallRye Fault Tolerance, permet de remettre un peu de discipline dans le tumulte… sans renier l’esprit de la maison.

L’extension qui donne vie aux vieilles lois de la taverne

Avant même que le comptoir ne s’anime, que les aventuriers ne poussent la porte et que la cave ne devienne un défi quotidien, encore faut-il poser les fondations qui permettent à ces règles anciennes de s’exprimer. Dans le monde de Quarkus, ces mécanismes de discipline — limitation, tentatives répétées, solutions de secours — ne sont pas magiques par nature : ils prennent vie grâce à une extension dédiée, ajoutée au projet comme on ferait entrer une vieille guilde spécialisée dans la gestion des imprévus au service de la taverne.

<dependency>  
    <groupId>io.quarkus</groupId>  
    <artifactId>quarkus-smallrye-fault-tolerance</artifactId>  
</dependency>

la dépendance à ajouter

Cette simple ligne ne raconte pas encore l’histoire, mais elle ouvre la porte. Elle introduit dans la taverne un ensemble de règles éprouvées, héritées des pratiques de résilience les plus anciennes : celles qui permettent de limiter les excès au comptoir, de retenter une descente en cave lorsqu’elle échoue, ou encore de proposer un repas de substitution lorsque la cuisine ne peut plus suivre.

Sans elle, le tavernier ferait face seul aux excès des aventuriers. Avec elle, il s’appuie sur une tradition structurée, presque invisible pour les clients, mais essentielle au bon déroulement du service.

Une taverne exposée aux excès

Imaginons une taverne numérique exposée via une API REST :

@Path("/taverne")  
public class TavernResource {

Trois services structurent la vie du comptoir :

  • commander une bière
  • descendre à la cave chercher du vin
  • réclamer le plat du jour
@POST  
@Path("/commande")  
@Produces(MediaType.TEXT_PLAIN)  
public Response orderBeer() {  
    return Response.ok(tavernService.orderBeer()).build();  
}
@GET  
@Path("/cave")  
@Produces(MediaType.TEXT_PLAIN)  
public Response fetchFromCellar(@QueryParam("reset") boolean reset) {  
    return Response.ok(tavernService.fetchFromCellar(reset)).build();  
}
@GET  
@Path("/plat-du-jour")  
@Produces(MediaType.TEXT_PLAIN)  
public Response orderPlatDuJour(@QueryParam("empty") boolean empty) {  
    return Response.ok(tavernService.orderPlatDuJour(empty)).build();  
}

À ce stade, la taverne est ouverte. Tout fonctionne. Trop bien, même.

Et comme souvent dans les récits anciens : le succès attire le chaos.

Premier garde-fou : limiter l’ivresse des commandes

Au début, la taverne semble pouvoir encaisser sans effort l’afflux des voyageurs, mais très vite le comptoir se transforme en point de passage obligé pour tout ce que le monde compte de buveurs pressés et bruyants : les compagnons d’armes encore couverts de poussière, les nains venus en groupe dont la simple présence fait vibrer le bois des tables, et les barbares qui enchaînent les chopes comme s’il s’agissait d’un entraînement avant bataille ; face à cette montée continue, où chaque commande en appelle dix autres et où le rythme dépasse largement les capacités d’un seul tavernier, il devient nécessaire d’imposer une règle ancienne et pragmatique héritée des comptoirs les plus sages : contrôler le flux avant que la taverne ne se transforme en chaos permanent.

@RateLimit(value = 3, window = 10, windowUnit = ChronoUnit.SECONDS)  
public String orderBeer() {  
    return "Tiens, une bonne pinte bien fraîche !";  
}

si y'a trop de commande, le barman ne suit plus

Trois commandes toutes les dix secondes, pas davantage : une règle simple, presque sévère, mais nécessaire pour éviter que le comptoir ne soit submergé par l’enthousiasme des clients les plus pressés ; et lorsque cette limite est franchie, que les chopes se tendent encore alors que le rythme n’a pas été respecté, la vieille tradition des tavernes reprend aussitôt le dessus, et le tavernier, fidèle à son caractère bien trempé, se fait entendre sans détour, rappelant à chacun que même dans les lieux les plus accueillants, la mesure reste une loi que nul ne peut ignorer.

@Provider  
public class RateLimitExceptionMapper implements ExceptionMapper<RateLimitException> {  
  
    @Override  
    public Response toResponse(RateLimitException exception) {  
        return Response.status(429)  
                .entity("Holà l'ami ! Laisse-moi le temps de tirer ta bière, le tonneau n'est pas infini !")  
                .build();  
    }  
}

on gère l'erreur

Ainsi, au lieu d’un simple message technique sans âme, le comptoir reprend la parole comme dans les tavernes d’autrefois, avec cette franchise un peu rude mais jamais dénuée de personnalité, où l’on ne se contente pas de refuser une demande : on la rappelle à l’ordre, on la replace dans le rythme du lieu, et on fait comprendre, avec une certaine autorité teintée d’habitude, que même les plus impatients doivent respecter le tempo du service.

La cave : quand même les anciens trébuchent

Descendre à la cave n’a jamais rien d’une simple formalité dans une taverne digne de ce nom, car entre les marches irrégulières, l’humidité du bois et l’obscurité qui avale la lumière des torches, même le plus expérimenté des taverniers peut perdre l’équilibre et manquer sa première tentative (avoir eu un 1 sur son lancer de dé 20).

échec critique

Il lui faut alors recommencer, reprendre son souffle, s’accrocher à la rambarde comme on s’accroche à une vieille habitude, avant de finalement parvenir jusqu’aux réserves, là où patientent les bouteilles comme si elles attendaient depuis toujours leur heure de retour à la lumière.

@Retry(maxRetries = 3, delay = 200)  
public String fetchFromCellar(boolean reset) {

Le scénario est simple :

  • les premières tentatives échouent volontairement
  • la cave résiste
  • puis finalement, le vin est remonté
int attempt = cellarTrips.incrementAndGet();  
if (attempt <= 2) {  
    LOG.warn("Oups ! Le tavernier a glissé sur une marche vers la cave ! (Tentative " + attempt + ")");  
    throw new RuntimeException("Tavernier tombé dans les escaliers");  
}  
  
LOG.info("Victoire ! La bouteille est remontée (Tentative " + attempt + ")");  
return "Voilà votre prestigieux Vin Elfique millésimé !";

Grâce au mécanisme de retry, Quarkus rejoue automatiquement la scène comme une vieille histoire que l’on raconte plusieurs fois jusqu’à ce qu’elle finisse enfin par se dérouler correctement, laissant au système le temps de retenter sa chance sans intervention humaine ; et parfois, il suffit d’un simple geste, presque anodin dans le tumulte de la taverne, pour remettre les compteurs à zéro et permettre au tavernier de repartir sereinement à l’assaut des marches de la cave.

if (reset) {  
    cellarTrips.set(0);  
    return "Cave réinitialisée";  
}
extrait des logs

Quand la cuisine est vide : prévoir l’imprévu

Il existe une vérité ancienne, connue de tout tenancier de taverne digne de ce nom : tôt ou tard, quel que soit l’art du cuisinier ou la générosité des portions, le plat du jour finit toujours par s’épuiser, victime du succès autant que de la gourmandise des aventuriers qui ne laissent jamais une marmite intacte bien longtemps.

@Fallback(fallbackMethod = "serveLeftovers")  
public String orderPlatDuJour(boolean empty) {  
    if (empty) {  
        throw new RuntimeException("Plus aucun ragoût de sanglier !");  
    }  
    return "Voici un délicieux ragoût de sanglier !";  
}

Lorsque la cuisine ne parvient plus à suivre le rythme des commandes et que la marmite rend les armes, il ne s’agit jamais de renvoyer les clients vers une réponse sèche ou décourageante, mais bien de préserver l’honneur du service en proposant une alternative plus humble, certes, mais présentée avec la même dignité que les plats les plus généreux, afin que nul ne quitte la table avec le sentiment d’avoir été oublié par la taverne.

public String serveLeftovers(boolean empty) {  
    return "Désolé l'ami, la marmite est vide... Tiens, un morceau de pain dur et du fromage sec en lot de consolation.";  
}

C’est là tout l’esprit du fallback : lorsque les conditions ne permettent plus de servir le plat attendu, la taverne ne s’effondre pas dans le silence ni dans l’erreur, mais conserve une continuité de service en proposant une alternative plus simple, permettant de maintenir l’accueil et la cohérence de l’expérience, même lorsque les ressources viennent à manquer.

Une taverne résiliente

En combinant ces mécanismes, la taverne gagne en stabilité :

  • le comptoir est protégé contre les excès
  • la cave tolère les échecs temporaires
  • la cuisine propose une alternative en cas de rupture

Ce n’est plus une simple auberge livrée au chaos des aventuriers, mais une organisation capable d’absorber les imprévus sans s’effondrer.

Conclusion

Dans les récits anciens, une bonne taverne ne se jugeait pas à son calme, mais à sa capacité à survivre aux soirées les plus agitées.

Avec Quarkus et SmallRye Fault Tolerance, cette idée prend une forme moderne : contrôler les flux, accepter l’échec temporaire, et toujours proposer une issue.

Ainsi, même lorsque les aventuriers se pressent au comptoir, que la cave résiste et que la cuisine se vide, la taverne continue de servir… avec constance et dignité.

Dernier