Passer au contenu principal

Le CAC est souvent sollicité pour examiner des problèmes liés aux files d'attente de messages VOS. Voici quelques cas intéressants, accompagnés de solutions et de recommandations que j'aimerais partager avec vous.

Problème 1 : Récemment, un client nous a fait part d'un problème. Un demandeur n'arrivait pas à ajouter un message à une file d'attente de messages et recevait le code d'erreur e$max_file_exceeded. Bizarrement, la file d'attente était vide, comme le montrait la commande list_messages.

Après examen de la file d'attente, il a été constaté que le nombre de blocs de disque utilisés pour cette file d'attente approchait la taille maximale d'un fichier non extensible.

nom : %s1#d01>AppData>queue.MQ

organisation des fichiers : fichier de file d'attente des messages
dernière utilisation : 11-08-16 14:45:13 edt
dernière modification : 11-08-16 14:45:13 edt
dernière sauvegarde : 10-06-14 21:34:18 edt
heure de création :             10-06-09 11:03:15 edt
fichier de transaction : oui
journal protégé : non
commutateur de sécurité : non
audit :                          non
extensions dynamiques : non
taille de l'extension : 1
dernier message : 51689380
blocs utilisés : 520201

Pourquoi cette file d'attente était-elle à la fois pleine et vide ?

À un moment donné dans le passé, les serveurs chargés de vider les messages de la file d'attente étaient hors ligne. Cela a entraîné un retard important dans le traitement des messages. Ces messages ont finalement été traités par les serveurs et supprimés de la file d'attente. Lorsque des messages sont supprimés d'une file d'attente, une clé est ajoutée à l'index _record_index de la file d'attente, et la valeur de la clé indique le nombre d'octets des messages supprimés.  Lorsqu'un nouveau message est ajouté à une file d'attente de messages, le système de fichiers tente de trouver un message précédemment supprimé dont la taille correspond exactement à celle du nouveau message. Si aucun message de cette taille n'est disponible, le nouveau message est écrit dans l'espace vierge à la fin de la file d'attente.

Dans ce cas, il n'y avait pas assez d'espace libre dans la file d'attente pour contenir le nouveau message, et il n'y avait pas de message supprimé préexistant de la taille appropriée.

La morale de cette histoire est qu'il est judicieux de limiter le nombre de longueurs de messages uniques dans une file d'attente donnée. Plutôt que d'attribuer à chaque message le nombre exact d'octets dont il a besoin, arrondissez la valeur à une taille standard. En utilisant cette technique, vous augmentez les chances qu'un nouveau message puisse réutiliser l'espace d'un message précédemment supprimé.

Problème 2 : Récemment, un autre problème est apparu concernant les performances des files d'attente de messages. Un client a déclaré que le temps nécessaire pour vider une file d'attente contenant plus de 400 000 messages était excessivement long.

Ils avaient récemment rencontré un problème avec leurs processus serveur, qui étaient incapables de traiter les messages dans une file d'attente en temps voulu. Heureusement, les demandeurs avaient continué à fonctionner, de sorte qu'aucune donnée n'avait été perdue. Une fois le problème serveur résolu, il a fallu plusieurs heures avant qu'ils rattrapent leur retard dans le traitement des demandes et puissent commencer à traiter les transactions récentes. Le client demandait pourquoi cela s'était produit et comment cela pouvait être évité ou accéléré à l'avenir.

Lorsqu'un message est supprimé dans une file d'attente de messages, une clé est ajoutée à l'index _record_index géré par le système, où la valeur de la clé correspond à la longueur du message. Si le message supprimé a la même longueur qu'un message précédemment supprimé, la position des données inutilisées est enregistrée en tant qu'entrée en double sur la clé contenant la taille de ce message à la fin de la liste des valeurs en double. Ainsi, s'il y a des centaines de milliers de messages supprimés, tous de même taille (ou si l'ensemble des longueurs des messages supprimés est petit), la liste des clés en double est très longue et le temps nécessaire pour supprimer un seul message augmente de manière linéaire.

À l'inverse, lorsqu'un message est ajouté à la file d'attente et qu'une clé _record_index pour la longueur du message existe, l'espace occupé par l'enregistrement supprimé le plus récent est réutilisé pour contenir les données du nouveau message. Cette valeur doit alors être supprimée de la valeur de clé contenant la longueur du message. Ainsi, le temps nécessaire pour ajouter un message augmente de manière linéaire : plus il y a de messages supprimés, plus il faut de temps pour ajouter un nouveau message.

La morale de cette histoire est que les données conservées par le système dans les files d'attente de messages ont une mémoire ; les files d'attente mémorisent l'emplacement et la taille de tous les messages précédents. Ces informations persistent même après que la file d'attente a été vidée. Évitez autant que possible que vos files d'attente de messages atteignent une taille trop importante (des dizaines ou des centaines de milliers de blocs disque). Sinon, vous constaterez que le coût de l'ajout et de la suppression de messages dans une file d'attente peut augmenter avec le temps.

Les solutions à ces deux situations sont identiques.

Solution A : Une file d'attente de messages peut être tronquée lorsqu'elle est ouverte. La routine s$truncate_queue peut être utilisée à cette fin. Cependant, quatre conditions doivent être remplies :

1 : aucun demandeur ne doit maintenir la file d'attente des messages ouverte.

2 : la file d'attente des messages doit être vidée de tous les messages

3 : cette routine doit être appelée par un serveur

4 : la file d'attente ne peut pas être un fichier de transaction

Si les trois premières conditions ne sont pas remplies, s$truncate_queue renvoie e$no_truncate_queue. Si la dernière condition n'est pas remplie, s$truncate_queue renvoie e$invalid_io_operation.

Solution B: si la conception de l'application permet d'avoir plusieurs serveurs, vous pouvez renommer périodiquement la file d'attente de messages existante, créer une nouvelle file d'attente de messages avec le nom correct, démarrer un nouvel ensemble de serveurs et rediriger les demandeurs. Lorsque les serveurs démarrent, ils commencent à traiter une nouvelle file d'attente de messages, mais vide. Lorsque les demandeurs démarrent, ils ajoutent leurs demandes dans la nouvelle file d'attente de messages vide. Le groupe de serveurs d'origine peut continuer à fonctionner et à traiter les demandes en attente jusqu'à ce que la file d'attente soit vide. Ensuite, l'ancien groupe de serveurs peut être arrêté et l'ancienne file d'attente de messages peut être supprimée.

De plus, une solution au problème 1 pourrait consister à utiliser une file d'attente de messages basée sur l'étendue. Cela permettrait de placer des messages supplémentaires dans la file d'attente, car la taille maximale du fichier serait plus grande d'un facteur égal à la taille de l'étendue. Cependant, en utilisant des files d'attente de messages basées sur l'étendue, les performances seront encore pires que la normale si la file d'attente contient un grand nombre de messages à un moment donné.

Comme mentionné précédemment, limiter le nombre de longueurs de messages uniques dans une file d'attente donnée améliorera la probabilité qu'un nouveau message puisse réutiliser l'espace libéré par un message précédemment supprimé. Cela permettra de résoudre les deux problèmes mentionnés dans cet article.