Frage

Ich arbeite an einer Anwendung, die angeblich Produkte zu schaffen (wie Versicherungspolicen Versand), wenn PayPal sofortige Zahlungs Benachrichtigungen empfangen werden. Leider PayPal sendet manchmal doppelte Benachrichtigungen. Darüber hinaus gibt es einen weiteren Drittanbietern, die Web-Service-Updates gleichzeitig ausführt, wenn sie Updates von PayPal erhalten auch.

Hier ist eine Prinzipskizze der Datenbanktabellen beteiligt.

// table "package"
// columns packageID, policyID, other data...
// 
// table "insurancepolicy"
// columns policyID, coverageAmount, other data...

Hier ist eine Prinzipskizze, was ich tun möchte:

using (SqlConnection conn = new SqlConnection(...))
{
  sqlTransaction sqlTrans = conn.BeginTransaction(IsolationLevel.RepeatableRead);

  // Calls a stored procedure that checks if the foreign key in the transaction table has a value.
  if (PackageDB.HasInsurancePolicy(packageID, conn))
  { 
    sqlTrans.Commit();
    return false;
  }

  // Insert row in foreign table.
  int policyID = InsurancePolicyDB.Insert(coverageAmount, conn);
  if (policyID <= 0)
  {
    sqlTrans.Rollback();
    return false;
  }

  // Assign foreign key to parent table.  If this fails, roll back everything.
  bool assigned = PackageDB.AssignPolicyID(packageID, policyID, conn);
  if (!assigned)
  {
    sqlTrans.Rollback();
    return false;
  }
}

Wenn zwei (oder mehr) Threads (oder Prozesse oder Anwendungen) dies zur gleichen Zeit zu tun, möchte ich das erste Thread das „Paket“ Zeile zu sperren, während es keine policyId hat, bis die Richtlinie erstellt wird und die policyId ist mit dem Paket-Tabelle zugeordnet. Dann würde die Sperre aufgehoben werden, nachdem der policyId das Paket Tabelle zugeordnet ist. Es ist meine Hoffnung, dass die anderen Threads, diese denselben Code rufen pausiert, wenn er das Paket Zeile liest sicherzustellen, dass es keine policyId erstes hat. Wenn die Sperre der ersten Transaktion freigegeben wird, es ist meine Hoffnung, dass die zweite Transaktion gibt den policyId zu sehen ist und daher zurückzukehren, ohne Zeilen in die Richtlinientabelle eingefügt wird.

. Hinweis: Wegen des CRUD Datenbank-Designs, die jeweils die gespeicherten Prozeduren beteiligt entweder Read (wählen), Create (Einsatz), oder aktualisiert

Ist das der richtige Einsatz von Repeatabletransaktionsisolations?

Danke.

War es hilfreich?

Lösung

Es wäre sicherer und saubere, wenn insert into Policy trifft nur einige Einzigartigkeit Tabellenbedingung beim Versuch Duplikat einzufügen. Isolationsstufe Anhebung kann Parallelität senken und andere unangenehme Probleme wie Deadlocks führen.

Eine andere Möglichkeit ist es, immer Politik Zeile einzufügen, dann rollen sie zurück, wenn Paket auf eine Politik angebracht wurde bereits:

begin tran (read committed)

/* tentatively insert new Policy */
insert Policy

/* attach Package to Policy if it's still free */
update Package
  set Package.policy_id = @policy_id
  where Package.package_id = @package_id and Package.policy_id is null

if @@rowcount > 0
  commit
else
  rollback

Dies funktioniert am besten, wenn Konflikte sind selten, die Ihr Fall zu sein scheint.

Andere Tipps

Ich glaube, Sie tatsächlich Serializable Isolationsstufe zu wollen. Das Problem ist, dass zwei Threads über die HasInsurancePolicyCheck bekommen können (obwohl ich keine Ahnung, was InsurancePolicyDB.Insert tun würde, oder warum es zurückgeben 0)

Sie haben viele andere Möglichkeiten, diese auch. Man wird mit einer Nachrichtenwarteschlange und die Verarbeitung dieser Anfragen selbst seriell. Ein weiterer Grund ist die Verwendung sp_getapplock und Sperre auf einige Schlüssel eindeutig zu diesem Paket . Auf diese Weise können keine weiteren Zeilen oder Tabellen sperre als Sie müssen.

Ich bin mit der "Message Queue" Idee in aaronjensen Antwort. Wenn Sie sich um mehrere gleichzeitige Threads versuchen, dieselbe Zeile von Daten gleichzeitig zu aktualisieren, sollten Sie stattdessen haben die Fäden, ihre Daten in eine Arbeitswarteschlange einzufügen, die dann der Reihe nach von einem einzigen Thread verarbeitet wird. Dies verringert erheblich Anstoßes auf die Datenbank, da der Zieltabelle, indem nur ein Thread anstelle von „N“, und die Arbeitsschlange Operationen beschränken sich auf die Einsätze von den Messaging-Threads, und eine Lese / Aktualisierung von der Datenverarbeitungsthread aktualisiert wird.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top