Faire la queue dans Mendix Cela peut être une bonne chose
Faire la queue est souvent considéré comme une perte de temps, que vous soyez dans une file d'attente pour le bus/tram/train pour rentrer chez vous ou pour aller chercher votre déjeuner, ou que vous attendiez en attente que quelqu'un du service client réponde à votre appel depuis la file d'attente téléphonique auentrez ici votre revendeur/fournisseur préféré>.
Dans une Mendix app, la plupart des actions sont monothread, donc par exemple, l'action que vous définissez commencera au début et se terminera à la fin et entre les deux suivra les opérations que vous spécifiez dans l'ordre que vous spécifiez. Cela permet de garder les choses simples et agréables, car vous n'avez pas à vous soucier trop de savoir si les opérations que vous définissez se produiront ou non comme prévu et si vous avez des dépendances dans votre action, vous pouvez voir clairement ce qu'elles sont.

D'un autre côté, les files d'attente peuvent être bénéfiques pour votre application, car elles peuvent être utilisées pour répartir l'exécution d'un processus sur plusieurs threads et nœuds. Si vous avez une tâche particulière à effectuer, les files d'attente peuvent vous aider en vous permettant d'effectuer différentes parties du travail simultanément, et globalement, le processus prend moins de temps. Alors, comment pouvez-vous y parvenir ?
Il s'agit du troisième d'une série d'articles de blog sur l'efficacité dans Mendix applications. Dans la première de la série (Santé et efficacité en Mendix), j'ai mis en évidence certaines des manières simples dont vous pourriez améliorer l'efficacité de votre low-code, et dans le deuxième (In Mendix Quelle est la longueur d'une chaîne ?), j'ai montré comment l'utilisation d'actions Java peut améliorer les performances dans les virages serrés. Cette fois, je veux illustrer comment Mendix Files d'attente de tâches peut être utilisé pour rendre votre application plus efficace.
Files d'attente de tâches
Mendix Files d'attente de tâches ont été introduits dans Mendix 9 en tant que remplacement moderne du module de marché Process Queue, et leurs fonctionnalités sont bien documentées. Dans cet article, je vais prendre un cas d'utilisation particulier et simple et montrer comment un File d'attente des tâches peuvent être utilisé pour réduire considérablement le temps écoulé nécessaire pour effectuer le processus requis.
Il existe une documentation détaillée sur le Page des files d'attente de tâches qui couvre ce que vous pouvez faire avec Files d'attente de tâches, et comment le faire, y compris les nouvelles fonctionnalités telles que la relance automatique des tâches ayant échoué et la planification des tâches pour qu'elles commencent à s'exécuter à une heure précise. Un point important à retenir est que vous devez faire attention à ne pas avoir de dépendances entre les « sous-tâches » de votre processus, à moins que vous ne gériez cela vous-même — l'exemple de cas d'utilisation présenté ici a des dépendances simples et j'ai conçu un moyen de contrôler cela.
Une grande suppression

Mon application est utilisée pour extraire des données à partir de sources de données externes définies lorsque l'utilisateur le demande. Ces données sont soumises à une analyse simple afin que l'utilisateur puisse prendre des décisions sur la manière dont les données doivent être utilisées. Au moment où l'utilisateur est satisfait et a terminé le travail en cours, les données doivent être supprimées.
Une application de test a été mise au point pour illustrer comment un File d'attente des tâches peut accélérer le processus de suppression : GitHub – Adrian-Preston/QueueingCanBeAGoodThing

Dans la configuration initiale, le modèle de domaine a été configuré pour la suppression automatique, de sorte que la suppression de l'objet source se répercutera automatiquement dans l'arborescence, supprimant tous les objets associés (mis en évidence par les cases d'association dans le modèle de domaine bordées de rouge). Il s'agit d'une option sûre, car elle empêchera les objets « orphelins » d'être laissés de côté et cela signifie également que le développeur peut simplement supprimer la source et tout le reste suivra. Cependant, comme il s'agit d'une opération à thread unique, cela peut prendre un certain temps s'il y a beaucoup de données dans l'arborescence.

Comme il s'agit d'un modèle de domaine relativement simple, il est assez facile de voir que nous pourrions supprimer en toute sécurité des objets pour certaines entités simultanément. Ainsi, ItemValue, ItemAttachment, ItemLink, AnalysedValue et AnalysedAttachment (Définir un) pour une source particulière peuvent être supprimés en toute sécurité en même temps. De même, Item et AnalysedItem (Définir deux) peuvent être supprimés simultanément, mais seulement après les enregistrements dans Définir un ont tous été supprimés. Enfin, Source, DocumentType et Document devront être supprimés dans le bon ordre après Définir un et Définir deux ont été supprimées. Ce sont les dépendances que j'ai mentionnées plus tôt.
Alors comment cela peut-il être réalisé ?
Sur l'interface utilisateur de l'application, il existe une page qui répertorie les sources actuellement chargées. À partir de là, l'utilisateur identifie la source à supprimer et appuie sur le bouton « Suppression intelligente de la source » sur cette ligne.

Cela appelle un nanoflow appelé « ACT_SmartDeletion », qui a deux tâches principales : premièrement, démarrer le processus de suppression en arrière-plan ; et deuxièmement, attendre que l'enregistrement source disparaisse de la base de données, indiquant que la tâche est terminée.


Le nanoflow appelle un microflow appelé « SUB_StartSmartDeletion », qui appelle un sous-microflow pour chaque type d'entité dans Définir un mais ceux-ci sont chacun appelés en les plaçant dans une file d'attente de tâches, ce qui signifie qu'ils ne sont pas exécutés directement, mais simplement mis en file d'attente afin qu'ils s'exécutent en arrière-plan. Nous créons également un objet DeletionControl spécial pour chacun des sous-microflux à recevoir (plus d'informations ci-dessous). Lorsque ce microflux est terminé, il revient au nanoflux.

Le nanoflow entre alors dans une boucle pour voir si l'enregistrement source est toujours présent dans la base de données, et pendant qu'il y est, le nanoflow s'arrête un court instant puis vérifie à nouveau. Lorsque l'enregistrement source n'est plus dans la base de données, le nanoflow informe l'utilisateur et termine.
Chacun des cinq sous-microflux est identique (à l'exception d'un sous-microflux avec du code supplémentaire). Le sous-microflux supprime tous les enregistrements de la source d'un type particulier d'entité, puis supprime finalement l'objet DeletionControl qui a été donné ci-dessus.


Le code supplémentaire dans 'SUB_DeleteItemValue' attend que tous les Définir un is terminé (en vérifiant que tous les objets DeletionControl ont été supprimés) et il démarre ensuite les sous-microflux dans le même File d'attente des tâches pour exécuter la suppression des entités dans Définir deux, donc quand Définir un est terminé Définir deux la suppression est automatiquement lancée en utilisant le même mécanisme.

De même, 'SUB_DeleteItem', lorsqu'il a supprimé tous les éléments, attend Définir deux à compléter et supprime enfin l'enregistrement source, complétant ainsi le processus. Comme les enregistrements DocumentType et Document sont peu nombreux, nous les supprimons simplement en utilisant le comportement « lors de la suppression » du modèle de domaine.

Comment se comparent-ils ?
L'application de test dispose également d'un bouton « Supprimer la source simple » sur la source, ce qui supprime directement la source et laisse le modèle de domaine pour garantir que les objets dépendants sont également supprimés. Nous pouvons donc exécuter la suppression simple ou intelligente pour un ensemble de données de test. De plus, l'application a la possibilité de créer un nouvel ensemble de données de test, d'exporter un ensemble de données de test et de réimporter un ensemble de données de test. De cette façon, l'application permettra de créer de nouveaux ensembles et pourra les exporter/importer afin que les options de suppression simple et intelligente puissent être utilisées à plusieurs reprises avec les mêmes données.
J'ai un ensemble de données de test inclus avec l'application dans le répertoire des ressources appelé « Source-36e63c07–9a8a-4c94–8f87–0fbf9b7dd39f », et c'est l'ensemble utilisé sur ma machine pour comparer les suppressions. Vous pouvez l'utiliser ou créer le vôtre.
J'ai exécuté l'application de test dans Mendix 9.18.0 avec configuration pour accéder à une base de données Postgres 10 locale. Avant de tester chaque type de suppression, j'ai démarré l'application à partir de zéro. J'ai ensuite importé l'ensemble de données de test et exécuté la suppression cinq fois. J'ai ignoré les meilleurs et les pires résultats des cinq et j'ai fait la moyenne des trois temps restants.
Alors, quel a été le résultat ? L'option de suppression simple a pris en moyenne 163.9 secondes. L'option Smart Delete a pris en moyenne 29.4 secondes — moins d'un cinquième du temps écoulé pris par la suppression simple. Maintenant, si l'utilisateur attend que la suppression soit terminée, cela semble être une économie intéressante.
Il existe d'autres moyens d'améliorer l'expérience utilisateur avec cette opération. Par exemple, vous pouvez marquer la source comme supprimée en plaçant un indicateur booléen sur l'enregistrement source, puis en ayant un processus d'événement périodique planifié distinct qui supprime les enregistrements source et leurs dépendants marqués comme tels. Il n'y a jamais qu'une seule solution à votre problème.
Il faut également comprendre que le fait d’avoir plusieurs threads travaillant dur pour un utilisateur peut avoir pour effet de ralentir l’application pour d’autres utilisateurs. Les besoins du cas d’utilisation précis et l’effet de la solution doivent donc être parfaitement compris et équilibrés.
N'oubliez pas que si vous avez un environnement de production qui est mis à l'échelle horizontalement, les tâches d'un File d'attente des tâches sera distribué sur les nœuds disponibles dans le cluster, ce qui peut vous faire gagner du temps supplémentaire (bien que le scénario présenté ici soit axé sur la base de données qui est toujours une ressource partagée).
Encore une chose
Dans l'état actuel des choses, nous avons un gain de temps considérable pour l'utilisateur qui, nous l'espérons, améliorera son expérience lors de l'utilisation de l'application. Mais une chose a été oubliée.
Dans le modèle de domaine, les associations entre Item, ItemValue, ItemAttachment, ItemLink, AnaysedItem, AnalysedValue et AnalysedAttachment ont toujours les options de suppression automatique configurées. Désormais, lorsque vous utilisez l'option Suppression intelligente, la suppression automatique de ces objets dans le modèle de domaine ne supprime rien en réalité, car les données cibles ont déjà été supprimées. Cependant, l'option Mendix L'exécution devra toujours vérifier s'il y a des enregistrements à supprimer et cela prend du temps.
Nous pouvons donc enfin supprimer les options de suppression automatique du modèle de domaine et réexécuter la suppression intelligente pour voir quel effet cela a.

Après avoir effectué ce changement, l'exécution de la suppression intelligente cinq fois comme auparavant a produit un temps écoulé moyen de 10.0 secondes par rapport à 29.4 secondes. Nous avons donc maintenant réduit le temps écoulé « normal » de 163 secondes écoulées jusqu'à 10 secondes. Cela me semble être une victoire, mais sachez que la suppression d'une source ne se répercutera plus sur les dépendances. Par conséquent, s'il existe d'autres endroits dans l'application où des suppressions doivent être appliquées à ces données, vous devrez également concevoir une solution pour cela.
En résumé
L'utilisation de Files d'attente de tâches peut améliorer considérablement les performances en matière de temps écoulé lorsqu'il est appliqué judicieusement à un cas d'utilisation approprié. Dans ce cas, les résultats du test montrent une économie significative pour l'utilisateur.

Votre kilométrage peut varier
Je n'ai probablement pas besoin de le dire, mais les avantages de l'utilisation de cette technique (pour tout type de processus, pas seulement les suppressions volumineuses) varieront considérablement en fonction de la complexité de l'opération entreprise et du modèle de domaine, de la quantité de ressources disponibles dans votre environnement et du degré de complexité que vous souhaitez pour votre modèle. Une fois de plus, je renvoie à mes commentaires dans mes blogs précédents sur la nécessité de garder votre code lisible et maintenable.
De plus, si deux utilisateurs ou plus suppriment des enregistrements sources en même temps, les ressources de la file d'attente seront partagées et les économies pour chacun peuvent être moindres.
J'ai utilisé une technique comme celle-ci dans un environnement de production réel (c'était avantMendix 9, il a donc utilisé le module ProcessQueue Marketplace) et a réalisé des gains de performances qui m'ont étonné et ravi le Product Owner. Assurez-vous donc de créer une branche et de l'essayer.
Bonne file d'attente !