Un problème assez commun que l'on doit traiter avec Mulesoft est la conservation de requêtes. De plus en plus souvent, nous connectons nos serveurs Mulesoft à des services qui délivrent des informations au fil de l'eau, au travers de ce qui est trop vite nommé des "Webhooks". Nous reparlerons prochainement des avantages et limites de telles architectures. Contentons nous aujourd'hui de répondre à une question: si je ne peut traiter dans l'immédiat une sollicitation pour une raison ou une autre (indisponibilité d'un service par exemple), comment puis-je m'assurer que je ne vais pas la perdre et pourrai la resoumettre plus tard ?
En fait, avec Mulesoft, c'est assez facile à implémenter en utilisant deux mécanismes:
- les queues de messages,
- les transactions (XA si nécessaires)
L'idée est simple : lorsque la requête est reçue, elle est stockée dans une queue de messages. Un autre flux, sous transaction celui-là viendra consommer la requête transformée en message:
Voici la configuration du listener VM (pour la partie transaction):
Dans cet exemple, comme il n'est pas nécessaire d'effectuer de mises à jour distribuées, une transaction locale est suffisante. Mais votre cas peut nécessiter d'utiliser un mode de transaction plus puissant et passer en XA (2 phase commit).
Si le fait d'utiliser une queue de message pour conserver les requêtes est parfois identifié comme une solution de "replay" par les équipes que je côtoie, le mécanisme est plutôt vu comme un mécanisme de gestion d'erreur. Le principe adopté est:
- quand je reçois la requête, de façon synchrone, je tente de la traiter.
- si je rencontre un problème (partie gestion d'erreur) alors j'expédie la requête dans une queue de messages pour traitement ultérieur
- je mets en place un flux capable d'exploiter le contenu de cette queue de messages. Ce flux, globalement, suit le même principe qu'en 1.
C'est faire la même chose en plus compliqué (et donc en plus fragile). S'il n'y a pas de contre-indication pour passer systématiquement par un traitement asynchrone (ce qui est la plupart du temps le cas), simplifions : une requête entrante est systématiquement conservée dans la queue de message. On m'avance parfois un potentiel gain de performance : il est toujours pratiquement nul. Résumons: faites simple.
Second point: quelle système de queue de messages utiliser ? Réponse : quelque chose qui soit performant et fiable. C'est à dire, entre autres, qui puisse s'inscrire dans une transaction. Personnellement, j'utilise la plupart du temps VM. Dans le cas de CloubHub 1.0, ou onPremise, c'est parfaitement justifié. Pour CloudHub 2.0, avec l'abandon d'une "vraie" persistance pour VM et Object Store, c'est plus discutable. L'adoption de Anypoint MQ ne me parait pas pertinent : on ne peut l'inclure dans une transaction ce qui complexifie son utilisation dans notre cas. Reste l'utilisation d'un produit du marché, répondant à la norme Java JMS (comme RabbitMQ, Tibco, ...) : il faut acheter, installer, configurer. Bref, je dirai, par ordre de pertinence:
- Si on utilise CloudHub 1.0 ou OnPremise "Bare Metal" : VM
- Si on utilise CloudHub 2.0 (ou RuntimeFabric) et que la perte de messages n'est pas une catastrophe irréparable: VM en faisant bien attention à déclarer au minimum 2 instances (ce qui est une très saine précaution, bien au delà de l'utilisation de VM)
- Si le perte d'un message est potentiellement une catastrophe absolue: JSM.
- Si on aime les challenges techniques (et qu'on dispose par ailleurs du produit): Anypoint MQ avec l'implémentation d'un solide pattern de recouvrement (hors du propos de cet article).
Le dernier sujet est la prise en compte de l'impossibilité de traiter la requête, c'est à dire qu'en dépit de tentatives répétées, on ne peut réussir. Cela peut être du:
- au fait qu'une application ou une ressource reste indisponible (j'ai vu des instances SAP être off pendant des jours...)
- au fait que le message entrant est erroné et n'est pas traitable en l'état.
Dans les deux cas, la solution consiste à rediriger le message vers une queue d'erreur généralement appelée Dead-End-Queue (DEQ) ou tout type de conservation équivalent.
VM permet de faire çà très facilement: il suffit d'indiquer le nombre maximum de tentatives que l'on souhaite. L'exception émise est MULE:REDELIVERY_EXHAUSTED :
Pousser un message vers un DEQ ou un système persistent est une chose. Etre en capacité d'exploiter cette DEQ en est une autre. Je vois trop souvent, des équipes implémenter une DEQ sans se préoccuper de la manière dont on l'utilisera. Cela devient effectivement une impasse ! C'est un vrai sujet en soi. Nous en reparlerons dans un prochain article.
____________________________________________________________________
A fairly common problem we have to deal with with Mulesoft is request retention. More and more often, we're connecting our Mulesoft servers to services that deliver information on the fly, through what are all too quickly dubbed "Webhooks". We'll come back to the advantages and limitations of such architectures shortly. For now, let's just answer one question: if I can't immediately process a request for one reason or another (unavailability of a service, for example), how can I be sure I won't lose it and be able to resubmit it later?
In fact, with Mulesoft, this is quite easy to implement, using two mechanisms:
- message queues,
- transactions (XA if necessary)
The idea is simple: when a request is received, it is stored in a message queue. Another flow, this one under a transaction, will consume the request transformed into a message:
Here's the VM listener configuration (for the transaction part):
In this example, as no distributed updates are required, a local transaction is sufficient. But your case may require you to use a more powerful transaction mode and switch to XA (2 phase commit).
While using a message queue to hold requests is sometimes identified as a "replay" solution by the teams I work with, the mechanism is seen more as an error management mechanism. The principle adopted is:
- when I receive the request, synchronously, I try to process it.
- if I encounter a problem (error handling part), I send the request to a message queue for further processing
- I set up a flow capable of exploiting the contents of this message queue. Overall, this flow follows the same principle as in 1.
It's doing the same thing, only more complicated (and therefore more fragile). If there's no reason to systematically use asynchronous processing (which is most of the time the case), let's simplify: an incoming request is systematically stored in the message queue. I'm sometimes told of a potential performance gain: it's always practically nil. To sum up: keep it simple.
Second point: which message queuing system to use? Answer: something that's efficient and reliable. That is, among other things, something that can be part of a transaction. Personally, I use VM most of the time. In the case of CloubHub 1.0, or onPremise, this is perfectly justified. For CloudHub 2.0, with the abandonment of "true" persistence for VM and Object Store, it's more questionable. Adopting Anypoint MQ doesn't seem relevant to me: you can't include it in a transaction, which complicates its use in our case. That leaves the use of an off-the-shelf product that complies with the Java JMS standard (such as RabbitMQ, Tibco, etc.): you have to buy, install and configure it. In short, in order of relevance:
- If using CloudHub 1.0 or OnPremise "Bare Metal": VM
- If using CloudHub 2.0 (or RuntimeFabric) and message loss is not an irreparable catastrophe: VM, taking care to declare at least 2 instances (which is a very healthy precaution, well beyond the use of VMs).
- If the loss of a message is potentially an absolute catastrophe: JSM.
- If you like technical challenges (and have the product at your disposal): Anypoint MQ with the implementation of a solid recovery pattern (outside the scope of this article).
The last topic is the consideration of the impossibility of processing the query, i.e. that despite repeated attempts, we can't succeed. This may be due to:
- the fact that an application or resource remains unavailable (I've seen SAP instances be off for days...)
- the incoming message is erroneous and cannot be processed as it stands.
In both cases, the solution is to redirect the message to an error queue, generally called Dead-End-Queue (DEQ), or any equivalent type of conservation.
VM makes this very easy: all you have to do is specify the maximum number of retries you want. The exception is MULE:REDELIVERY_EXHAUSTED :
Pushing a message to a persistent DEQ or system is one thing. Being able to exploit that DEQ is quite another. All too often, I see teams implementing a DEQ without thinking about how it will be used. This becomes a real dead end! It's a real subject in itself. We'll talk more about it in a future article.