• Hallo liebe Userinnen und User,

    nach bereits längeren Planungen und Vorbereitungen sind wir nun von vBulletin auf Xenforo umgestiegen. Die Umstellung musste leider aufgrund der Serverprobleme der letzten Tage notgedrungen vorverlegt werden. Das neue Forum ist soweit voll funktionsfähig, allerdings sind noch nicht alle der gewohnten Funktionen vorhanden. Nach Möglichkeit werden wir sie in den nächsten Wochen nachrüsten. Dafür sollte es nun einige der Probleme lösen, die wir in den letzten Tagen, Wochen und Monaten hatten. Auch der Server ist nun potenter als bei unserem alten Hoster, wodurch wir nun langfristig den Tank mit Bytes vollgetankt haben.

    Anfangs mag die neue Boardsoftware etwas ungewohnt sein, aber man findet sich recht schnell ein. Wir wissen, dass ihr alle Gewohnheitstiere seid, aber gebt dem neuen Board eine Chance.
    Sollte etwas der neuen oder auch gewohnten Funktionen unklar sein, könnt ihr den "Wo issn da der Button zu"-Thread im Feedback nutzen. Bugs meldet ihr bitte im Bugtracker, es wird sicher welche geben die uns noch nicht aufgefallen sind. Ich werde das dann versuchen, halbwegs im Startbeitrag übersichtlich zu halten, was an Arbeit noch aussteht.

    Neu ist, dass die Boardsoftware deutlich besser für Mobiltelefone und diverse Endgeräte geeignet ist und nun auch im mobilen Style alle Funktionen verfügbar sind. Am Desktop findet ihr oben rechts sowohl den Umschalter zwischen hellem und dunklem Style. Am Handy ist der Hell-/Dunkelschalter am Ende der Seite. Damit sollte zukünftig jeder sein Board so konfigurieren können, wie es ihm am liebsten ist.


    Die restlichen Funktionen sollten eigentlich soweit wie gewohnt funktionieren. Einfach mal ein wenig damit spielen oder bei Unklarheiten im Thread nachfragen. Viel Spaß im ngb 2.0.

Problem mit MySQL-Transaktion / PHP + PDO

X-Coder

Aktiver NGBler

Registriert
14 Juli 2013
Beiträge
149
Ich bin gerade mit meinem Latein am Ende und könnte etwas Hilfe gebrauchen.

Hat vielleicht jemand eine Idee warum es beim Commit in Zeile 50 nur gelegentlich zu dem PDO-Fehler: "There is no active transaction" kommt?
Ich starte diese Transaktion doch zuvor in Zeile 7, und beende diese dazwischen nicht mehr. Ich kann darin keinen Fehler entdecken.
Eingesetzt wird PHP 7.

Dieser Fehler tritt nur gelegentlich auf, ich konnte bis jetzt noch nicht nachstellen wann und warum. Er zeigt sich auch nur im Produktiv-System, nicht bei der Entwicklung.

Grundlegend versuche ich mehrere CSV-Dateien einzulesen, nehme vor dem Import in die Datenbank noch diverse Prüfungen vor, und importiere diese dann in eine MySQL-Datenbank.

Jede CSV-Datei wird als ein Auftragsjob behandelt, bei dem Datensätze mit Fehlern aber einfach übersprungen werden sollen und nur solche importiert werden sollen, welche auch allen Kriterien erfüllen und importierbar sind.

Ein Rollback soll nur durchgeführt werden, wenn gar keine Datensätze in die job_detail-Tablle importiert worden sind, damit der zuerst angelegt Job aus der job-Tabelle auch wieder gelöscht wird.


Könnte es sein, dass die Transaktion nicht durch mein Skript unterbrochen wird, sondern durch ein datenbankseitigen Timeout oder so etwas?
In dieser Zeit finden auch keine externen ALTER-Table Anweisungen statt, welche Transaktionen abbrechen lassen würden.

