Ich schreibe einen Daemon, um die Erstellung neuer Objekte zu überwachen, die Zeilen zu einer Datenbanktabelle hinzufügt, wenn es neue Dinge erkennt. Wir rufen die Objektwidgets auf. Der Codefluss ungefähr folgendes:Verwenden einer Transaktion, um ein Rennen zu vermeiden
1: every so often:
2: find newest N widgets (from external source)
3: foreach widget
4: if(widget not yet in database)
5: add rows for widget
Die letzten beiden Zeilen ein Rennzustand vorliegt, da, wenn zwei Instanzen dieses Daemon gleichzeitig ausgeführt werden, können sie sowohl eine Zeile für Widget X erstellen, wenn die Timing-Linien oben. Die naheliegendste Lösung wäre, eine unique
Einschränkung für die Widget-ID-Spalte zu verwenden, aber das ist aufgrund des Datenbanklayouts nicht möglich (es ist eigentlich zulässig, mehr als eine Zeile für ein Widget zu haben, aber der Daemon sollte nicht Mach das nie automatisch.
Mein nächster Gedanke wäre, eine Transaktion zu verwenden, da dies für sie vorgesehen ist. In der Welt von ADO.NET glaube ich, dass ich eine Isolationsstufe von Serializable
möchte, aber ich bin nicht positiv. Kann mir jemand in die richtige Richtung zeigen?
Update: Ich habe etwas experimentiert, und die serialisierte Transaktion scheint nicht das Problem zu lösen, oder zumindest nicht sehr gut. Der interessante Fall wird unten beschrieben und nimmt an, dass nur eine Tabelle beteiligt ist. Beachten Sie, dass ich über die Sperre Details nicht positiv bin, aber ich glaube, ich habe es richtig:
Thread A: Executes line 4, acquiring a read lock on the table
Thread B: Executes line 4, acquiring a read lock on the table
Thread A: Tries to execute line 5, which requires upgrading to a write lock
(this requires waiting until Thread B unlocks the table)
Thread B: Tries to execute line 5, again requiring a lock upgrade
(this requires waiting until Thread A unlocks)
Dies läßt uns in einer klassischen Deadlock-Bedingung. Andere Codepfade sind möglich, aber wenn die Threads A und B nicht verschachtelt werden, gibt es kein Synchronisationsproblem. Das Endergebnis ist, dass eine SqlException auf einen der Threads ausgelöst wird, nachdem SQL den Deadlock erkannt und eine der Anweisungen beendet hat. Ich kann diese Ausnahme abfangen und den speziellen Fehlercode erkennen, aber das fühlt sich nicht sehr sauber an.
Eine andere Route, die ich nehmen kann, besteht darin, eine zweite Tabelle zu erstellen, die vom Daemon gesehene Widgets verfolgt, wobei ich eine unique
Einschränkung verwenden kann. Dies erfordert immer noch das Erfassen und Erkennen bestimmter Fehlercodes (in diesem Fall Verletzungen der Integritätsbedingung), so dass ich immer noch an einer besseren Lösung interessiert bin, wenn mir jemand einen einfallen lässt.
Danke für die Ideen. Leider denke ich, dass dies das Problem nicht lösen wird (ich werde es in der ursprünglichen Frage erklären). – Charlie
In einer Konsistenz Sichtweise funktioniert es :-) aber es ist natürlich nicht gut, wenn es zu einer toten Sperren führt. Ich habe Angst, wenn ich nicht viel helfen kann, aber ich füge meiner Antwort einige Alternativen hinzu. –
Nochmals vielen Dank dafür, dass Sie so viel darüber nachgedacht haben. Ich hatte auch die Service-Idee in Betracht gezogen, und ich denke, das wäre die ideale Lösung in meinem speziellen Fall (da ich dann mehr "standardmäßige" Sperr-Primitive wie Mutexe oder Monitore verwenden kann). Ich stimme zu, dass zwei Dämonen, wie ich beschrieben habe, keine gute Idee sind. – Charlie