Infrastructure System.Transactions dans le monde .NET


Avez-vous vu une construction comme l' using (var scope = new TransactionScope(TransactionScopeOption.Required)) en C #? Cela signifie que le code exécuté dans le bloc using est dans la transaction et après avoir quitté ce bloc, les modifications seront validées ou annulées. Cela semble compréhensible jusqu'à ce que vous commenciez à creuser plus profondément. Et plus vous creusez profondément, plus cela devient «étrange et étrange». En tout cas, lorsque je me suis familiarisé avec la classe TransactionScope et, en général, avec les transactions .NET, tout un tas de questions se sont posées.

Qu'est-ce que la classe TransactionScope ? Dès que nous utilisons la construction using (var scope = new TransactionScope()) , tout dans notre programme devient-il immédiatement transactionnel? Que sont «Resource Manager» et «Transaction Manager»? Puis-je écrire mon propre gestionnaire de ressources et comment «se connecter» à l'instance TransactionScope créée? Qu'est-ce qu'une transaction distribuée et est-il vrai qu'une transaction distribuée dans SQL Server ou Oracle Database est identique à une transaction .NET distribuée?

Dans cette publication, j'ai essayé de collecter des informations qui aident à trouver des réponses à ces questions et à mieux comprendre les transactions dans le monde .NET.


Présentation


Que sont les transactions et quels problèmes résolvent-elles?


Les transactions en question ici sont des opérations qui transfèrent le système d'un état acceptable à un autre et sont garanties de ne pas laisser le système dans un état inacceptable même en cas de situations imprévues. Le type de conditions acceptables dans le cas général dépend du contexte. Nous considérerons ici une situation acceptable dans laquelle les données que nous traitons font partie intégrante. Il est entendu que les modifications qui composent la transaction sont engagées ensemble ou non engagées. De plus, les modifications apportées à une transaction peuvent être isolées des modifications apportées au système par une autre transaction. Les exigences de base pour les transactions sont désignées par l'acronyme ACID. Pour la première connaissance avec eux, un article sur Wikipedia convient.


Un exemple classique de transaction est le transfert d'argent entre deux comptes. Dans cette situation, retirer de l'argent du compte n ° 1 sans créditer sur le compte n ° 2 est inacceptable, de la même manière que déposer sur le compte n ° 2 sans retirer du compte n ° 1. En d'autres termes, nous voulons que les deux opérations soient à la fois des retraits et des crédits - effectuée immédiatement. Si l'un d'eux échoue, la deuxième opération ne doit pas être effectuée. Vous pouvez appeler ce principe «tout ou rien». De plus, il est souhaitable que les opérations soient effectuées de manière synchrone même en cas de pannes systémiques telles qu'une coupure de courant, c'est-à-dire que nous voyons le système dans un état acceptable dès qu'il devient disponible après restauration.

En termes mathématiques, nous pouvons dire qu'en ce qui concerne le système, il y a un invariant que nous aimerions certainement conserver. Par exemple, le montant sur les deux comptes: il est nécessaire qu'après la transaction (virement) le montant reste le même qu'avant. Soit dit en passant, dans l'exemple classique du transfert d'argent, la comptabilité apparaît également - un domaine où le concept de transaction est apparu naturellement.

Nous illustrons l'exemple du transfert d'argent entre deux comptes. La première image montre la situation où le transfert de 50 roubles du compte n ° 1 au compte n ° 2 s'est terminé avec succès. La couleur verte indique que le système est dans un état acceptable (les données sont complètes).


Imaginez maintenant que le transfert soit effectué en dehors de la transaction et après avoir retiré de l'argent du compte n ° 1, une défaillance s'est produite, en raison de laquelle l'argent retiré n'a pas été crédité sur le compte n ° 2. Le système sera dans un état inacceptable (couleur rouge).


Si une erreur s'est produite entre les opérations de retrait et de crédit, mais que le transfert a été effectué dans le cadre d'une transaction, l'opération de retrait sera annulée. Par conséquent, le système restera dans son état acceptable d'origine.


Je vais donner des exemples de situations tirées de l'expérience de notre entreprise dans lesquelles les transactions sont utiles: comptabilisation des marchandises (comptabilisation du nombre de marchandises de différents types qui se trouvent dans certains magasins et en cours de route), comptabilisation des ressources de stockage (comptabilisation du volume d'une pièce occupée par des marchandises d'un certain type, volume d'une gratuit pour le placement de marchandises, la quantité de marchandises que les employés et les systèmes de stockage automatisés peuvent déplacer par jour).

Les problèmes qui surviennent lorsque l'intégrité des données est violée sont évidents. Les informations fournies par le système ne deviennent pas seulement fausses - elles perdent contact avec la réalité et se transforment en bêtises.

Quelles transactions sont considérées ici


Les avantages procurés par les transactions sont connus. Donc, pour maintenir l'intégrité des données, avons-nous besoin d'une base de données relationnelle, car c'est là que les transactions sont effectuées? Pas vraiment. Il a été dit plus haut que le concept de transaction dépend du contexte, et maintenant nous allons brièvement examiner de quelles transactions nous pouvons parler lorsque nous discutons des systèmes d'information.

Pour commencer, nous séparons les concepts de transactions du domaine sujet (transactions commerciales) et de transactions système. Le second peut être mis en œuvre à différents endroits et de différentes manières.

Allons du plus haut niveau - le domaine. La personne intéressée peut déclarer qu'il existe des états acceptables et ne souhaite pas voir le système d'information en dehors de ces états. Nous ne proposerons pas d'exemples supplémentaires: le transfert d'argent entre les comptes convient ici. Nous précisons seulement qu'un transfert n'est pas nécessairement un transfert d'argent entre les comptes de règlement de deux clients bancaires. La tâche de comptabilité est tout aussi importante, lorsque les comptes doivent refléter les sources et l’objet des fonds de l’organisation, et que le transfert doit refléter le changement dans la répartition des fonds par ces sources et cet objectif. Il s'agit d'un exemple de transaction de domaine sujet .

Voyons maintenant les exemples les plus courants et les plus intéressants de la mise en œuvre de transactions système. Dans les transactions système, divers moyens techniques répondent aux exigences du domaine. Une solution éprouvée classique de ce type est une transaction SGBD relationnelle (premier exemple). Les systèmes de gestion de base de données modernes (à la fois relationnels et pas très ) fournissent un mécanisme de transaction qui vous permet soit d'enregistrer (valider) toutes les modifications apportées au cours de la période de travail spécifiée, soit de les annuler (annuler). Lors de l'utilisation d'un tel mécanisme, des opérations de retrait d'argent d'un compte et de crédit sur un autre compte qui composent la transaction du domaine concerné, les moyens SGBD seront combinés en une transaction système et seront exécutés ensemble ou ne seront pas exécutés du tout.

