我需要做一些基本的事情,我有兩個物體:用戶和操作。每個用戶都有管理員分配的 X 個令牌,然后他可以根據令牌的數量執行 Y 個操作。因此,假設一個用戶只有足夠的令牌來執行一個操作,我確定如果我在同一時間同時執行多個請求(例如同時執行 5 個或更多請求)。用戶執行兩個或多個操作而不是一個操作(并且僅在解釋的場景中,其余一切正常)
與我的解釋相關的代碼:
public function useractions(Requests $request){
$user = $this->getUser();
$post = Request::createFromGlobals();
if($post->request->has('new_action') && $this->isCsrfTokenValid("mycsrf", $post->request->get('csrf_token'))) {
$entityManager = $this->getDoctrine()->getManager();
$tokens = $user->getTokens();
if($tokens<1){
$error = "Not enough tokens";
}
if(empty($error)){
$user->setTokens($tokens-1);
$entityManager->flush();
$action = new Action();
$action->setUser($user);
$entityManager->persist($transaction);
$entityManager->flush();
}
}
}
我使用 mariadb 10.5.12 和 InnoDB 作為引擎
顯然,我在代碼中犯了一個大錯誤,或者在 Symfony 或 Doctrine 配置中遺漏了一些東西。有人可以告訴我錯誤嗎?謝謝
uj5u.com熱心網友回復:
對于一個更新命令的執行,你應該使用flush一次,而不是在每次迭代中。
此外,您可以使用事務將所有這些作為一個單元來完成。
但是如果你想防止并發更新,你應該使用版本控制!這樣,當學說想要更新某些內容時,請檢查版本控制,如果它發生更改,則會拋出錯誤并阻止將資料持久化到資料庫(您可以通過偵聽器處理此錯誤),這樣只有第一個請求才會成功。
例如,我將 updatedAt 標記為已歸檔的版本,并將在每個持久性中進行檢查。
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Version
* @ORM\Column(type="datetime",options={"default": "CURRENT_TIMESTAMP"})
*/
private $UpdatedAt;
uj5u.com熱心網友回復:
您可能在用戶SELECT(用戶重繪 ,由您的身份驗證機制呼叫)和您的UPDATE(第一個$entityManager->flush();)之間存在競爭條件。
Doctrine 有一個內置的方法來管理并發請求,EntityManager::transactional()方法 ( doc )。您可能只需要將代碼包裝到其中:
$entityManager = $this->getDoctrine()->getManager();
$error = $entityManager->transactional(function($entityManager) use ($user) {
// Required to perform a blocking SELECT for UPDATE
$entityManager->refresh($user);
$tokens = $user->getTokens();
if($tokens<1){
return "Not enough tokens";
}
$user->setTokens($tokens-1);
$entityManager->persist($user);
});
if (empty($error)) {
$action = new Action();
$action->setUser($user);
$entityManager->persist($action);
$entityManager->flush();
}
注意:確保您的事務足夠快,因為它在 mariadb 端有阻塞效應,影響涉及的行。
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/368821.html
