Wie gehen PHP und MySQL mit gleichzeitigen Anfragen um?
<p>Mir muss etwas darüber entgangen sein, wie PHP/Symfony gleichzeitige Anfragen verarbeitet, oder vielleicht, wie potenzielle gleichzeitige Abfragen in der Datenbank behandelt werden ...</p>
<p>Dieser Code scheint das Unmögliche zu leisten – er erstellt zufällig (etwa einmal im Monat) eine Kopie der neuen Entität unten. Meine Schlussfolgerung ist, dass, wenn zwei Clients zweimal dieselbe Anfrage stellen und beide Threads gleichzeitig eine SELECT-Abfrage ausführen, ein Eintrag mit stop == NULL ausgewählt wird und dann beide (?) die Stoppzeit dieses Eintrags festlegen Wenn dies geschieht, schreiben beide einen neuen Eintrag.</p>
<p>Soweit ich weiß, ist dies meine logische Gliederung: </p>
<ol>
<li>Alle Einträge mit NULL-Stoppzeit abrufen</li>
<li>Durchlaufen Sie die Einträge</li>
<li>Nur fortfahren, wenn das Eingabedatum (UTC) vom aktuellen Datum (UTC) abweicht</li>
<li>Stellen Sie die Stoppzeit für offene Einträge auf 23:59:59 ein und leeren Sie sie in die Datenbank</li>
<li>Neuen Eintrag erstellen, beginnend um 00:00:00 Uhr am nächsten Tag</li>
<li>Stellen Sie sicher, dass an diesem Standort keine weiteren offenen Einträge vorhanden sind</li>
<li>Stellen Sie sicher, dass an diesem Ort keine zukünftigen Einträge vorhanden sind</li>
<li>Nur dies – neue Einträge in die Datenbank löschen</li>
</ol>
<p>Controller schaltet sich automatisch aus und ein</p>
<pre class="brush:php;toolbar:false;">//Wenn der Eintrag über den Tagesanbruch (Mitternacht) hinausgeht, schließen Sie ihn und öffnen Sie einen neuen Eintrag zu Beginn des nächsten Tages
private Funktion autocloseAndOpen($units) {
$now = new DateTime("now", new DateTimeZone("UTC"));
$repository = $this->em->getRepository('AppEntityPoslogEntry');
$query = $repository->createQueryBuilder('e')
->where('e.stop ist NULL')
->getQuery();
$results = $query->getResult();
if (!isset($results[0])) {
return null; //es sind überhaupt keine offenen Einträge vorhanden
}$em = $this->em;
$messages = "";
foreach ($results as $r) {
if ($r->getPosition()->getACRGroup() == $unit) { //nur die eigenen Einträge des Benutzers berühren
$start = $r->getStart();
//Eintrag über den Datumsbruch hinweg geltend machen
$startStr = $start->format("Y-m-d"); //Für den Vergleich erforderlich, wenn $start->format("Y-m-d") in die Vergleichsklausel eingefügt wird, vergleicht PHP weiterhin das zu formatierende Datetime-Objekt, nicht die Ausgabe der Formatierung.
$nowStr = $now->format("Y-m-d"); //Für den Vergleich erforderlich, wenn $start->format("Y-m-d") in die Vergleichsklausel eingefügt wird, vergleicht PHP weiterhin das zu formatierende Datetime-Objekt, nicht die Ausgabe der Formatierung.
if ($startStr < $nowStr) {
$stop = new DateTimeImmutable($start->format("Y-m-d")."23:59:59", new DateTimeZone("UTC"));
$r->setStop($stop);
$em->flush();
$txt = $unit->getName() . " hatte einen Eintrag in Position (" . $r->getPosition()->getName() . "), der sich über den Datumswechsel (UTC) erstreckte. Automatisch geschlossen bei " . $stop->format("Y-m-d H:i:s") . „z.“;
$messages .= "<p>" . $txt . "</p>";
//Neuen Eintrag öffnen
$newStartTime = $stop->modify('+1 Sekunde');
$entry = neuer Eintrag();
$entry->setStart( $newStartTime );
$entry->setOperator( $r->getOperator() );
$entry->setPosition( $r->getPosition() );
$entry->setStudent( $r->getStudent() );
$em->persist($entry);
//Stellen Sie sicher, dass keine zukünftigen Einträge vorhanden sind, bevor ein neuer Eintrag automatisch geöffnet wird
$futureE = $this->checkFutureEntries($r->getPosition(),true);
$openE = $this->checkOpenEntries($r->getPosition(), true);
if ($futureE !== 0 || $openE !== 0) {
$txt = "Es wurde versucht, einen neuen Eintrag für " zu öffnen. . $r->getOperator()->getSignature() . " an der gleichen Position (" . $r->getPosition()->getName() . ") am nächsten Tag, aber es gibt widersprüchliche Einträge.";
$messages .= "<p>" . $txt . "</p>";
}anders {
$em->flush(); //in DB speichern
$txt = "Ein neuer Eintrag wurde für " $r->getOperator()->getSignature() geöffnet. getName() . ")"
$messages .= "<p>"
}
}
}
}
$messages zurückgeben;
}</pre>
<p>Ich habe hier sogar checkOpenEntries() verwendet, um eine zusätzliche Prüfung durchzuführen, um zu sehen, ob zu diesem Zeitpunkt an dieser Stelle Einträge mit stoptime == NULL vorhanden sind. Anfangs hielt ich das für überflüssig, weil ich dachte, dass, wenn eine Anfrage läuft und auf der Datenbank arbeitet, eine andere Anfrage erst starten würde, wenn die erste abgeschlossen ist. </p>
<pre class="brush:php;toolbar:false;">private Funktion checkOpenEntries($position,$checkRelatives = false) {
$positionsToCheck = array();
if ($checkRelatives == true) {
$positionsToCheck = $position->getRelatedPositions();
$positionsToCheck[] = $position;
} anders {
$positionsToCheck = array($position);
}
//Alle offenen Einträge für die Position abrufen
$repository = $this->em->getRepository('AppEntityPoslogEntry');
$query = $repository->createQueryBuilder('e')
->where('e.stop ist NULL und e.position IN (:positions)')
->setParameter('positions', $positionsToCheck)
->getQuery();
$results = $query->getResult();
if(!isset($results[0])) {
return 0; //teilt dem Aufrufer mit, dass keine offenen Einträge vorhanden sind
} anders {
if (count($results) === 1) {
return $results[0]; //wenn genau ein offener Eintrag, dieses Objekt an den Aufrufer zurückgeben
} anders {
$body = 'Mehr als 1 offener Protokolleintrag für Position ' gefunden. $position->getName() ' in ' $position->getName() '. Es scheint beschädigte Daten in der Datenbank zu geben.';
$this->email($body);
$output['success'] = false;
$output['message'] = $body . ' Eine automatische E-Mail wurde an ' $this->globalParameters->get('poslog-email-to') ' gesendet, um über das Problem zu informieren ist erforderlich.';
$output['logdata'] = null;
return $this->prepareResponse($output);
}
}
}</pre>
<p>Muss ich eine Art „Datenbank sperren“-Methode verwenden, um diese Funktionalität zu aktivieren und das zu erreichen, was ich tun möchte? </p>
<p>Ich habe alle Funktionen getestet und wenn ich verschiedene Zustände simuliere (NULL-Eingabe für Stoppzeiten usw., auch wenn dies nicht der Fall sein sollte), funktioniert alles einwandfrei. Meistens funktioniert alles gut, aber eines Tages mitten im Monat passiert so etwas ... </p>