Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Note
C'est le quatrième article d'une série sur la conception de personnalisation évolutive. Pour commencer au début, consultez La conception de personnalisation évolutive dans Microsoft Dataverse.
Cette section décrit les modèles de conception pour éviter ou réduire leurs implications. Chaque modèle de conception doit être pris en compte dans le contexte du problème métier résolu et peut être utile en tant qu’options d’examen.
N’évitez pas le verrouillage
Le verrouillage est un composant important de SQL Server et dataverse, et est essentiel à une opération saine et à la cohérence du système. Pour cette raison, il est important de comprendre ses implications sur la conception, en particulier à grande échelle.
Utilisation des transactions : indicateur nolock
L’une des fonctionnalités de la plateforme Dataverse qui est fortement utilisée par les vues est la possibilité de spécifier qu’une requête peut être effectuée avec un indicateur nolock, indiquant à la base de données qu’aucun verrou n’est nécessaire pour cette requête.
Les vues utilisent cette approche, car il n’existe aucun lien direct entre l’acte de lancement de la vue et les actions consécutives. Un certain nombre d’autres activités peuvent être exécutées par cet utilisateur ou d’autres entre-temps, et il n’est ni pratique, ni utile de verrouiller l’ensemble de la table de données affichée par la vue en attendant que l’utilisateur passe à autre chose.
Étant donné qu’une requête sur un jeu de données volumineux signifie qu’elle affecte potentiellement les autres personnes qui tentent d’interagir avec ces données, en étant en mesure de spécifier qu’aucun verrou n’est requis peut avoir un avantage significatif sur l’extensibilité du système.
Lors de l’exécution d’une requête de la plateforme via le Kit de développement logiciel (SDK), il peut être utile de spécifier que l’option nolock peut être utilisée. Il indique que vous reconnaissez que cette requête n’a pas besoin de prendre un verrou de lecture dans la base de données. Il est particulièrement utile pour les requêtes où :
- Il existe un large éventail de données
- Les ressources hautement contestées sont consultées
- La sérialisation n’est pas importante
Nolock ne doit pas être utilisé si une action ultérieure dépend d’aucune modification des résultats, par exemple dans l’exemple de verrouillage de numéro automatique précédemment.
Un exemple de scénario dans lequel il peut être utile est de déterminer si un e-mail est lié à un cas existant. Empêcher d’autres utilisateurs de créer de nouveaux cas pour s’assurer qu’il n’existe aucune possibilité de générer un cas pour lequel l’e-mail peut lier n’est pas un niveau de contrôle de cohérence avantageux.
Au lieu de cela, faire un effort raisonnable pour interroger des cas connexes et joindre l’e-mail à un cas existant ou en créer un nouveau, tout en permettant à d’autres cas d’être générés, est plus approprié. En particulier, étant donné qu’il n’y a pas de lien inhérent dans le minutage entre ces deux actions, l’e-mail pourrait être aussi facilement arrivé en quelques secondes plus tôt et aucun lien n’aurait été détecté.
La validité des indicateurs de non-blocage pour un scénario particulier repose en général sur l'évaluation de la probabilité et de l’impact des conflits potentiels ainsi que sur les implications commerciales de ne pas garantir la cohérence des actions entre une opération de récupération et les actions suivantes. Si aucun impact sur l’entreprise ne se produit pour éviter le verrouillage, l’utilisation de nolocks serait un choix d’optimisation précieux. S’il y a un impact potentiel sur l’entreprise, la conséquence de cela peut être évaluée par rapport aux performances et aux avantages de l’extensibilité de l’évitement du verrouillage.
Prise en compte de l’ordre des verrouillages
Une autre approche qui peut être utile pour réduire l’impact du blocage, et en particulier pour éviter l’interblocage, consiste à adopter une approche cohérente de l’ordre des verrous dans une implémentation.
Un exemple simple et courant consiste à mettre à jour ou à interagir avec des groupes d’utilisateurs. Si vous avez des demandes qui mettent à jour les utilisateurs associés (comme l’ajout de membres à des équipes ou la mise à jour de tous les participants dans une activité), ne pas spécifier d'ordre peut signifier que si deux activités simultanées tentent de mettre à jour les mêmes utilisateurs, vous pouvez vous retrouver avec le comportement suivant, ce qui entraîne un blocage mutuel :
- La transaction A tente de mettre à jour l’utilisateur X, puis l’utilisateur Y
- La transaction B tente de mettre à jour l’utilisateur Y, puis user X
Étant donné que les deux requêtes démarrent ensemble, la transaction A est en mesure d’obtenir un verrou sur l’utilisateur X, et la transaction B est en mesure d’obtenir un verrou sur l’utilisateur Y. Mais dès que chacun d’eux essaie d’obtenir un verrou sur l’autre, ils se bloquent, et il y a un interblocage.
Simplement en triant les ressources auxquelles vous accédez de manière cohérente, vous pouvez empêcher de nombreuses situations d’interblocage. Le mécanisme de classement n’est souvent pas important tant qu’il est cohérent et peut être effectué aussi efficacement que possible. Par exemple, l’ordre des utilisateurs par nom ou même par GUID peut au moins garantir un niveau de cohérence qui évite les blocages.
Dans un scénario utilisant cette approche, la transaction A obtiendrait l’utilisateur X, mais la transaction B essayait également d’obtenir l’utilisateur X plutôt que l’utilisateur Y en premier. Bien que cela signifie que transaction B bloque jusqu’à la fin de la transaction A, ce scénario évite l’interblocage et est terminé avec succès.
Dans des scénarios plus complexes et efficaces, il se peut que vous verrouillez les utilisateurs les moins couramment référencés en premier et plus fréquemment référencés en dernier, ce qui conduit au modèle de conception suivant.
Maintenez les verrous controversés pendant une période aussi courte que possible
Il existe des scénarios, comme l’approche de numérotation automatique, où il n’existe aucun moyen de contourner le fait qu’il existe une ressource fortement contestée qui doit être verrouillée. Dans ce cas, le problème de blocage ne peut pas être évité, mais peut être réduit.
Lorsque les ressources sont fortement disputées, une bonne conception consiste à ne pas intégrer l'interaction avec cette ressource au moment logiquement fonctionnel du processus, mais de déplacer cette interaction aussi près que possible de la fin de la transaction.
Avec cette approche, bien qu’il y ait encore un blocage sur cette ressource, elle réduit le temps de verrouillage de cette ressource et réduit donc la probabilité et le temps dans lequel d’autres requêtes sont bloquées pendant l’attente de la ressource.
Réduire la longueur des transactions
De la même façon, un verrou devient un problème de blocage uniquement si deux processus ont besoin d’accéder à la même ressource en même temps. Plus la transaction qui contient un verrou est courte, moins il est probable que deux processus, même s’ils accèdent à la même ressource, en ont besoin exactement en même temps et provoquent une collision. Moins les transactions sont conservées longtemps, moins il est probable que le blocage devienne un problème.
Dans l'exemple suivant, les mêmes verrous sont pris, mais d'autres traitements dans la transaction entraînent une prolongation de la durée globale de celle-ci, ce qui conduit à des demandes qui se chevauchent pour les mêmes ressources. Cela signifie que le blocage se produit et que chaque requête est plus lente dans l’ensemble.
En raccourcissant la durée globale de la transaction, la première transaction se termine et libère ses verrous avant même que la deuxième requête ne commence, ce qui implique qu’aucun blocage ne se produit et que les deux transactions se terminent efficacement.
D’autres activités au sein d’une demande qui prolongent la durée de vie d’une transaction peuvent augmenter le risque de blocage, en particulier lorsqu’il existe plusieurs demandes qui se chevauchent et peuvent entraîner un système plus lent.
Il existe de nombreuses façons de réduire la longueur de la transaction.
Optimiser les demandes
Chaque transaction est constituée d’une série de demandes de base de données. Si chaque demande est effectuée aussi efficacement que possible, cela réduit la durée globale d’une transaction et réduit la probabilité d’une collision.
Passez en revue chaque requête que vous effectuez pour déterminer si :
Votre requête demande uniquement ce dont elle a besoin, par exemple, des colonnes, des enregistrements ou des types d’entités.
- Cela optimise le risque qu’un index puisse être utilisé pour traiter efficacement la requête
- Elle réduit le nombre de tables et de ressources qui doivent être accessibles, ce qui réduit la surcharge sur d’autres ressources du serveur de base de données et réduit le temps de requête
- Cela permet d’éviter un blocage potentiel sur les ressources dont vous n’avez pas besoin, en particulier si une jonction à une autre table est demandée mais pourrait être évitée ou est inutile
Un index est en place pour aider la requête, vous interrogez de manière efficace, et une recherche d’index plutôt qu’une analyse a lieu.
Il est important de noter que l’introduction d’un index n’évite pas le verrouillage lors de la création/mise à jour d’enregistrements dans la table sous-jacente. Les entrées des index sont également verrouillées lorsque l’enregistrement associé est mis à jour, car l’index lui-même est susceptible de changer. L’existence d’index n’évite pas complètement ce problème.
Dans l’exemple suivant, la récupération des cas connexes n’est pas optimisée et ajoute à la longueur globale de la transaction, en introduisant le blocage entre les threads.
En optimisant la requête, il y a moins de temps passé à l’exécuter, et le risque de collision est inférieur, réduisant ainsi le blocage.
Assurez-vous que le serveur de base de données peut traiter votre requête aussi efficacement que possible peut réduire considérablement le temps global de vos transactions et réduire le risque de blocage.
Réduire la chaîne d’événements
Comme indiqué dans les exemples précédents, les conséquences de longues chaînes d’événements connexes peuvent avoir un impact matériel sur le temps de transaction global et, par conséquent, le risque de blocage survient. Cela est particulièrement le cas lors du déclenchement de plug-ins et de flux de travail synchrones, qui déclenchent ensuite d’autres actions, et ils déclenchent à leur tour d’autres plug-ins et workflows synchrones.
L’examen et la conception d’une implémentation soigneusement afin d’éviter les longues chaînes d’événements qui se produisent de manière synchrone peuvent être utiles pour réduire la longueur globale d’une transaction. Cela permet aux verrous qui sont pris d’être libérés plus rapidement et de réduire le risque de blocage.
Elle réduit également la probabilité que les verrous secondaires deviennent une préoccupation majeure. Dans l’exemple de numérotation automatique lors de la création du compte, le problème principal est d’abord l’accès à la table de numérotation automatique, mais lorsque de nombreuses actions différentes sont effectuées dans une séquence, un blocage secondaire, tel que les mises à jour d’un enregistrement utilisateur associé, peut également commencer à apparaître. Une fois que plusieurs ressources contestées sont impliquées, l’évitement du blocage devient encore plus difficile.
Tenir compte du fait que certaines activités doivent être synchrones ou asynchrones peut signifier que les mêmes activités sont exécutées mais ont un impact initial moindre. En particulier pour les actions plus longues ou celles qui dépendent de ressources fortement contestées, les séparant de la transaction principale en les effectuant dans une action asynchrone peuvent avoir des avantages significatifs. Cette approche ne fonctionne pas si l’action doit s’exécuter ou échouer avec l’étape de plateforme plus large, par exemple comme la mise à jour d’un rapport de police avec la prochaine valeur de numéro automatique garantissant un schéma de numéros séquentiel et continu est maintenue. Dans ces scénarios, d’autres approches visant à réduire l’impact doivent être prises.
Comme l’illustre l’exemple suivant, il suffit de déplacer certaines actions vers un processus asynchrone, ce qui signifie que les actions sont effectuées en dehors de la transaction de plateforme, peuvent signifier que la longueur de la transaction est plus courte et que le risque de traitement simultané augmente.
Éviter plusieurs mises à jour du même enregistrement
Lors de la conception de plusieurs couches d’activité fonctionnelle, alors qu’il est recommandé de décomposer les actions nécessaires aux flux logiques et facilement suivis de l’activité, dans de nombreux cas, cela entraînerait plusieurs mises à jour distinctes du même enregistrement.
Dans le scénario de gestion de cas, il est parfaitement logique de commencer par mettre à jour un cas en attribuant un propriétaire par défaut, en fonction du client contre lequel il est soulevé. Par la suite, il est judicieux de mettre en place un processus distinct pour envoyer des communications de manière automatique à ce client et mettre à jour la date de dernier contact liée au cas.
Toutefois, le défi est que cela signifie qu’il existe plusieurs demandes adressées à Dataverse pour mettre à jour le même enregistrement, ce qui a de nombreuses implications :
- Chaque requête est une mise à jour de plateforme distincte, en ajoutant une charge globale au serveur Dataverse et en ajoutant du temps à la longueur globale de la transaction, ce qui augmente la probabilité de blocage.
- Cela signifie que l’enregistrement d’incident est verrouillée par la première action effectuée sur cet incident, autrement dit que le verrouillage est maintenu tout au long de la transaction. Si le cas est accessible par plusieurs processus parallèles, cela peut entraîner le blocage d’autres activités.
La consolidation des mises à jour du même enregistrement à une seule étape de mise à jour, et ultérieurement dans la transaction, peut avoir un avantage significatif pour l’extensibilité globale, en particulier si l’enregistrement est fortement contesté ou accessible par plusieurs personnes rapidement après la création, par exemple, comme avec un cas.
Décider s’il faut consolider les mises à jour du même enregistrement sur un seul processus consisterait à équilibrer la complexité de l’implémentation par rapport au risque de conflits susceptibles d’introduire des mises à jour distinctes. Mais pour les systèmes à volume élevé, cela peut être bénéfique pour les ressources hautement contestées.
Mettre à jour uniquement les éléments dont vous avez besoin
Bien qu’il soit important de ne pas réduire l’avantage d’un système Dataverse en excluant les activités qui seraient bénéfiques, les demandes sont souvent faites pour inclure des personnalisations qui ajoutent peu de valeur métier, mais qui entraînent une complexité technique réelle.
Si chaque fois que nous créons une tâche, nous mettons également à jour le fichier utilisateur avec le nombre de tâches qui leur sont actuellement allouées, ce qui pourrait introduire un niveau secondaire de blocage, car le fichier utilisateur serait également fortement concurrencé. Il ajouterait une autre ressource que chaque requête peut avoir besoin de bloquer et d’attendre, même si elle n’est pas nécessairement critique pour l’action. Dans cet exemple, déterminez soigneusement si le stockage du nombre de tâches par rapport à l’utilisateur est important ou si le nombre peut être calculé à la demande ou stocké ailleurs, comme l’utilisation des fonctionnalités de champ de hiérarchie et de cumul dans Dataverse en mode natif.
Comme indiqué ultérieurement, la mise à jour des enregistrements utilisateur système peut avoir des conséquences négatives du point de vue de l’extensibilité.
Plusieurs personnalisations déclenchées sur le même événement
Le déclenchement de plusieurs actions lors du même événement peut augmenter la probabilité de collision, car ces actions sont susceptibles d'interagir avec les mêmes objets associés ou leur objet parent, en raison de la nature des requêtes.
Il s’agit d’un modèle qui doit être soigneusement pris en compte ou évité, car il est facile d’ignorer les conflits, en particulier lorsque différentes personnes implémentent les différents processus.
Quand utiliser différents types de personnalisation
Chaque type de personnalisation a des implications différentes pour l'utilisation. Le tableau suivant met en évidence certains modèles courants, quand chacun doit être pris en compte et utilisé, et quand il n’est pas approprié pour une utilisation.
Souvent, une compromission entre différents comportements peut être nécessaire pour que cela donne des conseils sur certaines des caractéristiques et scénarios communs à prendre en compte, mais chaque scénario doit être évalué et l’approche appropriée choisie en fonction de tous les facteurs pertinents.
| Pré/post-phase | Synchronisation/asynchrone | Type de personnalisation | Quand utiliser | Quand ne pas utiliser |
|---|---|---|---|---|
| Pré validation | Synchronisation | Plug-in | Validation à court terme des valeurs d’entrée | Actions de longue durée. Lors de la création d'éléments connexes devant être annulés si des étapes ultérieures échouent. |
| Opération préalable | Synchronisation | Flux de travail/Plug-in | Validation à court terme des valeurs d’entrée. Lors de la création d'éléments associés qui doivent être annulés en cas d'échec d'une étape de la plateforme. |
Actions de longue durée. Lorsque vous créez un élément et que le GUID résultant doit être stocké par rapport à cet élément, l'étape de la plateforme créera ou mettra à jour l'élément. |
| Après opération | Synchronisation | Flux de travail/ Module d'extension | Les actions de courte durée qui suivent naturellement l’étape de la plateforme et doivent être annulées si les étapes ultérieures échouent, par exemple la création d’une tâche pour le propriétaire d’un compte nouvellement créé. Création d’éléments associés qui ont besoin du GUID de l’élément créé et qui doivent restaurer l’étape de plateforme en cas d’échec |
Actions à longue durée d'exécution Lorsqu'un échec ne doit pas compromettre l'achèvement d'une étape du pipeline de la plateforme. |
| Pas dans le pipeline d’événements | Async | Flux de travail/ Plug-in | Actions de longueur moyenne qui ont un impact sur l’expérience utilisateur. Des actions qui ne peuvent pas être annulées en cas de défaillance. Les actions qui ne doivent pas forcer l'annulation de l'étape spécifique de la plateforme en cas d'échec. |
Actions très longues. Ceux-ci ne doivent pas être gérés dans Dataverse. Actions à faible coût. La surcharge liée à la génération d’un comportement asynchrone pour les actions à faible coût peut être prohibitive ; si possible, effectuez ces opérations de manière synchrone et évitez la surcharge du traitement asynchrone. |
| N/A Prend le contexte de là où il est appelé |
Actions personnalisées | Combinaisons d’actions lancées à partir d’une source externe, par exemple à partir d’une ressource web | Lorsqu’il est toujours déclenché en réponse à un événement de plateforme, utilisez plug-in/workflow dans ces cas. |
Les plug-ins/workflows ne sont pas des mécanismes de traitement par lots
Les actions longues ou de grande envergure ne sont pas destinées à être exécutées à partir de plug-ins ou de workflows. Dataverse n’est pas destiné à être une plateforme de calcul et en particulier n’est pas destiné au contrôleur pour générer des groupes volumineux de mises à jour non liées.
Si vous avez besoin de le faire, déchargez et exécutez-le à partir d’un service distinct, tel qu’un rôle de travail Azure.
Configuration de la sécurité
Une zone d’escalade courante est la scalabilité de la configuration de la sécurité. Il s’agit d’une opération coûteuse. Par conséquent, lorsqu’elles sont effectuées en volume, elles peuvent toujours provoquer des difficultés si elles ne sont pas comprises et soigneusement prises en compte.
Configuration de l’équipe
- Ajoutez toujours des utilisateurs dans le même ordre : évitez les blocages
- Mettre à jour uniquement les utilisateurs s’ils doivent être mis à jour : évitez d’invalider inutilement les caches des utilisateurs
Propriétaire v. Équipes d’accès
- Si les équipes des utilisateurs changent régulièrement, faites attention à l’utilisation excessive des équipes de propriétaires ; chaque fois qu’elles changent, elles invalident le cache de l’utilisateur dans le serveur web
- Dans l’idéal, apportez des modifications lorsque l’utilisateur ne fonctionne pas, réduisez l’impact, par exemple la nuit
Nombreuses appartenances à une équipe/divisions
- Examinez soigneusement les scénarios comprenant un grand nombre d’ajouts d’équipes/divisions à la complexité de calcul
Comportement en cascade
- Envisagez de partager en cascade, par exemple, l’affectation
Mise à jour minutieuse des enregistrements utilisateur
- Ne mettez pas régulièrement à jour les enregistrements utilisateur système, sauf si quelque chose de fondamental a changé, car cela force le cache utilisateur à recharger et les privilèges de sécurité à recalculer, une activité coûteuse
- N’utilisez pas l’utilisateur système pour enregistrer le nombre d’activités ouvertes dont l’utilisateur dispose, par exemple
Actions liées au diagramme
Une activité qui est bénéfique en tant que mesure préventive, et un outil pour diagnostiquer les problèmes de blocage, consiste à diagrammer les actions associées déclenchées dans la plateforme Dataverse. Lorsque vous effectuez cette opération, il permet de mettre en évidence à la fois les dépendances intentionnelles et involontaires et les déclencheurs dans le système. Si vous n’êtes pas en mesure de le faire pour votre solution, vous n’avez peut-être pas une image claire de ce que fait réellement votre implémentation. La création d’un tel diagramme peut exposer des conséquences inattendues et est une bonne pratique à tout moment dans une implémentation.
L’exemple suivant montre comment les deux processus fonctionnent parfaitement ensemble, mais en maintenance continue, l’ajout d’une nouvelle étape pour créer une tâche peut créer une boucle inattendue. L’utilisation de cette technique de documentation peut mettre en évidence cela à l’étape de conception et éviter cela affectant le système.
Passer en revue les données de télémétrie et les traces
Vous pouvez mettre en place un environnement Application Insights pour recevoir la télémétrie sur les diagnostics et les performances capturés par la plateforme Dataverse. Découvrez comment analyser les données de télémétrie Dataverse avec Application Insights
Une fois que vous avez un environnement Application Insights, vous pouvez utiliser l’interface Microsoft.Xrm.Sdk.PluginTelemetry.ILogger dans votre code de plug-in pour écrire des données de télémétrie dans Application Insights. Découvrez comment écrire des données de télémétrie dans votre ressource Application Insights à l’aide d’ILogger
Lorsque certaines erreurs se produisent, l’utilisation des fichiers de trace du serveur pour comprendre où des problèmes connexes peuvent se produire dans la plateforme peut également être utile. Plus d’informations : Utiliser le suivi
Résumé
Le contenu de Scalable Customization Design in Dataverse et les articles suivants Transactions de base de données, Problèmes d’accès concurrentiel et cet article décrivent les concepts suivants avec des exemples et des stratégies qui vous aident à comprendre comment concevoir et implémenter des personnalisations évolutives pour Dataverse.
Voici quelques éléments clés à mémoriser :
Serrures/ Transactions
- Les verrous et les transactions sont essentiels à un système sain
- Mais lorsqu’il est utilisé de manière incorrecte, peut entraîner des problèmes
Contraintes de plateforme
- Les contraintes de plateforme sont souvent exposées sous la forme d’erreurs
- Mais rarement la contrainte est la cause du problème
- Ils sont là pour protéger la plateforme et autres activités afin qu'elles ne soient pas affectées.
Conception pour l’utilisation des transactions
- Si les implémentations sont conçues avec un comportement de transaction à l’esprit, cela peut entraîner une plus grande scalabilité et une amélioration des performances des utilisateurs