Bagaimanakah PHP dan MySQL mengendalikan permintaan serentak?
<p>Saya mesti kehilangan sesuatu tentang cara PHP/Symfony mengendalikan permintaan serentak, atau mungkin cara mengendalikan pertanyaan serentak yang berpotensi pada pangkalan data...</p>
<p>Kod ini nampaknya melakukan perkara yang mustahil - ia secara rawak (kira-kira sebulan sekali) mencipta salinan entiti baharu di bahagian bawah. Kesimpulan saya ialah apabila dua pelanggan membuat permintaan yang sama dua kali, dan kedua-dua utas melaksanakan pertanyaan SELECT pada masa yang sama, pilih entri dengan berhenti == NULL, dan kemudian mereka berdua (?) menetapkan masa berhenti entri itu, ia mesti Apabila ini berlaku, mereka berdua menulis entri baru.</p>
<p>Setahu saya, ini adalah rangka logik saya: </p>
<ol>
<li>Dapatkan semua penyertaan dengan NULL masa berhenti</li>
<li>Gelung melalui entri</li>
<li>Teruskan hanya jika tarikh input (UTC) berbeza daripada tarikh semasa (UTC)</li>
<li>Tetapkan masa berhenti untuk entri terbuka kepada 23:59:59 dan siram ke pangkalan data</li>
<li>Bina entri baharu bermula jam 00:00:00 keesokan harinya</li>
<li>Tegaskan bahawa tiada entri terbuka lain di lokasi ini</li>
<li>Tegaskan bahawa tiada entri akan datang di lokasi ini</li>
<li>Hanya ini - siram entri baharu ke pangkalan data</li>
</ol>
<p>Pengawal dimatikan dan dihidupkan secara automatik</p>
<pre class="brush:php;toolbar:false;">//jika entri menjangkau waktu subuh (tengah malam) tutupnya dan buka entri baharu pada permulaan hari berikutnya
fungsi peribadi autocloseAndOpen($units) {
$now = DateTime baharu("sekarang", DateTimeZone baharu("UTC"));
$repository = $this->em->getRepository('AppEntityPoslogEntry');
$query = $repository->createQueryBuilder('e')
->di mana('e.stop is NULL')
->getQuery();
$results = $query->getResult();
if (!isset($results[0])) {
return null; //tiada entri terbuka sama sekali
}$em = $ini->em;
$mesej = "";
foreach ($hasil sebagai $r) {
if ($r->getPosition()->getACRGroup() == $unit) { //hanya sentuh entri pengguna sendiri
$start = $r->getStart();
//Tegaskan entri yang merangkumi pecah tarikh
$startStr = $start->format("Y-m-d"); //Diperlukan untuk perbandingan, jika $start->format("Y-m-d") diletakkan dalam klausa perbandingan PHP masih akan membandingkan objek datetime yang sedang diformat, bukan output pemformatan.
$nowStr = $now->format("Y-m-d"); //Diperlukan untuk perbandingan, jika $start->format("Y-m-d") diletakkan dalam klausa perbandingan PHP masih akan membandingkan objek datetime yang sedang diformat, bukan output pemformatan.
if ($startStr < $nowStr) {
$stop = DateTimeImmutable baharu($start->format("Y-m-d")."23:59:59", DateTimeZone baharu("UTC"));
$r->setStop($stop);
$em->flush();
$txt = $unit->getName() . " mempunyai entri dalam kedudukan (" . $r->getPosition()->getName() . ") merangkumi tarikh pecah (UTC). Ditutup secara automatik pada " . $stop->format("Y-m-d H:i:s") . "z.";
$mesej .= "<p>" . $txt . "</p>";
//Buka entri baharu
$newStartTime = $stop->modify('+1 saat');
$entry = new Entry();
$entry->setStart( $newStartTime );
$entry->setOperator( $r->getOperator() );
$entry->setPosition( $r->getPosition() );
$entry->setStudent( $r->getStudent() );
$em->berterusan($entry);
//Tegaskan bahawa tiada entri akan datang sebelum membuka automatik entri baharu
$futureE = $this->checkFutureEntries($r->getPosition(),true);
$openE = $this->checkOpenEntries($r->getPosition(), true);
jika ($futureE !== 0 || $openE !== 0) {
$txt = "Cuba membuka entri baharu untuk " . $r->getOperator()->getSignature() . " dalam kedudukan yang sama (" . $r->getPosition()->getName() . ") pada hari berikutnya tetapi terdapat entri yang bercanggah.";
$mesej .= "<p>" . $txt . "</p>";
}lain {
$em->flush(); //simpan ke DB
$txt = "Entri baharu telah dibuka untuk " $r->getOperator()->getSignature() . getName() ")"
$messages .= "<p>"
}
}
}
}
kembalikan $mesej;
}</pre>
<p>Saya juga menggunakan checkOpenEntries() di sini untuk menjalankan semakan tambahan untuk melihat sama ada terdapat sebarang entri dengan masa henti == NULL di lokasi itu pada masa ini. Pada mulanya, saya fikir ini berlebihan kerana saya fikir jika satu permintaan berjalan dan beroperasi pada pangkalan data, permintaan lain tidak akan bermula sehingga permintaan pertama selesai. </p>
<pre class="brush:php;toolbar:false;">semak fungsi peribadiOpenEntries($position,$checkRelatives = false) {
$positionsToCheck = array();
if ($checkRelatives == benar) {
$positionsToCheck = $position->getRelatedPositions();
$positionsToCheck[] = $position;
} lain {
$positionsToCheck = array($position);
}
//Dapatkan semua penyertaan terbuka untuk jawatan
$repository = $this->em->getRepository('AppEntityPoslogEntry');
$query = $repository->createQueryBuilder('e')
->di mana('e.stop ialah NULL dan e.position IN (:positions)')
->setParameter('positions', $positionsToCheck)
->getQuery();
$results = $query->getResult();
if(!isset($results[0])) {
return 0; //memberitahu pemanggil bahawa tiada entri terbuka
} lain {
if (count($results) === 1) {
return $results[0]; //jika betul-betul satu entri terbuka, kembalikan objek itu kepada pemanggil
} lain {
$body = 'Mendapati lebih daripada 1 entri log terbuka untuk kedudukan ' $position->getName() ' dalam ' $position->getACRGroup()->getName() . nampaknya terdapat data yang rosak dalam pangkalan data.';
$this->email($body);
$output['success'] = palsu;
$output['message'] = $body . E-mel automatik telah dihantar ke ' $this->globalParameters->get('poslog-email-to') adalah diperlukan.';
$output['logdata'] = null;
kembalikan $this->prepareResponse($output);
}
}
}</pre>
<p>Adakah saya perlu menggunakan beberapa jenis kaedah "pangkalan data kunci" untuk membolehkan fungsi ini mencapai apa yang saya mahu lakukan? </p>
<p>Saya telah menguji semua fungsi dan apabila saya mensimulasikan pelbagai keadaan (memasukkan NULL untuk masa henti, dsb., walaupun tidak sepatutnya), semuanya berfungsi dengan baik. Selalunya, semuanya berfungsi dengan baik, tetapi satu hari pada pertengahan bulan, ini berlaku... </p>