nhibernate deadlocks

J'utilise le code suivant dans une page ASP.NET pour créer un logging, puis count les loggings pour m'assurer que je n'ai pas dépassé une limite définie et annuler la transaction si je l'ai.

using (var session = NhibernateHelper.OpenSession()) using (var transaction = session.BeginTransaction()) { session.Lock(mall, LockMode.None); var voucher = new Voucher(); voucher.FirstName = firstName ?? ssortingng.Empty; voucher.LastName = lastName ?? ssortingng.Empty; voucher.Address = address ?? ssortingng.Empty; voucher.Address2 = address2 ?? ssortingng.Empty; voucher.City = city ?? ssortingng.Empty; voucher.State = state ?? ssortingng.Empty; voucher.Zip = zip ?? ssortingng.Empty; voucher.Email = email ?? ssortingng.Empty; voucher.Mall = mall; session.Save(voucher); var issued = session.CreateCriteria<Voucher>() .Add(Ressortingctions.Eq("Mall", mall)) .SetProjection(Projections.Count("ID")) .UniqueResult<int>(); if (issued >= mall.TotalVouchers) { transaction.Rollback(); throw new VoucherLimitException(); } transaction.Commit(); return voucher; } 

Cependant, je reçois une tonne d'interblocages. Je suppose que cela se produit parce que j'essaie de countr les loggings dans une table que je viens d'effectuer un insert sur et un verrou est toujours maintenu sur la ligne insérée, ce qui provoque l'impasse.

  • Quelqu'un peut-il confirmer cela?
  • Quelqu'un peut-il suggérer une solution?

J'ai essayé d'appeler SetLockMode (LockMode.None) sur la requête finale, mais cela entraîne une exception NullReferenceException que je ne peux pas comprendre.

Edit: Si j'exécute la requête avant de sauvegarder l'object, cela fonctionne, mais je n'effectue pas l'objective de vérifier que mon insertion n'a pas dépassé la limite (dans le cas d'insertions simultanées).

Edit: J'ai trouvé qu'utiliser IsolationLevel.ReadUncommited dans l'appel session.BeginTransaction résout le problème, mais je ne suis pas expert en database. Est-ce la solution appropriée au problème ou devrais-je ajuster ma logique d'une certaine manière?

Cette design serait encline à l'impasse – généralement (pas toujours) une connection est peu susceptible de se bloquer, mais plusieurs connections qui font des insertions et des agrégats contre la même table sont très susceptibles de se bloquer. En effet, alors que toute l'activité d'une transaction semble complète du sharepoint vue de la connection effectuant le travail – la database ne verrouille pas une transaction à partir de ses propres loggings – les requêtes agrégées provenant des transactions AUTRES tenteraient de verrouiller la table entière ou de grandes parties de celui-ci en même time, et ceux-ci seraient impasse.

Lire Uncommitted n'est pas votre ami dans ce cas, car il dit essentiellement "ignorer les verrous", ce qui à un moment donné signifiera violer les règles que vous avez définies autour des données. IE le nombre d'loggings dans la table sera inexact, et vous allez agir sur ce nombre inexact. Votre count reviendra 10 ou 13 lorsque la vraie réponse est 11.

Le meilleur conseil que j'ai est de réorganiser votre logique d'insertion de sorte que vous capturez l'idée du nombre, sans countr littéralement les lignes. Vous pourriez aller dans quelques directions. Une idée que j'ai est la suivante: numéroter littéralement les coupons insérés avec une séquence et imposer une limite à la séquence elle-même.

  1. Faire un tableau de séquence avec des colonnes (je devine) MallID, nextVoucher, maxVouchers
  2. Semence cette table avec les mallids, 1, et quelle que soit la limite est pour chaque centre commercial
  3. Changez la logique d'insertion en ce pseudo code:
 Commencer la transaction
 Sanity vérifier le prochain coupon pour Mall dans la table de séquence;  s'il y en a trop, avorter
 Si less de MaxVouchers pour Mall alors {
   vérifier, récupérer, verrouiller et incrémenter nextVoucher
   Si l'incrément a réussi, utilisez la valeur de nextVoucher pour effectuer votre insertion. 
     Incluez-le dans la table cible.
 }
 Erreur?  Rollback
 Pas d'erreur?  Commettre

Une table de séquence comme celle-ci bloque la concurrency, mais je ne pense pas autant que de countr les lignes de la table en permanence. Assurez-vous de tester le perf. En outre, [vérifier, récupérer, verrouiller et incrémenter] est important – vous devez verrouiller exclusivement la ligne dans la table de séquence pour empêcher une autre connection d'utiliser la même valeur dans la fraction de seconde avant de l'incrémenter. Je connais la syntaxe SQL pour cela, mais je crains de ne pas être un expert de nHibernate.

Pour lire les erreurs de données non validées, vérifiez ceci: http://sqlblog.com/blogs/merrill_aldrich/archive/2009/07/29/transaction-isolation-dirty-reads-deadlocks-demo.aspx (avertissement: Merrill Aldrich est moi 🙂

2 questions:

  1. À quelle fréquence les coupons sont-ils supprimés
  2. Des objections (au-delà de la pureté) à un triggersur de niveau DB?