Ich habe durch Ressourcen für rollBack()
, commit()
und various transaction stuff gesucht, aber ich kann nicht finden, ob rollBack()
aufgerufen werden kann, nachdem commit()
bereits aufgerufen wurde.PHP, MySQL, PDO-Transaktion - Kann rollBack() verwendet werden, nachdem commit() aufgerufen wurde?
Die Situation ist folgende:
Ich habe zwei verschiedene Datenbanken: $dbu = new PDO(..db1..)
und $dbb = new PDO(..db2..)
Beide Datenbanken haben Tabellen, die innerhalb einer einzigen Funktion aktualisiert werden. Die Operation ist alle oder keine - entweder alle Tabellen wurden erfolgreich aktualisiert, oder keine sind.
Verwendung von zwei separaten Transaktionen, wenn die Transaktion für $dbu
erfolgreich abgeschlossen, aber die Transaktion für $dbb
ausfällt, muß ich ungeschehen machen, was in der ersten Transaktion durchgeführt wurde:
Codeblock 1
$dbu->beginTransaction();
try{
$stmt = $dbu->prepare(...);
$stmt->execute();
// stuff
$dbu->commit();
}catch(Exception $e){
// do stuff
$dbu->rollBack();
exit();
}
$dbb->beginTransaction();
try{
$stmt = $dbb->prepare(...);
$stmt->execute();
// stuff
$dbb->commit();
}catch(Exception $e){
// do stuff
$dbb->rollBack();
// Need to undo what we did
$dbu->beginTransaction();
try{
$stmt = $dbu->prepare(...);
$stmt->execute();
// opposite of whatever operation was in the first transaction
$dbu->commit();
}catch(Exception $e){
}
exit();
}
Dies ist unordentlich und unzuverlässig, wenn etwas mit der Verbindung zwischen den beiden primären Transaktionen passiert.
Also was ich stattdessen tun möchte, ist die zweite Transaktion innerhalb der ersten verschachteln. Es scheint logisch, dass ich dies tun könnte, denn $dbu
und $dbb
sind zwei eindeutige PDO-Objekte, die auf zwei separate Datenbanken verweisen. Es sieht aus wie:
Codeblock 2
$dbu->beginTransaction();
try{
$stmt = $dbu->prepare(...);
$stmt->execute();
// stuff
$dbb->beginTransaction();
try{
$stmt = $dbb->prepare(...);
$stmt->execute();
// stuff
$dbb->commit();
}catch(Exception $e){
// do stuff
$dbb->rollBack();
$dbu->rollBack(); // Since $dbu was first part of transaction, it needs to be rolled back too
exit();
}
$dbu->commit();
}catch(Exception $e){
// do stuff
$dbu->rollBack();
$dbb->rollBack(); // **THIS IS THE TRICKY LINE!**
exit();
}
Seit commit()
für $dbu
nach die gesamte $dbb
Transaktion aufgerufen wird, kann der Fall eintreten, wo $dbb
erfolgreich war, und $dbu
gescheitert. Wenn das passiert, muss ich rückgängig machen, was in der $dbb
Transaktion getan wurde.
So ...
Kann ich nennen $dbb->rollBack();
(nahe dem Ende des Codeblock 2) NACH$dbb->commit();
gelaufen? Oder stehe ich in der gleichen Situation, in der ich ursprünglich war, wo ich manuell rückgängig machen muss, was auch immer in der $dbb
Transaktion passiert ist? Auch dies ist nicht ideal. Wenn die Verbindung in der Mitte davon fällt, könnte ich mit Daten in den $dbb
Tabellen verlassen werden, die nicht dort sein sollten (weil die $dbu
Transaktion fehlgeschlagen ist).
Vielleicht kann ich die beiden Transaktionen in einen einzigen try/catch
Block kombinieren?
Codeblock 3
$dbu->beginTransaction();
$dbb->beginTransaction();
try{
$stmt = $dbu->prepare(...);
$stmt->execute();
$stmt2 = $dbb->prepare(...);
$stmt2->execute();
// stuff
$dbu->commit();
$dbb->commit();
}catch(Exception $e){
// do stuff
$dbu->rollBack();
$dbb->rollBack(); // **THIS IS THE TRICKY LINE!**
exit();
}
Aber das sieht nicht viel anders aus als -Code Block 2, weil wir immer noch die Situation haben können, wo $dbu->commit();
erfolgreich ist, aber $dbb->commit();
ausfällt.Wenn das passiert, versuchen wir immer noch, $dbu->rollBack();
aufzurufen, nachdem sein Partner-Commit bereits verarbeitet wurde.
Wenn ich kann nicht Anruf rollBack()
nach commit()
, gibt es eine häufig verwendete Methode, um dieses 2-DB Problem zu lösen? Etwas, das so effizient ist wie rollBack()
und keine vollständige zusätzliche Transaktion erfordert, um die vorherige Operation rückgängig zu machen.
EDIT 1
Hinzufügen auf Codeblock 3, konnte ich jede Ausführung überprüfen, wie sie genannt werden?
Codeblock 4
$dbu->beginTransaction();
$dbb->beginTransaction();
try{
$stmt = $dbu->prepare(...);
if(!$stmt->execute()){
throw new Exeption('something somethign');
}
$stmt2 = $dbb->prepare(...);
if(!$stmt2->execute()){
throw new Exeption('something two');
}
// stuff
$dbu->commit();
$dbb->commit();
}catch(PDOException $e){
// do stuff
$dbu->rollBack();
$dbb->rollBack(); // **THIS IS THE TRICKY LINE!**
exit();
}catch(Exception $e){
// do stuff
$dbu->rollBack();
$dbb->rollBack(); // **THIS IS THE TRICKY LINE!**
exit();
}
Wird dies die beiden commit
Aussagen dazu beitragen sicherzustellen, haben die bestmögliche Chance auf Erfolg? Oder wirft der try/catch
Block automatisch PDOException
, bevor die benutzerdefinierten jemals aufgerufen werden? Es wäre schön, einen einfachen Bezeichner zu haben, um zu wissen, welche Transaktion fehlschlägt, im Gegensatz zu der gesamten $e->getMessage();
.
Ok, vergessen, das Gefühl mit der macht „verpflichten sich selten zum Scheitern verurteilt“, da alle die Beinarbeit getan wurde, zu diesem Zeitpunkt. Ich habe am Ende der Frage eine Bearbeitung hinzugefügt, mit ein paar Gedanken, um Probleme zu mildern, wenn Sie mir Ihre Gedanken mitteilen könnten, wenn es sich sogar lohnt hinzuzufügen. – Birrel
Ah, ich verstehe. Ja, ich habe es auf 'setAttribute (PDO :: ATTR_ERRMODE, PDO :: ERRMODE_EXCEPTION);' eingestellt, also denke ich, dass es meine benutzerdefinierten Ausnahmen nicht sehen wird. – Birrel
Wenn Sie den Fehlermodus so einstellen, dass Ausnahmen ausgelöst werden, müssen Sie jede 'prepare' /' execute' Anweisung mit ihrem eigenen 'try' /' catch' Block umgeben, um zwischen ihnen zu unterscheiden - was Sie wahrscheinlich schon erraten haben. –