PHP と MySQL は同時リクエストをどのように処理しますか?
<p>PHP/Symfony が同時リクエストを処理する方法、あるいはおそらくデータベース上で潜在的な同時クエリを処理する方法について何かが欠けているはずです...</p>
<p>このコードは不可能を実行しているようです。ランダムに (月に 1 回程度) 新しいエンティティのコピーが下部に作成されます。私の結論は、2 つのクライアントが同じリクエストを 2 回実行し、両方のスレッドが同時に SELECT クエリを実行し、stop == NULL を持つエントリを選択し、その後、両方が (?) そのエントリの停止時間を設定すると、これが発生した場合、両方とも新しいエントリを作成する必要があります。</p>
<p>私の知る限り、これが私の論理的な概要です:</p>
<ol>
<li>終了時刻が NULL のエントリをすべて取得します</li>
<li>エントリをループします</li>
<li>入力日付 (UTC) が現在の日付 (UTC) と異なる場合のみ続行します</li>
<li>オープンエントリの停止時間を 23:59:59 に設定し、データベースにフラッシュします</li>
<li>翌日の 00:00:00 から新しいエントリを作成します</li>
<li>この場所に他に開いているエントリがないことをアサートします</li>
<li>この場所には今後エントリが存在しないことをアサートします</li>
<li>これのみ - 新しいエントリをデータベースにフラッシュします</li>
</ol>
<p>コントローラーの電源が自動的にオフになり、自動的にオンになります</p>
<pre class="brush:php;toolbar:false;">//エントリが夜明け(真夜中)にまたがる場合は、エントリを閉じて、次の日の初めに新しいエントリを開きます
プライベート関数 autocloseAndOpen($units) {
$now = new \DateTime("now", new \DateTimeZone("UTC"));
$repository = $this->em->getRepository('App\Entity\Poslog\Entry');
$query = $repository->createQueryBuilder('e')
->where('e.stop は NULL')
->getQuery();
$results = $query->getResult();
if (!isset($results[0])) {
return null; //開いているエントリがまったくありません
}$em = $this->em;
$messages = "";
foreach ($results as $r) {
if ($r->getPosition()->getACRGroup() == $unit) { //ユーザー自身のエントリのみをタッチします
$start = $r->getStart();
//日付区切りにまたがるエントリをアサートします
$startStr = $start->format("Y-m-d"); //比較に必要です。 $start->format("Y-m-d") が比較句に置かれている場合、PHP はフォーマットの出力ではなく、フォーマットされている datetime オブジェクトを比較します。
$nowStr = $now->format("Y-m-d"); //比較に必要です。 $start->format("Y-m-d") が比較句に置かれている場合、PHP はフォーマットの出力ではなく、フォーマットされている datetime オブジェクトを比較します。
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() 。 「日付区切り (UTC) にわたる位置 (" . $r->getPosition()->getName() . ") にエントリがありました。 「」で自動的に閉じられます。 。 $stop->format("Y-m-d H:i:s") 。 「z。」;
$messages .= "<p>" 。 $txt 。 "</p";
// 新しいエントリを開く
$newStartTime = $stop->modify(' 1 秒');
$entry = 新しいエントリ();
$entry->setStart( $newStartTime );
$entry->setOperator( $r->getOperator() );
$entry->setPosition( $r->getPosition() );
$entry->setStudent( $r->getStudent() );
$em->persist($entry);
//新しいエントリを自動で開く前に、将来のエントリがないことをアサートします
$futureE = $this->checkFutureEntries($r->getPosition(),true);
$openE = $this->checkOpenEntries($r->getPosition(), true);
if ($futureE !== 0 || $openE !== 0) {
$txt = "" の新しいエントリを開こうとしました。 $r->getOperator()->getSignature() 。 「翌日同じ位置 (" . $r->getPosition()->getName() . ") にありますが、競合するエントリがあります。";
$messages .= "<p>" 。 $txt 。 "</p";
} それ以外 {
$em->flush(); //DBに保存
$txt = "新しいエントリが開かれました" 。 $r->getOperator()->getSignature() 。 「同じ位置 (" . $r->getPosition()->getName() . ")";
$messages .= "<p>" 。 $txt 。 "</p";
}
}
}
}
$messages を返します。
}</pre>
<p>ここでは、 checkOpenEntries() を使用して追加の検査を実行し、現時点でその位置に stoptime == NULL のストリーム項目が存在するかどうかを確認します。当初、私はこれが冗長であると考えていました。なぜなら、1 つのリクエストがデータベース上で実行および操作されている場合、最初のリクエストが完了するまで別のリクエストは開始されないと考えたからです。 </p>
<pre class="brush:php;toolbar:false;">プライベート関数 checkOpenEntries($position,$checkRelatives = false) {
$positionsToCheck = 配列();
if ($checkRelatives == true) {
$positionsToCheck = $position->getManyPositions();
$positionsToCheck[] = $position;
} それ以外 {
$positionsToCheck = 配列($position);
}
//ポジションのオープンエントリーをすべて取得します
$repository = $this->em->getRepository('App\Entity\Poslog\Entry');
$query = $repository->createQueryBuilder('e')
->where('e.stop は NULL で、e.position IN (:positions)')
->setParameter('positions', $positionsToCheck)
->getQuery();
$results = $query->getResult();
if(!isset($results[0])) {
return 0; //呼び出し元に開いているエントリがないことを通知します
} それ以外 {
if (count($results) === 1) {
return $results[0]; // 開いているエントリが 1 つだけの場合、そのオブジェクトを呼び出し元に返す
} それ以外 {
$body = '位置 ' . $position->getName() . ' で 1 つ以上の開いているログ エントリが見つかりました ' . $position->getACRGroup()->getName() . ' これは不可能であるはずですが、データベースに破損したデータがあるようです。';
$this->email($body);
$output['success'] = false;
$output['message'] = $body . ' 問題を通知する自動電子メールが ' . $this->globalParameters->get('poslog-email-to') . ' に送信されました。手動検査が必要です。';
$output['logdata'] = null;
return $this->prepareResponse($output);
}
}
}</pre>
<p>この機能を有効にしてやりたいことを実現するには、何らかの「データベースのロック」メソッドを使用する必要がありますか? </p>
<p>すべての機能をテストしましたが、さまざまな状態をシミュレートしたとき (停止時間に NULL を入力するなど、本来であるべきでない場合でも)、すべてが正常に動作しました。ほとんどの場合、すべてがうまくいきますが、月中旬のある日、このようなことが起こります...</p>