Der Code ist sehr gekürzt, ich habe weitgehendst nur die Kontrollstrukturen darin gelassen.
[src=php]<?php
try {
#$cfg->db // = Wrapper für PDO
$cntRowsInserted = 0; // Zähler für erfolgreiche Inserts

$autocommit = $cfg->db->get_autocommit(); // aktuellen Transaktionsmodus sichern (für verschachtelte Transaktionen)
$cfg->db->autocommit(false); // Autocommit deaktivieren und Transaktion starten

if ($hasError) {
throw new Exception('Fehlermeldung');
}

// Job anlegen
$stmt = $LEVEO->db->prepare('INSERT INTO jobs ...');
$stmt->execute();

// Zeilen einlesen und in Datenbank abspeichern
while ($row = ...) {
try {
if ($hasError) {
throw new Exception('Fehlermeldung');
}

// Query ausführen
try {
$stmt = $cfg->db->query('INSERT INTO job_details ...'); //löst Exception aus bei Fehlern
$cntRowsInserted++; //Wenn OK, dann erhöhen
}
catch (Exception $e) {
throw $e;
}
}
catch (Exception $ex) {
// - alle Exception abfangen, fehlerhaften Datensatz überspringen
// - mit anderen Datensätzen fortfahren
// - Fehlermeldung protokollieren
#fputcsv(...);
}
}

//Alles Datensätze des Jobs verarbeitet? - dann Job speichern
if ($cntRowsInserted > 0) { // Es wurde mindestens ein Datensatz erfolgreich verarbeitet
// ganzen Job zur Weiterverarbeitung freigeben
if ($autoFreigabe) {
$stmtJobFreigabe->bindValue(':job_id', $job_id);
$stmtJobFreigabe->execute();
}

// Auftragsjob speichern
$cfg->db->commit(); //<------------------ There is no active transaction!
$cfg->db->autocommit($autocommit); // Transaktionsmodus wiederherstellen
} else {
// Sollten keine Datensätze importiert worden sein, dann Job durch Rollback löschen
$cfg->db->rollback();
}
}
//Fehler?
catch (Exception $ex) {
// Sonstige unbehandelte Fehler speichern
$error = $ex->getMessage();

if (empty($DEBUG)) {
// Fehler-Details mailen
}

try {
// alle Änderungen an der Datenbank zurücknehmen
$cfg->db->rollback();
$cfg->db->autocommit($autocommit); // Transaktionsmodus wiederherstellen
}
catch (Exception $ex2) {
if (!empty($DEBUG)) {
// Fehlermeldung an Debugoutput anhängen
$error .= "\r\n" . $ex2->getMessage();
}
}
}
?>
[/src]
 
Zuletzt bearbeitet:

X-Coder

Aktiver NGBler

Registriert
14 Juli 2013
Beiträge
149
  • Thread Starter Thread Starter
  • #3
Das fehlt nicht, du konntest es nur nicht sehen. Das wird im Wrapper aufgerufen und passiert zusammen mit dem autocommit(false)-Aufruf in Zeile 7, in dieser Funktion wird direkt $this->link->beginTransaction() der PDO-Klasse aufgerufen.

Das ist noch der Code vom Wrapper:
[src=php]
private $autocommit = true;
private $transactionLevel = 0;

function autocommit($b_mode){
if(!$b_mode && $this->transactionLevel == 0){
$transaction_started = $this->link->beginTransaction();
if($transaction_started){
$this->transactionLevel++;
$this->autocommit = false;
}
return $transaction_started;
}
}

function commit(){
$commited = false;
if($this->transactionLevel>0){
$this->transactionLevel--;
}
if($this->transactionLevel==0){
$commited = $this->link->commit();
if($commited) $this->autocommit = true;
}
return $commited;
}[/src]

Das Transaktions-Level sorgt dafür dass nur der letzte commit-Aufruf (wenn $transactionLevel==0) auch einen tatsächlichen commit auf Datenbankebene erzeugt. Dies benötige ich für die verschachtelten Transaktionen, da das ganze relativ modular aufgebaut ist.

EDIT: Mir ist da gerade etwas in der autocommit-Funktion aufgefallen, dass muss ich eben mal überprüfen...
 
Zuletzt bearbeitet:

Asseon

Draic Kin

Registriert
14 Juli 2013
Beiträge
10.353
Ort
Arcadia
MySQL kann keine "Nested Transcations", sobald transactionLevel > 1 und versucht wird eine "äußere" Transaktion zu commiten tritt vermutlich der Fehler auf, da jedes "START TRANSACTION" schwebende, also die vorherige, Transaktionen implizit commited.

Das geht eindeutig aus der mysql dokumentation(such nach pending) seit mindestens MySQL 5.5 hervor.

Abhängig vom exakten use case ließe sich das allerdings mittels Savepoints umsetzen.
 
Zuletzt bearbeitet:

X-Coder

Aktiver NGBler

Registriert
14 Juli 2013
Beiträge
149
  • Thread Starter Thread Starter
  • #5
Genau das war mein Problem mit MySQL und den nicht unterstützten nested Transactions. Das ist quasi mein kleiner Workaround für das von dir beschriebene Problem.

Die "inneren" beginTransactions und commits sollten mit diesem Workaround tatsächlich auch nie auf Datenbankebene durchgeführt werden, sondern nur der commit auf der äußersten Ebene, wenn dass Transaktions-Level also wieder bei 0 ist.

Aber ich habe gerade vorhin bemerkt, dass ich das $transactionLevel bei mehrfachen Aufrufen von autocommit() gar nicht mehr das Level erhöhe, dass kann so dann auch nicht funktionieren. Eigentlich sollte jeder Aufruf von autocommit(false) dafür sorgen, dass das Level erhöht wird und bei jedem commit-Aufruf wieder abgezogen wird.
 
Zuletzt bearbeitet:
Oben