Bien entendu, l'utilisation d'un SGBD n'est pas nécessaire. En gros, vous pouvez généralement implémenter le mécanisme de transaction SGBD dans votre langage de programmation préféré et profiter de l'analogue instable et bogué des outils existants. Mais votre «vélo» peut être optimisé pour des situations spécifiques dans le domaine.

Il existe des options plus intéressantes. Les langages de programmation industrielle modernes (C # et Java en premier lieu) offrent des outils conçus spécifiquement pour organiser des transactions impliquant des sous-systèmes complètement différents, et pas seulement le SGBD. Dans cette publication, nous appellerons ces logiciels de transactions. Dans le cas de C #, il s'agit de transactions à partir de l'espace de noms System.Transactions (le deuxième exemple), et elles sont décrites ci-dessous.

Avant de passer aux transactions System.Transactions , on ne peut manquer de mentionner un autre phénomène intéressant. System.Transactions outils System.Transactions permettent au programmeur d'implémenter indépendamment la mémoire de transaction programmatique . Dans ce cas, les opérations de programme qui affectent l'état du système (dans le cas des langages de programmation impératifs classiques, il s'agit d'une opération d'affectation) sont incluses par défaut dans les transactions qui peuvent être validées et annulées de la même manière que les transactions SGBD. Avec cette approche, la nécessité d'utiliser des mécanismes de synchronisation (en C # - lock , en Java - synchronized ) est considérablement réduite. Un autre développement de cette idée est la mémoire transactionnelle logicielle, prise en charge au niveau de la plate-forme (troisième exemple). Un tel miracle devrait se trouver dans une langue dont l'élégance dépasse son applicabilité industrielle - Clojure. Et pour les langues ouvrières-paysannes, il existe des bibliothèques de plug-ins qui fournissent les fonctionnalités de la mémoire transactionnelle programmatique.

Les transactions système peuvent inclure plusieurs systèmes d'information, auquel cas elles sont distribuées. Distribué peut être à la fois des transactions SGBD et des logiciels; tout dépend de la fonctionnalité prise en charge par un outil de transaction particulier. Des transactions réparties plus détaillées sont décrites dans la section correspondante. Je vais donner une image pour faciliter la compréhension des sujets abordés.


Section TL; DR


Il existe des processus qui consistent en plusieurs opérations indivisibles (atomiques) appliquées au système, dans le cas général pas nécessairement informatives. Chaque opération indivisible peut laisser le système dans un état inacceptable lorsque l'intégrité des données est compromise. Par exemple, si un transfert d'argent entre deux comptes est représenté par deux opérations indivisibles de retrait du compte n ° 1 et de crédit sur le compte n ° 2, alors une seule de ces opérations violera l'intégrité des données. L'argent disparaît au milieu de nulle part, ou apparaît au milieu de nulle part. Une transaction combine des opérations indivisibles afin qu'elles soient exécutées toutes ensemble (bien sûr, séquentiellement, si nécessaire) ou non exécutées du tout. Nous pouvons parler des transactions de domaine et des transactions dans les systèmes techniques qui implémentent généralement les transactions de domaine.

Transactions basées sur System.Transactions


Qu'est ce que c'est


Dans le monde .NET, il existe un cadre logiciel conçu par les créateurs d'une plateforme de gestion des transactions. Du point de vue d'un programmeur transactionnel, ce cadre comprend les System.Transactions TransactionScope , TransactionScopeOption , TransactionScopeAsyncFlowOption et TransactionOptions System.Transactions espace de noms System.Transactions . Si nous parlons de .NET Standard, tout cela est disponible à partir de la version 2.0 .

Les transactions à partir de l'espace de noms System.Transactions sont basées sur la norme X / Open XA de The Open Group . Cette norme introduit de nombreux termes abordés ci-dessous et, plus important encore, décrit les transactions distribuées, qui sont également couvertes dans cette publication dans une section spéciale. L'implémentation de transactions logicielles sur d'autres plateformes, par exemple Java, est basée sur le même standard.

Un cas d'utilisation de transaction typique pour un programmeur C # est le suivant:

 using (var scope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required)) { // -  ,    . scope.Complete(); } 

À l'intérieur du bloc using trouve le code qui effectue le travail, dont les résultats doivent être validés ou annulés tous ensemble. Des exemples classiques d'un tel travail sont la lecture et l'écriture dans la base de données ou l'envoi et la réception de messages de la file d'attente. Lorsque le contrôle quitte le bloc using , la transaction sera validée. Si vous supprimez l'appel Complete , la transaction sera annulée. Assez simple.

Il s'avère que lors d'une annulation de transaction, toutes les opérations effectuées à l'intérieur d'un tel bloc using seront annulées? Et si j'ai assigné à une variable une valeur différente, alors cette variable restaurera l'ancienne valeur? Quand j'ai vu un design similaire pour la première fois, je le pensais. En fait, bien sûr, tous les changements ne seront pas annulés, mais seulement des changements très spéciaux . Si toutes les modifications étaient annulées, ce serait la mémoire transactionnelle logicielle décrite ci-dessus. Voyons maintenant quelles sont ces modifications spéciales qui peuvent participer aux transactions de programme basées sur System.Transactions .

Gestionnaires de ressources


Pour que quelque chose prenne en charge les transactions basées sur System.Transactions , il est nécessaire qu'il possède des informations qu'une transaction est actuellement en cours et qu'il est enregistré dans un registre des participants aux transactions. Vous pouvez obtenir des informations indiquant si le travail transactionnel est en cours en vérifiant la propriété statique Current de la classe System.Transactions.Transaction . La saisie du bloc using du type indiqué ci-dessus ne fait que définir cette propriété, si elle n'a pas été définie auparavant. Et pour vous inscrire en tant que participant à une transaction, vous pouvez utiliser des méthodes de type Transaction.Enlist Smth . De plus, vous devez implémenter l'interface requise par ces méthodes. Gestionnaire de ressources - c'est juste un «quelque chose» qui prend en charge l'interaction avec les transactions de System.Transactions (une définition plus spécifique est donnée ci-dessous).

Que sont les gestionnaires de ressources? Si nous travaillons à partir de C # avec un SGBD, par exemple, SQL Server ou Oracle Database, nous utilisons généralement les pilotes appropriés et ce sont les ressources de gestion. Dans le code, ils sont représentés par les types System.Data.SqlClient.SqlConnection et Oracle.ManagedDataAccess.Client.OracleConnection . Ils disent également que MSMQ prend en charge les transactions basées sur System.Transactions . Guidé par des connaissances et des exemples tirés d'Internet, vous pouvez créer votre propre gestionnaire de ressources. L'exemple le plus simple est donné dans la section suivante.

En plus des gestionnaires de ressources, nous devons également avoir un gestionnaire de transactions, qui surveillera la transaction et donnera des ordres aux gestionnaires de ressources en temps opportun. Selon les gestionnaires de ressources impliqués dans la transaction (leurs caractéristiques et leur emplacement), différents gestionnaires de transactions sont connectés au travail. Dans ce cas, la sélection de la version appropriée est automatique et ne nécessite pas l'intervention d'un programmeur.

Plus précisément, le gestionnaire de ressources est une instance d'une classe qui implémente l'interface spéciale System.Transactions.IEnlistmentNotification . L'instance de classe, selon les instructions du client, est enregistrée en tant que participant à la transaction à l'aide de la propriété statique System.Transactions.Transaction.Current . Par la suite, le gestionnaire de transactions appelle les méthodes de l'interface spécifiée si nécessaire.


Il est clair qu'au moment de l'exécution, l'ensemble des gestionnaires de ressources impliqués dans la transaction peut changer. Par exemple, après avoir entré le bloc using , nous pouvons d'abord faire quelque chose dans SQL Server, puis dans Oracle Database. En fonction de cet ensemble de gestionnaires de ressources, le gestionnaire de transactions utilisé est déterminé. Pour être plus précis, le protocole de transaction utilisé est déterminé par l'ensemble des gestionnaires de ressources, et le gestionnaire de transaction qui le prend en charge est déterminé sur la base du protocole. Nous examinerons les protocoles transactionnels plus tard lorsque nous parlerons de transactions distribuées. Le mécanisme de sélection automatique du gestionnaire de transactions approprié au moment de l'exécution lors du changement des gestionnaires de ressources impliqués dans la transaction est appelé Promotion des transactions.

Types de gestionnaires de ressources


Les gestionnaires de ressources peuvent être divisés en deux grands groupes: durables et variables.

Durable Resource Manager - un gestionnaire de ressources qui prend en charge une transaction même si le système d'information n'est pas disponible (par exemple, lorsque l'ordinateur redémarre). Volatile Resource Manager - Un gestionnaire de ressources qui ne prend pas en charge une transaction si le système d'information n'est pas disponible. Un gestionnaire de ressources incohérent prend uniquement en charge les transactions en mémoire.

Les gestionnaires de ressources classiques à long terme sont le SGBD (ou le pilote SGBD pour la plate-forme logicielle). Quoi qu'il arrive - au moins un dysfonctionnement du système d'exploitation, au moins une panne de courant - le SGBD garantira l'intégrité des données après son retour en état de fonctionnement. Pour cela, bien sûr, vous devez payer quelques inconvénients, mais dans cet article, nous ne les considérerons pas. Un exemple de gestionnaire de ressources non persistant est la mémoire transactionnelle logicielle mentionnée ci-dessus.

Utilisation de TransactionScope


Lors de la création d'un objet de type TransactionScope vous pouvez spécifier certains paramètres.

Tout d'abord, il existe un paramètre qui indique au runtime ce dont il a besoin:

  1. Utilisez une transaction qui existe déjà à ce moment;
  2. Assurez-vous d'en créer un nouveau;
  3. à l'inverse, exécutez du code à l'intérieur d'un bloc using dehors d'une transaction.

L'énumération System.Transactions.TransactionScopeOption est responsable de tout cela.

Deuxièmement, vous pouvez définir le niveau d'isolement des transactions. C'est un paramètre qui vous permet de trouver un compromis entre l'indépendance du changement et la vitesse. Le niveau le plus indépendant - sérialisable - garantit qu'il n'y a pas de situations où des modifications apportées dans une transaction qui n'ont pas encore été validées peuvent être vues dans une autre transaction. Chaque niveau suivant ajoute une telle situation spécifique, lorsque l'exécution simultanée de transactions peut s'influencer mutuellement.Par défaut, une transaction est ouverte au niveau sérialisable, ce qui peut être désagréable (voir par exemple ce commentaire ).

La définition du niveau d'isolement des transactions lors de la création TransactionScopeest conseillée aux gestionnaires de ressources. Ils peuvent même ne pas prendre en charge tous les niveaux répertoriés System.Transactions.IsolationLevel. En outre, il convient de garder à l'esprit que lors de l'utilisation du pool de connexions pour travailler avec la base de données, la connexion pour laquelle le niveau d'isolement des transactions a été modifié conservera ce niveau lors du retour au pool . Désormais, lorsque le programmeur reçoit cette connexion du pool et s'appuie sur les valeurs par défaut, il observe un comportement inattendu.

Scénarios de travail typiques cTransactionScopeet les pièges importants (à savoir, les transactions imbriquées) sont bien couverts dans cet article sur "Habr" .

Applicabilité des transactions logicielles


Il faut dire que dans presque tous les systèmes d'information en exploitation commerciale, des processus sont lancés qui peuvent conduire le système à un état inacceptable. Par conséquent, il peut être nécessaire de contrôler ces processus, de déterminer si l'état actuel du système est acceptable et, sinon, de le restaurer. Transactions logicielles - un outil prêt à l'emploi pour maintenir le système dans un état acceptable.

Dans chaque cas, il serait constructif de considérer le coût:

  1. intégrer les processus dans l'infrastructure de transaction logicielle (ces processus doivent également être conscients TransactionScopede bien d'autres choses);
  2. l'entretien de cette infrastructure (par exemple, le coût de location d'équipement avec Windows à bord);
  3. la formation des employés (car le sujet des transactions .NET n'est pas courant).

Nous ne devons pas oublier que le processus de transaction peut être tenu de rendre compte de ses progrès au "monde extérieur", par exemple, pour tenir un journal des actions en dehors de la transaction.

De toute évidence, le rejet des transactions logicielles nécessitera la création ou la mise en œuvre de certains autres moyens de maintenir l'intégrité des données, qui auront également leur valeur. En fin de compte, il peut y avoir des cas où les violations de l'intégrité des données sont si rares qu'il est plus facile de restaurer un état acceptable du système par des interventions chirurgicales que de maintenir un mécanisme de récupération automatique.

Un exemple de gestionnaire de ressources volage


Voyons maintenant un exemple de gestionnaire de ressources simple qui ne prend pas en charge la récupération après une défaillance du système. Nous aurons un bloc de mémoire transactionnelle logicielle qui stocke une certaine valeur qui peut être lue et écrite. En l'absence de transaction, ce bloc se comporte comme une variable normale, et en présence d'une transaction, il stocke la valeur initiale, qui peut être restaurée après l'annulation de la transaction. Le code d'un tel gestionnaire de ressources est présenté ci-dessous:

 internal sealed class Stm<T> : System.Transactions.IEnlistmentNotification { private T _current; private T _original; private bool _enlisted; public T Value { get { return _current; } set { if (!Enlist()) { _original = value; } _current = value; } } public Stm(T value) { _current = value; _original = value; } private bool Enlist() { if (_enlisted) return true; var currentTx = System.Transactions.Transaction.Current; if (currentTx == null) return false; currentTx.EnlistVolatile(this, System.Transactions.EnlistmentOptions.None); _enlisted = true; return true; } #region IEnlistmentNotification public void Commit(System.Transactions.Enlistment enlistment) { _original = _current; _enlisted = false; } public void InDoubt(System.Transactions.Enlistment enlistment) { _enlisted = false; } public void Prepare(System.Transactions.PreparingEnlistment preparingEnlistment) { preparingEnlistment.Prepared(); } public void Rollback(System.Transactions.Enlistment enlistment) { _current = _original; _enlisted = false; } #endregion IEnlistmentNotification } 

On peut voir que la seule exigence formelle est l'implémentation de l'interface System.Transactions.IEnlistmentNotification. Parmi les intéressantes, les méthodes Enlist(qui ne font pas partie System.Transactions.IEnlistmentNotification) et Prepare. La méthode Enlistvérifie simplement si le code donné fonctionne dans le cadre de la transaction et, si c'est le cas, enregistre une instance de sa classe en tant que gestionnaire de ressources non constant. La méthode Prepareest appelée par le gestionnaire de transactions avant de valider les modifications. Notre gestionnaire de ressources signale sa disponibilité pour la validation en appelant une méthode System.Transactions.PreparingEnlistment.Prepared.

Ce qui suit est un code montrant un exemple d'utilisation de notre gestionnaire de ressources:

 var stm = new Stm<int>(1); using (var scope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required)) { stm.Value = 2; scope.Complete(); } 

Si vous usinglisez la propriété immédiatement après avoir quitté le bloc stm.Value, la valeur attendue y sera attendue 2. Et si vous supprimez l'appel scope.Complete, la transaction sera annulée et la propriété stm.Valueaura la valeur 1définie avant le début de la transaction.

Une séquence d'appels simplifiée lors de l'utilisation de transactions est System.Transactionsillustrée dans le diagramme ci-dessous.


On peut voir que dans cet exemple, toutes les possibilités offertes par l'infrastructure ne sont pas prises en compte System.Transactions. Nous les examinerons plus en détail après avoir pris connaissance des protocoles transactionnels et des transactions distribuées dans la section suivante.

Section TL; DR


Un programmeur peut utiliser une classe TransactionScopepour exécuter du code dans une transaction existante ou nouvelle. Une transaction est validée si et seulement si la TransactionScopeméthode est appelée sur une instance existante de la classe Dispose, même si la méthode a été appelée avantComplete. Un programmeur peut indiquer s'il souhaite démarrer une nouvelle transaction, tirer parti d'une transaction existante ou, à l'inverse, exécuter du code en dehors d'une transaction existante. Seuls les gestionnaires de ressources sont impliqués dans la transaction - des composants logiciels qui implémentent certaines fonctionnalités. Les gestionnaires de ressources peuvent être à long terme (se remettre d'une défaillance du système) et intermittents (ne pas se remettre). Un SGBD est un exemple de gestionnaire de ressources à longue durée de vie. Le gestionnaire de ressources est coordonné par un gestionnaire de transactions - un composant logiciel qui est automatiquement sélectionné par le runtime sans la participation d'un programmeur.

Le gestionnaire de ressources incohérent est une classe qui implémente l'interface System.Transactions.IEnlistmentNotificationdans la méthodePrepareconfirmant sa volonté de valider les modifications ou, inversement, signalant une annulation des modifications. Lorsque l'appelant fait quelque chose avec le gestionnaire de ressources, il vérifie si la transaction est ouverte maintenant et si elle est ouverte, elle est enregistrée à l'aide de la méthode System.Transactions.Transaction.EnlistVolatile.

Transactions distribuées


Qu'est ce que c'est


Une transaction distribuée implique plusieurs sous-systèmes d'information (en fait, tout n'est pas aussi simple, voir plus loin ci-dessous). Il est entendu que les modifications de tous les systèmes impliqués dans une transaction distribuée doivent être validées ou annulées.

Divers moyens de mise en œuvre des transactions ont été présentés ci-dessus: SGBD, infrastructure System.Transactionset mémoire transactionnelle programmatique intégrée à la plate-forme. Les transactions distribuées peuvent également être fournies avec ces outils. Par exemple, dans la base de données Oracle, la modification (et la lecture effective) des données de plusieurs bases de données au sein d'une même transaction les transforme automatiquement en données distribuées. Ensuite, nous parlerons des transactions distribuées par logiciel, qui peuvent inclure des gestionnaires de ressources hétérogènes.

Protocoles transactionnels


Un protocole transactionnel est un ensemble de principes par lesquels les applications impliquées dans une transaction interagissent. Dans le monde .NET, les protocoles suivants sont les plus courants.

Léger. Pas plus d'un gestionnaire de ressources durable n'est utilisé. Toutes les interactions transactionnelles se produisent dans le même domaine d'application ou le gestionnaire de ressources prend en charge la promotion et la validation en une seule phase (implémente IPromotableSinglePhaseNotification).

OleTx. L'interopérabilité entre plusieurs domaines d'application et plusieurs ordinateurs est autorisée. Vous pouvez utiliser de nombreux gestionnaires de ressources durables. Tous les ordinateurs participants doivent exécuter Windows. Utilisez les appels de procédure à distance (RPC).

WS-AT.L'interopérabilité entre plusieurs domaines d'application et plusieurs ordinateurs est autorisée. Vous pouvez utiliser de nombreux gestionnaires de ressources durables. Les ordinateurs participants peuvent exécuter différents systèmes d'exploitation, pas seulement Windows. Le protocole HTTP (Hypertext Transmission Protocol) est utilisé.

Il a été noté ci-dessus que le protocole de transaction actuel affecte le choix du gestionnaire de transactions, et les caractéristiques des ressources de contrôle impliquées dans la transaction influencent le choix du protocole. Nous listons maintenant les gestionnaires de transactions bien connus.

Gestionnaire de transactions léger (LTM) . Introduit dans le .NET Framework 2.0 et versions ultérieures. Gère les transactions à l'aide du protocole léger.

Gestionnaire de transactions du noyau (KTM). Introduit dans Windows Vista et Windows Server 2008. Gère les transactions à l'aide du protocole léger. Il peut appeler un système de fichiers transactionnels (TxF) et un registre transactionnel (TxR) sur Windows Vista et Windows 2008.

Coordinateur de transactions distribuées (MSDTC) . Gère les transactions à l'aide des protocoles OleTx et WS-AT.

Il convient également de garder à l'esprit que certains gestionnaires de ressources ne prennent pas en charge tous les protocoles répertoriés. Par exemple, MSMQ et SQL Server 2000 ne prennent pas en charge Lightweight, donc les transactions impliquant MSMQ ou SQL Server 2000 seront gérées par MSDTC, même s'ils sont les seuls participants. Techniquement, cette limitation provient du fait que les gestionnaires de ressources spécifiés, implémentant, bien sûr, l'interfaceSystem.Transactions.IEnlistmentNotificationN'implémentez pas l'interface System.Transactions.IPromotableSinglePhaseNotification. Il contient, entre autres, une méthode Promoteque le runtime invoque, si nécessaire, pour basculer vers un gestionnaire de transactions plus raide.

L'ambiguïté du concept de transaction distribuée devrait maintenant devenir évidente. Par exemple, vous pouvez définir une transaction distribuée comme une transaction à laquelle elle participe:

  1. au moins deux des gestionnaires de ressources;
  2. des gestionnaires de ressources arbitrairement variables et au moins deux à long terme;
  3. au moins deux des gestionnaires de ressources nécessairement situés sur des ordinateurs différents.

Par conséquent, il est préférable de toujours clarifier quelles transactions particulières sont impliquées.

Et dans ce contexte, MSDTC est principalement discuté. Il s'agit d'un composant logiciel de Windows qui gère les transactions distribuées. Il existe une interface graphique pour configurer et surveiller les transactions, qui se trouve dans l'utilitaire «Services de composants», en suivant le chemin «Ordinateurs - Poste de travail - Coordinateur de transactions distribuées - DTC local».


Pour la configuration, sélectionnez l'élément «Propriétés» dans le menu contextuel du nœud «Local DTC», et pour surveiller les transactions distribuées, sélectionnez l'élément «Statistiques de transaction» dans le panneau central.

Fixation biphasique


Si plusieurs gestionnaires de ressources participent à la transaction, les résultats de leur travail peuvent différer: par exemple, l'un d'eux s'est terminé avec succès, et il est prêt à valider les modifications, et l'autre a une erreur, et il va annuler les modifications. Cependant, l'essence d'une transaction distribuée réside dans le fait que les changements dans toutes les ressources de contrôle impliquées dans la transaction sont soit validés tous ensemble, soit annulés. Par conséquent, dans de tels cas, un protocole de fixation en deux phases est généralement utilisé.

En général, l'essence de ce protocole est la suivante. Pendant la première phaseLes gestionnaires de ressources impliqués dans la transaction préparent des informations suffisantes pour se remettre de l'échec (s'il s'agit d'un gestionnaire de ressources à long terme) et pour une exécution réussie à la suite d'une validation. D'un point de vue technique, le gestionnaire de ressources signale qu'il a terminé la première phase en appelant la méthode System.Transactions.PreparingEnlistment.Prepareddans la méthode Prepare. Ou le gestionnaire de ressources peut vous informer que les modifications ont été annulées en appelant la méthode ForceRollback.

Lorsque tous les gestionnaires de ressources impliqués dans la transaction ont «voté», c'est-à-dire qu'ils ont informé le gestionnaire de transactions s'ils souhaitaient valider ou annuler les modifications, la deuxième phase commence. À ce stade, les gestionnaires de ressources sont invités à valider leurs modifications (si tous les participants ont voté pour la correction) ou à refuser les modifications (si au moins un participant a voté pour la restauration). Techniquement, cela se traduit par l'invocation des méthodes Commitet Rollbackque les gestionnaires de ressources implémentent et dans lesquelles ils invoquent la méthode System.Transactions.Enlistment.Done.

Le gestionnaire de ressources peut également appeler la méthode System.Transactions.Enlistment.Donelors de la première phase. Dans ce cas, il est entendu qu'il n'engagera aucun changement (par exemple, ne fonctionne que pour la lecture) et ne participera pas à la deuxième phase. En savoir plus sur la validation en deux phases chez Microsoft .

Si la connexion entre le gestionnaire de transactions et au moins l'un des gestionnaires de ressources est perdue, la transaction devient gelée («dans le doute», dans le doute). Le gestionnaire de transactions, en appelant des méthodes InDoubt, avertit les gestionnaires de ressources disponibles de cet événement qui peuvent répondre de manière appropriée.

Il existe encore une fixation triphasée et ses modifications avec ses avantages et ses inconvénients. Le protocole de validation en trois phases est moins courant, peut-être parce qu'il nécessite encore plus de coûts de communication entre les sous-systèmes en interaction.

Aide-mémoire sur les interfaces System.Transactions


Quelque chose est difficile. Pour trier un peu les choses, je vais brièvement décrire les principales interfaces d'espace de noms System.Transactionsnécessaires pour créer un gestionnaire de ressources. Voici un diagramme de classes.



IEnlistmentNotification. Le gestionnaire de ressources implémente cette interface. Le gestionnaire de transactions appelle les méthodes implémentées dans l'ordre suivant. Au cours de la première phase, il appelle la méthode Prepare(sauf si les étoiles se sont réunies pour appeler la méthode ISinglePhaseNotification.SinglePhaseCommit, comme décrit dans le paragraphe suivant). Selon cette méthode, le gestionnaire de ressources enregistre les informations nécessaires pour se remettre d'un échec, prépare la validation finale des modifications de son côté et vote pour valider ou annuler les modifications. S'il arrive une deuxième phase, en fonction de la disponibilité des ressources et le contrôle des résultats du vote de l'opération de contrôle est l' une des trois méthodes: Commit, InDoubt, Rollback.

ISinglePhaseNotification.Le gestionnaire de ressources implémente cette interface s'il souhaite fournir au gestionnaire de transactions l'opportunité d'optimiser l'exécution en réduisant la deuxième phase de validation. Si le gestionnaire de transactions ne voit qu'un seul gestionnaire de ressources, dans la première phase de validation, il essaie d'appeler la méthode du gestionnaire de ressources SinglePhaseCommit(à la place IEnlistmentNotification.Prepare) et d'exclure ainsi le vote et la transition vers la deuxième phase. Cette approche présente des avantages et des inconvénients, dont Microsoft a le plus clairement parlé ici .

ITransactionPromoter. Le gestionnaire de ressources implémente cette interface (non seulement directement, mais via l'interfaceIPromotableSinglePhaseNotification), s'il souhaite fournir au gestionnaire de transactions la possibilité d'adhérer au protocole Lightweight même lors d'un appel à distance, jusqu'à ce que d'autres conditions surviennent nécessitant une complication du protocole. Lorsque vous devez compliquer le protocole, la méthode sera appelée Promote.

IPromotableSinglePhaseNotification. Le gestionnaire de ressources implémente cette interface afin, d'une part, de mettre en œuvre l'interface ITransactionPromoter, et d'autre part, afin que le gestionnaire de transactions puisse utiliser la validation en une seule phase, les méthodes d'appel IPromotableSinglePhaseNotification.SinglePhaseCommitet IPromotableSinglePhaseNotification.Rollback. Le gestionnaire de transactions appelle une méthode IPromotableSinglePhaseNotification.Initializepour marquer l'enregistrement réussi du gestionnaire de ressources de manière simplifiée. Plus ou moins, cela peut être compris à partir d' un document Microsoft .

Regardons un peu plusSystem.Transactions.Enlistmentet ses héritiers. Ce type d'instance est fourni par le gestionnaire de transactions lorsqu'il invoque les méthodes d'interface implémentées par le gestionnaire de ressources.



Enrôlement. Le gestionnaire de ressources peut appeler une seule méthode de ce type - Done, - pour signaler la réussite de sa partie du travail.

Préparation de l'inscription. À l'aide d'une instance de ce type lors de la première phase de validation, le gestionnaire de ressources peut signaler son intention de valider ou d'annuler les modifications. Un gestionnaire de ressources à longue durée de vie peut également obtenir les informations nécessaires pour récupérer d'une défaillance du système.

SinglePhaseEnlistment. À l'aide d'une instance de ce type, le gestionnaire de ressources peut transmettre au gestionnaire de transactions des informations sur les résultats de son travail à l'aide d'un schéma simplifié (validation en une phase).

Limitations et alternatives des transactions distribuées par logiciel


Une brève étude des opinions trouvées sur Internet montre que dans de nombreux domaines, les transactions distribuées sont démodées. Jetez un œil à ce commentaire malveillant , par exemple . L'objet principal de la critique, qui est brièvement mentionné ici , est la nature synchrone (bloquante) des transactions distribuées. Si l'utilisateur a envoyé une demande au cours du traitement de laquelle une transaction distribuée a été organisée, il ne recevra une réponse qu'après (avec succès ou avec une erreur) que tous les sous-systèmes inclus dans la transaction aient fini de fonctionner. Dans le même temps, il existe une opinion soutenue par la recherche selon laquelle le protocole de validation en deux phases affiche de mauvaises performances, en particulier avec une augmentation du nombre de sous-systèmes impliqués dans la transaction, comme cela est mentionné, par exemple, danscette publication sur "Habré" .

Si le créateur du système préfère renvoyer la réponse à l'utilisateur le plus tôt possible, en différant la coordination des données pour plus tard, alors une autre solution lui conviendra mieux. Dans le contexte du théorème de Brewer (théorème CAP ), nous pouvons dire que les transactions distribuées conviennent aux cas où la cohérence des données est plus importante que la disponibilité.

Il existe d'autres restrictions pratiques sur l'utilisation des transactions distribuées par logiciel. Par exemple, il a été établi expérimentalement que les transactions distribuées utilisant le protocole OleTx ne devaient pas traverser des domaines réseau. En tout cas, de longues tentatives pour les faire travailler n'ont pas abouti. De plus, il a été révélé que l'interaction entre plusieurs instances d'Oracle Database (transactions de bases de données distribuées) impose de sérieuses restrictions sur l'applicabilité des transactions logicielles distribuées (là encore, n'a pas pu démarrer).

Quelles sont les alternatives aux transactions distribuées? Tout d'abord, je dois dire qu'il sera très difficile de se passer de transactions techniques (normales, non distribuées). Il y a probablement des processus dans le système qui peuvent temporairement perturber l'intégrité des données, et il sera nécessaire d'assurer en quelque sorte la surveillance de ces processus. De la même manière, en termes de domaine, un concept peut surgir qui inclut un processus mis en œuvre par un ensemble de processus dans différents systèmes techniques, qui devrait commencer et se terminer dans le domaine des données intégrales.

En passant à des alternatives aux transactions distribuées, nous pouvons noter des solutions basées sur des services de messagerie, tels que RabbitMQ et Apache Kafka. Dans cette publication sur "Habré", quatre de ces solutions sont envisagées:

  1. , , ;
  2. , (Transaction Log Tailing);
  3. , ;
  4. (Event Sourcing).

Une autre alternative est le modèle Saga. Il implique une cascade de sous-systèmes avec ses transactions locales. À la fin des travaux, chaque système appelle les éléments suivants (soit indépendamment, soit avec l'aide d'un coordinateur). Pour chaque transaction, il existe une transaction d'annulation correspondante, et au lieu de transférer le contrôle, le sous-système peut initier l'annulation des modifications apportées précédemment par les sous-systèmes précédents. Sur "Habré" il y a de bons articles sur le modèle "Saga". Par exemple, cette publication fournit des informations générales sur le maintien des principes d'ACID dans les microservices, et cet article détaille un exemple d'implémentation du modèle Saga avec un coordinateur.

Dans notre entreprise, certains produits utilisent avec succès les transactions distribuées par logiciel via WCF, mais il existe d'autres options. Une fois, lorsque nous avons essayé de nous faire des amis un nouveau système avec des transactions distribuées, nous avons eu de nombreux problèmes, y compris une collision avec les limitations décrites ci-dessus et des problèmes parallèles avec la mise à jour de l'infrastructure logicielle. Par conséquent, dans des conditions de pénurie de ressources pour exécuter une autre décision de capital, nous avons appliqué les tactiques suivantes. L'appelé capture les modifications dans tous les cas, mais note qu'elles sont à l'état de brouillon, de sorte que ces modifications n'affectent pas encore le fonctionnement du système appelé. Ensuite, l'appelant, lorsqu'il termine son travail via une transaction distribuée, le SGBD active les modifications apportées par le système appelé. De cette façonau lieu de transactions distribuées par logiciel, nous avons utilisé des transactions SGBD distribuées, qui dans ce cas se sont révélées beaucoup plus fiables.

Est-ce donc dans .NET Core?


Dans .NET Core (et même dans .NET Standard), il existe tous les types nécessaires pour organiser les transactions et créer votre propre gestionnaire de ressources. Malheureusement, dans .NET Core, les transactions basées System.Transactionsont une sérieuse limitation: elles ne fonctionnent qu'avec le protocole Lightweight. Par exemple, si deux gestionnaires de ressources durables sont utilisés dans le code, lors de l'exécution, l'environnement lèvera une exception dès que le deuxième gestionnaire sera appelé.

Le fait est qu'ils essaient de rendre .NET Core indépendant du système d'exploitation, de sorte que le lien vers les gestionnaires de transactions tels que KTM et MSDTC est exclu, à savoir qu'ils sont nécessaires pour prendre en charge les transactions avec les propriétés spécifiées. Il est possible que la connexion des gestionnaires de transactions soit implémentée sous la forme de plugins, mais jusqu'à présent, cela a été écrit avec une fourche, vous ne pouvez donc pas encore compter sur l'utilisation industrielle des transactions distribuées dans .NET Core.

Par expérience, vous pouvez vérifier les différences dans les transactions distribuées dans .NET Framework et .NET Core en écrivant le même code, en le compilant et en l'exécutant sur différentes plates-formes.

Un exemple d'un tel code qui appelle SQL Server et Oracle Database de manière séquentielle.

 private static void Main(string[] args) { using (var scope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required)) { MsSqlServer(); Oracle(); scope.Complete(); } } private static void Oracle() { using (var conn = new Oracle.ManagedDataAccess.Client.OracleConnection("User Id=some_user;Password=some_password;Data Source=some_db")) { conn.Open(); using (var cmd = conn.CreateCommand()) { cmd.CommandText = "update t_hello set id_hello = 2 where id_hello = 1"; cmd.ExecuteNonQuery(); } conn.Close(); } } private static void MsSqlServer() { var builder = new System.Data.SqlClient.SqlConnectionStringBuilder { DataSource = "some_computer\\some_db", UserID = "some_user", Password = "some_password", InitialCatalog = "some_scheme", Enlist = true, }; using (var conn = new System.Data.SqlClient.SqlConnection(builder.ConnectionString)) { conn.Open(); using (var cmd = conn.CreateCommand()) { cmd.CommandText = "update t_hello set id_hello = 2 where id_hello = 1"; cmd.ExecuteNonQuery(); } conn.Close(); } } 

Les projets prêts pour la construction sont sur GitHub .

L'exécution de l'exemple pour .NET Core échoue. L'emplacement et le type de l'exception levée dépendent de l'ordre de l'appel du SGBD, mais dans tous les cas, cette exception indique une opération de transaction non valide. L'exécution de l'exemple pour .NET Framework réussit si MSDTC est en cours d'exécution à ce moment; cependant, dans l'interface graphique de MSDTC, vous pouvez observer l'enregistrement d'une transaction distribuée.

Transactions distribuées et WCF


Windows Communication Foundation (WCF) est le cadre .NET pour l'organisation et l'appel des services réseau. Comparé aux approches REST et ASP.NET Web API les plus en vogue, il présente ses propres avantages et inconvénients. WCF est un très bon ami des transactions .NET, et dans le monde du .NET Framework, il est pratique de l'utiliser pour organiser les transactions réparties entre le client et le service.

Dans .NET Core, cette technologie ne fonctionne que du côté client, c'est-à-dire que vous ne pouvez pas créer un service, mais vous pouvez uniquement faire référence à un service existant. Cependant, cela n'est pas très important, car, comme mentionné ci-dessus, avec les transactions distribuées dans .NET Core, les choses ne vont pas du tout bien.

Comment fonctionne WCF

Pour les lecteurs qui ne sont pas familiers avec WCF, voici les informations générales les plus courtes sur ce que cette technologie est en pratique. Contexte - deux systèmes d'information appelés client et service. Au moment de l'exécution, le client accède à un autre système d'information qui prend en charge le service qui l'intéresse et nécessite qu'une opération soit effectuée. La gestion est ensuite retournée au client.

Pour créer un service sur WCF, vous devez généralement écrire une interface qui décrit le contrat pour le service en cours de création et une classe qui implémente cette interface. La classe et l'interface sont marquées avec des attributs WCF spéciaux qui les distinguent du reste des types et spécifient certains détails du comportement lors de la découverte et de l'appel du service. Ces types sont enveloppés dans quelque chose qui fonctionne comme un serveur (par exemple, dans une DLL sur laquelle IIS est défini) et sont complétés par un fichier de configuration (il existe des options), où les détails de l'implémentation du service sont indiqués. Après le démarrage, le service est accessible, par exemple, à l'adresse réseau; dans le navigateur Internet, vous pouvez voir les contrats que le service demandé met en œuvre.

Un programmeur qui souhaite accéder à un service WCF existant utilise un utilitaire de console ou une interface graphique intégrée à l'environnement de développement afin de former des types en C # (ou dans un autre langage pris en charge) qui correspondent aux contrats de service à l'adresse du service. Le fichier avec les types obtenus est inclus dans le projet d'application client, et ensuite le programmeur utilise les mêmes termes que ceux contenus dans l'interface de service, profitant des avantages de la progression (typage statique). De plus, le fichier de configuration du client précise les caractéristiques techniques du service appelé (il peut également être configuré dans le code, sans le fichier de configuration).

WCF prend en charge différents types de transport, de chiffrement et d'autres paramètres techniques plus subtils. La plupart d'entre eux sont unis par le concept de "liaison" (Reliure). Le service WCF comporte trois paramètres importants:

  1. l'adresse à laquelle il est disponible;
  2. contraignant
  3. contrat (interfaces).

Tous ces paramètres sont définis dans les fichiers de configuration du service et du client.

Dans notre entreprise, WCF (avec et sans transactions distribuées) est largement utilisé dans les produits mis en œuvre, cependant, étant donné les tendances de la mode, son utilisation dans les nouveaux produits est toujours en question.

Comment lancer des transactions distribuées dans WCF

Afin d'initier des transactions basées dans WCF System.Transactions, le programmeur doit définir plusieurs attributs dans le code, s'assurer que les liaisons utilisées prennent en charge les transactions distribuées, qu'elles sont écrites sur le client et dans le service, transactionFlow="true"et qu'un gestionnaire de transactions approprié s'exécute sur tous les ordinateurs impliqués (le plus probable , ce sera MSDTC).

Liaisons de transactions distribuées: NetTcpBinding, NetNamedPipeBinding, WSHttpBinding, WSDualHttpBinding et WSFederationHttpBinding.

La méthode (opération) de l'interface de service doit être marquée d'un attribut System.ServiceModel.TransactionFlowAttribute. Ensuite, avec certains paramètres d'attribut et lors de la définition du paramètre d' TransactionScopeRequiredattribut , la System.ServiceModel.OperationBehaviorAttributetransaction sera répartie entre le client et le service. De plus, par défaut, le service est considéré comme votant pour valider la transaction, sauf si une exception est levée au moment de l'exécution. Pour modifier ce comportement, vous devez définir la valeur de paramètre d' TransactionAutoCompleteattribut correspondante System.ServiceModel.OperationBehaviorAttribute.

Code d'un service WCF simple qui prend en charge les transactions distribuées.
 [System.ServiceModel.ServiceContract] public interface IMyService { [System.ServiceModel.OperationContract] [System.ServiceModel.TransactionFlow(System.ServiceModel.TransactionFlowOption.Mandatory)] int DoSomething(string input); } public class MyService : IMyService { [System.ServiceModel.OperationBehavior(TransactionScopeRequired = true)] [System.ServiceModel.TransactionFlow(System.ServiceModel.TransactionFlowOption.Mandatory)] public int DoSomething(string input) { if (input == null) throw new System.ArgumentNullException(nameof(input)); return input.Length; } } 

De toute évidence, il ne diffère du code de service normal que par l'utilisation de l'attribut System.ServiceModel.TransactionFlowet par le réglage spécial de l'attribut System.ServiceModel.OperationBehavior.

Exemple de configuration pour ce service.
 <system.serviceModel> <services> <service name="WcfWithTransactionsExample.MyService" behaviorConfiguration="serviceBehavior"> <endpoint address="" binding="wsHttpBinding" bindingConfiguration="mainWsBinding" contract="WcfWithTransactionsExample.IMyService"/> <endpoint address="mex" contract="IMetadataExchange" binding="mexHttpBinding"/> </service> </services> <bindings> <wsHttpBinding> <binding name="mainWsBinding" maxReceivedMessageSize="209715200" maxBufferPoolSize="209715200" transactionFlow="true" closeTimeout="00:10:00" openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00"> <security mode="None"/> <readerQuotas maxArrayLength="209715200" maxStringContentLength="209715200"/> </binding> </wsHttpBinding> </bindings> </system.serviceModel> 

Notez que la liaison est de type WSHttpBinding et l'attribut est utilisé transactionFlow="true".

Section TL; DR


Les transactions distribuées incluent plusieurs gestionnaires de ressources et toutes les modifications doivent être validées ou annulées. Certains SGBD modernes implémentent des transactions distribuées qui fournissent un mécanisme pratique pour connecter plusieurs bases de données. Les transactions distribuées par logiciel (non implémentées dans le SGBD) peuvent inclure différentes combinaisons de gestionnaires de ressources sur différents ordinateurs exécutant des systèmes d'exploitation différents, mais elles ont des limites qui doivent être prises en compte avant de se fier à elles. Les solutions de messagerie constituent une alternative moderne aux transactions distribuées. Dans .NET Core, les transactions distribuées ne sont pas encore prises en charge.

WCF est l'un des outils standard et éprouvés de création et d'accès aux services dans le monde .NET, prenant en charge plusieurs types de transport et de chiffrement. WCF est très proche des transactions distribuées basées sur System.Transactions. La configuration des transactions distribuées pour WCF consiste à baliser le code avec plusieurs attributs et à ajouter quelques mots dans les fichiers de configuration du service et du client. Toutes les liaisons WCF ne prennent pas en charge les transactions distribuées. De plus, il est clair que les transactions dans WCF ont les mêmes limitations que sans utiliser WCF. Jusqu'à présent, la plate-forme .NET Core vous permet uniquement d'accéder aux services sur WCF, plutôt que de les créer.

Lit d'enfant


Cet article est un aperçu des bases des transactions logicielles .NET. Certaines conclusions concernant les tendances des transactions de logiciels peuvent être trouvées dans les sections sur l'applicabilité et les limites des sujets discutés, et en conclusion, les principales thèses de la publication sont rassemblées. Je suppose qu'ils peuvent être utilisés comme une feuille de triche lorsque l'on considère les transactions logicielles comme l'une des options pour la mise en œuvre d'un système technique ou pour actualiser les informations pertinentes en mémoire.

Transactions (domaine, SGBD, logiciels). Les exigences de domaine sont parfois formulées sous la forme de transactions - opérations qui, commençant dans le domaine des données intégrales, une fois terminées (y compris infructueuses) devraient également entrer dans le domaine des données intégrales (éventuellement déjà différentes). Ces exigences sont généralement implémentées en tant que transactions système. Un exemple classique de transaction est le transfert d'argent entre deux comptes, consistant en deux opérations indivisibles - retirer de l'argent d'un compte et créditer un autre. En plus des transactions bien connues implémentées par le SGBD, il existe également des transactions logicielles, par exemple, dans le monde .NET. Les gestionnaires de ressources sont des composants logiciels qui sont conscients de l'existence de telles transactions et peuvent y être inclus, c'est-à-dire valider ou annuler les modifications apportées.Les gestionnaires de ressources reçoivent des instructions sur la validation et l'annulation des modifications du gestionnaire de transactions, qui est le fondement de l'infrastructureSystem.Transactions.

Gestionnaires de ressources durables et intermittents. Les gestionnaires de ressources à long terme prennent en charge la récupération de données après une défaillance du système. Les pilotes de SGBD pour .NET offrent généralement de telles fonctionnalités. Les gestionnaires de ressources intermittents ne prennent pas en charge la reprise après sinistre. La mémoire transactionnelle programmatique - un moyen de gérer des objets en RAM - peut être considérée comme un exemple de gestionnaire de ressources volage.

Transactions et gestionnaires de ressources .NET. Le programmeur .NET utilise des transactions logicielles et crée ses propres gestionnaires de ressources à l'aide de types de l'espace de nomsSystem.Transactions. Cette infrastructure permet d'utiliser des transactions d'imbrication et d'isolement diverses (avec des limitations connues). L'utilisation des transactions n'est pas compliquée, et elle consiste à encapsuler le code dans un bloc usingavec certaines caractéristiques. Cependant, les gestionnaires de ressources inclus de cette manière dans la transaction doivent conserver pour leur part les fonctionnalités requises. L'utilisation de gestionnaires de ressources hétérogènes dans une transaction ou l'utilisation d'un gestionnaire de différentes manières peut automatiquement transformer une transaction en transaction distribuée.

Transactions distribuées (SGBD, logiciels).Une transaction distribuée couvre plusieurs sous-systèmes, dont les changements doivent être synchronisés, c'est-à-dire qu'ils sont soit validés tous ensemble, soit annulés. Les transactions distribuées sont implémentées dans certains SGBD modernes. Les transactions distribuées par logiciel (ce ne sont pas celles qui sont implémentées par le SGBD) imposent des restrictions supplémentaires sur les processus et les plates-formes en interaction. Les transactions distribuées se dégradent progressivement pour laisser place à des solutions basées sur les services de messagerie. Pour transformer une transaction ordinaire en transaction distribuée, le programmeur n'a rien à faire: lorsqu'un gestionnaire de ressources avec certaines caractéristiques est inclus dans la transaction au moment de l'exécution, le gestionnaire de transaction fera automatiquement tout ce qu'il faut. Les transactions logicielles régulières sont disponibles dans .NET Core et .NET Standard, et les transactions distribuées ne sont pas disponibles.

Transactions distribuées via WCF. WCF est l'un des outils .NET standard pour créer et appeler des services, qui prend également en charge des protocoles standardisés. En d'autres termes, les services WCF configurés d'une manière spécifique sont accessibles à partir de n'importe quelle application, pas seulement .NET ou Windows. Pour créer une transaction distribuée au-dessus de WCF, vous devez baliser les types qui composent le service avec des attributs supplémentaires et apporter des modifications minimales aux fichiers de configuration du service et du client. Vous ne pouvez pas créer de services WCF dans .NET Core et .NET Standard, mais vous pouvez créer des clients WCF.

Exemple de vérification de System.Transactions sur GitHub

Les références

Concepts de base


ACID ( «»)
( Microsoft)
( Microsoft)
( «»)
( «»)
( «»)


X/Open XA ( The Open Group)
Java Transaction API ( «»)
Cache ( InterSystems)

.NET


.NET ( Tech Blog Collection)
TransactionScope («»)
, ( Microsoft)
.NET Standard 2.0 ( .NET Standard GitHub)
( YarFullStack)


(«»)
(«»)
«» («»)
«» («»)
(«»)

Éditorial

Source: https://habr.com/ru/post/fr433136/


All Articles