3 Stimmen

Mehrere gleichzeitige Speicherungen verursachen WCF/NHibernate "Zeile durch andere Transaktion aktualisiert"

Ich arbeite an einer großen Anwendung, die komplexe "Ereignisse" mit einer großen Menge an Daten verwaltet.

Die Anwendung besteht aus einem Client (hauptsächlich C#, .NET 2.0), einem WCF-basierten Server, der auf IIS läuft (Webdienste auf dieser Ebene), und einem Daten-Backend, das auf NHibernate mit einem MS SQL Server-Datenbank-Backend basiert.

Bei folgendem Szenario ist ein Fehler aufgetreten:

Mehrere Benutzer (in diesem Fall 6, in unserem Test) speichern gleichzeitig ein persistiertes Objekt (z. B. ein Ereignis) auf dem Client. (Dies geschieht über einen verbalen Countdown, so dass es aufgrund der Reaktionszeiten zu einem Unterschied von etwa einer Sekunde kommt.)

Wenn sie Objekte auf dem Client speichern, ruft der Client den Server auf, um das Objekt zu speichern. Der Server führt die erforderliche Geschäftslogik aus und überträgt die Änderung dann über NHibernate.

In dieser Situation (mehrfaches Speichern über Serviceaufrufe) werden einige Kunden mit einer unbearbeiteten Ausnahme zurückkommen:

"Zeile wurde durch eine andere Transaktion aktualisiert oder gelöscht"

Dies ist eine NHibernate-Ausnahme.

Bei meinen Nachforschungen habe ich einige Fälle dieses Fehlers gefunden, aber es war immer sequenziell. Unsere Anwendung ist sequenziell in Ordnung, dies geschieht nur bei gleichzeitigen Speichern.

Wie sollten mehrere gleichzeitige NHibernate-Transaktionen voreinander geschützt werden?

(Da es sich um eine Anwendung mit hohem Datenaufkommen handelt, ist es gut, wenn die Sperrung auf ein Minimum reduziert wird. Wir können es uns nicht leisten, alle Speicherungen generell zu sperren).

Gibt es NHibernate-Einstellungen, mit denen dies möglich ist? Müssen wir vorher einige Sperren auf der Seite des Servercodes herausfinden? Kann die Datenbank damit umgehen, vielleicht mit unterschiedlichen Einstellungen für den Transaktionsschutz?

Wie bei den meisten Threading-Problemen ist es schwierig, dies zu testen, daher der Ansatz Forschung/Theorie. Wie sind die Erfahrungen mit einer solchen Architektur?

Edit: Weitere Informationen und eine Theorie.

Es scheint, dass das Ändern von Werten, die sich direkt in der Tabelle als Hauptentität befinden, dieses Problem nicht verursacht, während das Ändern von Elementen, auf die über eine Verknüpfung zugegriffen wird, dies tut. (In diesem Fall eine Reihe von Attributen).

Unsere Arbeitstheorie, die auf diese Situation zu passen scheint, ist die folgende:

Für ein und dasselbe Ereignis werden mehrere Daten gespeichert. Jedes Ereignis erhält eine Kopie des bestehenden Ereignisses (zum Ändern). Die ersten Speicherungen sind in Ordnung. Die späteren haben jedoch Aktualisierungen wie das Hinzufügen oder Löschen von Attributen (die eine Join-Tabelle sind) und entdecken, dass das Hinzufügen oder Löschen, das getan werden muss, bereits getan wurde, und brechen ab, weil eine andere Transaktion das Ereignis bearbeitet hat.

Wie sollten wir diese Ereignisse synchron halten?

1voto

MrTelly Punkte 14443

Ich hatte das gleiche Problem mit einer Einzelbenutzeranwendung, die multithreaded war. Der Schlüssel war, meine Sitzungsvariable mit [ThreadSafe] zu dekorieren. Seltsamerweise scheinen die verschiedenen Sitzungsmanager, die ich mir angesehen habe, dies nicht zu tun, und sie meinen, sie seien automatisch thread-sicher ... war nicht der Fall in meiner Erfahrung.

Asp Session Manager

Multi-DB-Sitzung

1voto

Colin Dabritz Punkte 811

Ich habe die obige Antwort akzeptiert, weil es mehr oder weniger der Weg war, den wir eingeschlagen haben. Ich wollte das weiter ausführen. Unsere Anwendung soll stark genutzt werden, so dass es eine schlechte Idee ist, eine "update major entity"-Prozedur komplett zu sperren.

Was wir am Ende taten, war die Verwendung der C# Mutex, die "etwas Einzigartiges" zum Sperren benötigt, in diesem Fall eine Ereignis-ID. Das bedeutet, dass alle Operationen auf dieselbe komplexe Entität gesperrt werden und in diesem Sinne "atomar" werden. Damit war unser Problem gelöst. Wir verpackten jede "destruktive" Service-Operation mehr oder weniger in einen Mutex (über eine bestehende Utility-Klasse).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X