在高负载下(进行负载测试以测试结帐流程),看来我们的自定义订单控制器在付款时会引发异常。这大约发生在9个订单中的1个:
RaceConditionException: "Operated entity was previously modified."
我们已按如下方式覆盖了sylius_shop_checkout_complete路线
sylius_shop_checkout_complete:
path: /complete
methods: [GET, PUT]
defaults:
_controller: sylius.controller.order:completeSectionAction
_sylius:
event: complete
flash: false
template: "@SyliusShop/Checkout/complete.html.twig"
repository:
method: findCartForSummary
arguments:
- "expr:service('sylius.context.cart').getCart().getId()"
state_machine:
graph: sylius_order_checkout
transition: complete
redirect:
route: sylius_shop_order_pay
parameters:
tokenValue: resource.tokenValue
form:
type: Sylius\Bundle\CoreBundle\Form\Type\Checkout\CompleteType
options:
validation_groups: 'sylius_checkout_complete'
还有我们自己的支票付款状态路线,PaymentGateway会在付款后重定向到该路线,如下所示:
sylius_shop_checkout_status:
path: /check-status
methods: [GET, PUT]
defaults:
_controller: sylius.controller.order:checkStatusAction
_sylius:
event: complete
flash: false
template: "@SyliusShop/Checkout/complete.html.twig"
repository:
method: findCartForSummary
arguments:
- "expr:service('sylius.context.cart').getCart().getId()"
state_machine:
graph: sylius_order_checkout
transition: complete
redirect:
route: sylius_shop_order_pay
parameters:
tokenValue: resource.tokenValue
form:
type: Sylius\Bundle\CoreBundle\Form\Type\Checkout\CompleteType
options:
validation_groups: 'sylius_checkout_complete'
在checkStatusAction函数中,我们得到以下异常
request.CRITICAL: Uncaught PHP Exception Sylius\Component\Resource\Exception\RaceConditionException: "Operated entity was previously modified." at /srv/sylius/vendor/sylius/sylius/src/Sylius/Bundle/CoreBundle/Doctrine/ORM/Handler/ResourceUpdateHandler.php line 46 {"exception":"[object] (Sylius\\Component\\Resource\\Exception\\RaceConditionException(code: 0): Operated entity was previously modified. at /srv/sylius/vendor/sylius/sylius/src/Sylius/Bundle/CoreBundle/Doctrine/ORM/Handler/ResourceUpdateHandler.php:46, Doctrine\\ORM\\OptimisticLockException(code: 0): The optimistic lock on an entity failed. at /srv/sylius/vendor/doctrine/orm/lib/Doctrine/ORM/OptimisticLockException.php:64)"} []
这是我们的checkStatus函数:
public function checkStatusAction(Request $request, LoggerInterface $logger): Response
{
$configuration = $this->requestConfigurationFactory->create($this->metadata, $request);
$this->isGrantedOr403($configuration, ResourceActions::UPDATE);
$resource = $this->findOr404($configuration);
$form = $this->resourceFormFactory->create($configuration, $resource);
$payment = $resource->getLastPayment();
$paymentMethod = $payment->getMethod();
$paymentConfig = $paymentMethod->getGatewayConfig()->getConfig();
$paymentFactoryName = $paymentMethod->getGatewayConfig()->getFactoryName();
$response = $this->checkPaymentStatus($request->get('resourcePath'), $resource, $paymentConfig);
$decodedResponse = json_decode($response, true);
if(substr($decodedResponse['result']['code'], 0, 4 ) === '000.') {
// If need to run scheduler
$tcResponse = null;
if ($paymentFactoryName === 'tp_payment_subscription' || $paymentFactoryName === 'tp_bank_transfer') {
$amount = $paymentFactoryName === 'tp_bank_transfer' ? 0.00 : number_format($resource->getTotal() / 100, 2, '.', '');
$tcResponse = $this->createSubscription($resource, $response, $amount, $paymentConfig);
}
// Store payment details
$paymentDetails = [
'PayOn' => $response,
'TotalControl' => $tcResponse
];
$payment->setDetails($paymentDetails);
$payment->setState(Payment::STATE_COMPLETED);
try {
$this->resourceUpdateHandler->handle($resource, $configuration, $this->manager);
} catch (UpdateHandlingException $exception) {
if (!$configuration->isHtmlRequest()) {
return $this->viewHandler->handle(
$configuration,
View::create($form, $exception->getApiResponseCode())
);
}
$this->flashHelper->addErrorFlash($configuration, $exception->getFlash());
return $this->redirectHandler->redirectToReferer($configuration);
}
// if ($configuration->isHtmlRequest()) {
// $this->flashHelper->addSuccessFlash($configuration, ResourceActions::UPDATE, $resource);
// }
$postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE, $configuration, $resource);
return $this->redirectHandler->redirectToResource($configuration, $resource);
}
$this->flashHelper->addErrorFlash($configuration, 'something_went_wrong_error');
$flashes = $request->getSession()->getBag('flashes');
$flashes->add('error', [
'message' => 'sylius.totalp.payment_failure',
'parameters' => ['hello' => 'hi']
]);
return $this->redirectHandler->redirectToReferer($configuration, $resource);
}
该错误特别发生在以下行:
$this->resourceUpdateHandler->handle($resource, $configuration, $this->manager);
仅当网站同时执行许多订单时才会发生此问题,有人知道如何解决此问题吗?
我已经设法解决了这个问题,如果有人遇到此问题,Sylius订单号生成器似乎并不是为繁重的并发负载而构建的。
特别是SequentialOrderNumberGenerator.php。
我们更改了以下内容(为了清楚起见,我已保留在注释的代码中)
/**
* {@inheritdoc}
*/
public function generate(OrderInterface $order): string
{
//$sequence = $this->getSequence();
//$this->sequenceManager->lock($sequence, LockMode::OPTIMISTIC, $sequence->getVersion());
$number = $this->generateNumber($order);
//$sequence->incrementIndex();
return $number;
}
private function generateNumber($order): string
{
//$number = $this->startNumber + $index;
return str_pad((string) $order->getId(), $this->numberLength, '0', \STR_PAD_LEFT);
}