” 。 $txt 。 “</p>”; //開啟新條目 $newStartTime = $stop->modify('1秒'); $entry = 新條目(); $entry->setStart( $newStartTime ); $entry->setOperator( $r->getOperator() ); $entry->setPosition( $r->getPosition() ); $entry->setStudent( $r->getStudent() ); $em->堅持($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 .= “
” 。 $txt 。 “</p>”; } 別的 { $em->flush(); //儲存到資料庫 $txt =「為」開啟了一個新條目。 $r->getOperator()->getSignature() 。 」在相同位置 (" . $r->getPosition()->getName() . ")"; $messages .= “
” 。 $txt 。 “</p>”; } } } } 返回$訊息; }</pre> <p>我甚至在這裡使用 checkOpenEntries() 運行額外的檢查,以查看此時該位置是否存在任何 stoptime == NULL 的邊界。最初,我認為這是多餘的,因為我認為如果一個請求正在資料庫上運行和操作,則另一個請求只有在第一個請求完成後才會啟動。 </p> <pre class="brush:php;toolbar:false;">private function checkOpenEntries($position,$checkRelatives = false) { $positionsToCheck = array(); if ($checkRelatives == true) { $positionsToCheck = $position->getRelatedPositions(); $positionsToCheck[] = $position; } else { $positionsToCheck = array($position); } //Get all open entries for position $repository = $this->em->getRepository('App\Entity\Poslog\Entry'); $query = $repository->createQueryBuilder('e') ->where('e.stop is NULL and e.position IN (:positions)') ->setParameter('positions', $positionsToCheck) ->getQuery(); $results = $query->getResult(); if(!isset($results[0])) { return 0; //tells caller that there are no open entries } else { if (count($results) === 1) { return $results[0]; //if exactly one open entry, return that object to caller } else { $body = 'Found more than 1 open log entry for position ' . $position->getName() . ' in ' . $position->getACRGroup()->getName() . ' this should not be possible, there appears to be corrupt data in the database.'; $this->email($body); $output['success'] = false; $output['message'] = $body . ' An automatic email has been sent to ' . $this->globalParameters->get('poslog-email-to') . ' to notify of the problem, manual inspection is required.'; $output['logdata'] = null; return $this->prepareResponse($output); } } }</pre> <p>我是否需要使用某種「鎖定資料庫」方法來啟動此功能才能實現我想要做的事情? </p> <p>我已經測試了所有功能,並且當我模擬各種狀態時(即使不應該如此,也為停止時間輸入 NULL 等),一切都正常。大多數情況下,一切都運作良好,但在月中的某一天,這種事情發生了...</p>
您永遠無法保證順序(或隱式獨佔存取)。嘗試一下,你就會把自己挖掘得越來越深。
正如Matt 和KIKO 在評論中提到的,您可以使用約束和事務,這些應該會有很大幫助,因為您的資料庫將保持乾淨,但請記住您的應用程式需要能夠捕獲資料庫層產生的錯誤。 絕對值得先嘗試。
處理此問題的另一種方法是強制資料庫/應用程式層級鎖定。
資料庫層級鎖定更粗糙,如果您在某個地方忘記釋放鎖定(在長時間運行的腳本中),則非常不可原諒。
MySQL 文件:
#鎖定整個表通常是一個壞主意,但它是可行的。這很大程度上取決於應用程式。
一些開箱即用的 ORM 支援物件版本控制,如果版本在執行過程中發生更改,則會拋出異常。理論上,您的應用程式會遇到異常,重試時會發現其他人已經填充了該字段,並且不再是更新的候選者。
應用程式層級鎖定更細粒度,但程式碼中的所有點都需要遵守鎖定,否則,您將回到方#1。如果您的應用程式是分散式的(例如 K8S,或只是部署在多個伺服器上),那麼您的鎖定機制也必須是分散式的(不是實例本地的)