Comment PHP et MySQL gèrent-ils les requêtes simultanées ?
<p>Il me manque quelque chose sur la façon dont PHP/Symfony gère les requêtes simultanées, ou peut-être sur la manière de gérer les requêtes simultanées potentielles sur la base de données...</p>
<p>Ce code semble faire l'impossible : il crée de manière aléatoire (environ une fois par mois) une copie de la nouvelle entité en bas. Ma conclusion est que lorsque deux clients font la même demande deux fois et que les deux threads exécutent une requête SELECT en même temps, sélectionnent une entrée avec stop == NULL, puis ils définissent tous les deux (?) l'heure d'arrêt de cette entrée, cela doivent Lorsque cela se produit, ils écrivent tous les deux une nouvelle entrée.</p>
<p>Pour autant que je sache, voici mon schéma logique : </p>
<ol>
<li>Obtenir toutes les entrées avec une heure d'arrêt NULL</li>
<li>Parcourir les entrées</li>
<li>Continuer uniquement si la date de saisie (UTC) est différente de la date actuelle (UTC)</li>
<li>Définissez l'heure d'arrêt des entrées ouvertes sur 23:59:59 et videz-les dans la base de données</li>
<li>Créer une nouvelle entrée à partir de 00:00:00 le lendemain</li>
<li>Affirmer qu'il n'y a aucune autre entrée ouverte à cet emplacement</li>
<li>Affirmer qu'il n'y a aucune entrée future à cet emplacement</li>
<li>Uniquement ceci - vider les nouvelles entrées dans la base de données</li>
</ol>
<p>Le contrôleur s'éteint et se rallume automatiquement</p>
<pre class="brush:php;toolbar:false;">//si l'entrée dure l'aube (minuit), fermez-la et ouvrez une nouvelle entrée au début du jour suivant
fonction privée autocloseAndOpen ($ unités) {
$maintenant = new DateTime("maintenant", new DateTimeZone("UTC"));
$repository = $this->em->getRepository('AppEntityPoslogEntry');
$query = $repository->createQueryBuilder('e')
->où('e.stop est NULL')
->getQuery();
$results = $query->getResult();
si (!isset($results[0])) {
return null ; //il n'y a aucune entrée ouverte
}$em = $this->em;
$messages = "";
foreach ($résultats comme $r) {
if ($r->getPosition()->getACRGroup() == $unit) { //ne touche que les propres entrées de l'utilisateur
$start = $r->getStart();
//Assert une entrée couvrant la datebreak
$startStr = $start->format("Y-m-d"); // Nécessaire pour la comparaison, si $start->format("Y-m-d") est mis dans la clause de comparaison, PHP comparera toujours l'objet datetime en cours de formatage, pas la sortie du formatage.
$nowStr = $now->format("Y-m-d"); // Nécessaire pour la comparaison, si $start->format("Y-m-d") est mis dans la clause de comparaison, PHP comparera toujours l'objet datetime en cours de formatage, pas la sortie du formatage.
si ($startStr < $nowStr) {
$stop = new DateTimeImmutable($start->format("Y-m-d")."23:59:59", new DateTimeZone("UTC"));
$r->setStop($stop);
$em->flush();
$txt = $unit->getName() . " avait une entrée en position (" . $r->getPosition()->getName() . ") couvrant la datebreak (UTC). Fermé automatiquement à " . $stop->format("Y-m-d H:i:s") . "z.";
$messages .= "<p>" . $txt . "≪/p>" ;
//Ouvrir une nouvelle entrée
$newStartTime = $stop->modify('+1 seconde');
$entrée = nouvelle entrée();
$entry->setStart( $newStartTime );
$entry->setOperator( $r->getOperator() );
$entry->setPosition( $r->getPosition() );
$entry->setStudent( $r->getStudent() );
$em->persist($entry);
// Affirmer qu'il n'y a aucune entrée future avant d'ouvrir automatiquement une nouvelle entrée
$futureE = $this->checkFutureEntries($r->getPosition(),true);
$openE = $this->checkOpenEntries($r->getPosition(), true);
si ($futureE !== 0 || $openE !== 0) {
$txt = "J'ai essayé d'ouvrir une nouvelle entrée pour " . $r->getOperator()->getSignature() . " dans la même position (" . $r->getPosition()->getName() . ") le lendemain mais il y a des entrées contradictoires.";
$messages .= "<p>" . $txt . "≪/p>" ;
}autre {
$em->flush(); //stocker dans la base de données
$txt = "Une nouvelle entrée a été ouverte pour " . $r->getOperator()->getSignature() . getName() .
$messages .= "<p>" .
}
}
}
}
renvoyer $messages ;
}</pré>
<p>J'ai même utilisé checkOpenEntries() ici pour exécuter une vérification supplémentaire pour voir s'il y a des entrées avec stoptime == NULL à cet endroit à ce moment-là. Au départ, je pensais que c'était redondant car je pensais que si une requête était en cours d'exécution et fonctionnait sur la base de données, une autre requête ne démarrerait pas tant que la première requête ne serait pas terminée. </p>
<pre class="brush:php;toolbar:false;">fonction privée checkOpenEntries($position,$checkRelatives = false) {
$positionsToCheck = tableau();
si ($checkRelatives == vrai) {
$positionsToCheck = $position->getRelatedPositions();
$positionsToCheck[] = $position;
} autre {
$positionsToCheck = tableau($position);
}
//Obtenir toutes les entrées ouvertes pour le poste
$repository = $this->em->getRepository('AppEntityPoslogEntry');
$query = $repository->createQueryBuilder('e')
->where('e.stop est NULL et e.position IN (:positions)')
->setParameter('positions', $positionsToCheck)
->getQuery();
$results = $query->getResult();
si(!isset($results[0])) {
return 0 ; // indique à l'appelant qu'il n'y a aucune entrée ouverte.
} autre {
if (count($results) === 1) {
return $results[0] ; //si exactement une entrée est ouverte, renvoie cet objet à l'appelant
} autre {
$body = 'Plus d'une entrée de journal ouverte pour la position ' . $position->getName() ' dans ' ' $position->getACRGroup()->getName() ' cela ne devrait pas être possible. il semble y avoir des données corrompues dans la base de données.';
$this->email($body);
$output['succès'] = faux ;
$output['message'] = $body ' Un e-mail automatique a été envoyé à ' $this->globalParameters->get('poslog-email-to') ' pour notifier le problème, inspection manuelle. est requis.';
$output['logdata'] = null ;
return $this->prepareResponse($output);
}
}
}</pré>
<p>Dois-je utiliser une sorte de méthode de « verrouillage de la base de données » pour activer cette fonctionnalité et réaliser ce que je veux faire ? </p>
<p>J'ai testé toutes les fonctionnalités et lorsque je simule différents états (saisie de NULL pour les heures d'arrêt, etc., même lorsque cela ne devrait pas l'être), tout fonctionne bien. La plupart du temps, tout fonctionne bien, mais un jour au milieu du mois, cela arrive... </p>