Reduce lock contention

Change-Id: I029ed7bfb75a3a3fce1245790ae9362685e09897
Signed-off-by: smarcet <smarcet@gmail.com>
This commit is contained in:
smarcet 2020-10-07 00:20:06 -03:00
parent c08bee3ce6
commit a30e62fa05
7 changed files with 59 additions and 31 deletions

View File

@ -1,4 +1,4 @@
<?php
<?php namespace libs\utils;
/**
* Copyright 2015 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
@ -11,11 +11,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
namespace libs\utils;
use Closure;
/**
* Interface ITransactionService
* @package libs\utils
@ -25,10 +21,11 @@ interface ITransactionService
/**
* Execute a Closure within a transaction.
*
* @param Closure $callback
* @param Closure $callback
* @param int $isolationLevel
* @return mixed
*
* @throws \Exception
*/
public function transaction(Closure $callback);
public function transaction(Closure $callback, int $isolationLevel);
}

View File

@ -302,6 +302,7 @@ class SummitOrder extends SilverstripeBaseModel implements IQREntity
}
public function setPaid(){
Log::debug(sprintf("SummitOrder::setPaid order %s", $this->id));
if($this->isPaid()){
Log::warning(sprintf("SummitOrder %s is already Paid.", $this->getId()));
return;

View File

@ -462,7 +462,7 @@ final class EventServiceProvider extends ServiceProvider
if(!$event instanceof PaymentSummitRegistrationOrderConfirmed) return;
$order_id = $event->getOrderId();
Log::debug(sprintf("EventServiceProvider::PaymentSummitRegistrationOrderConfirmed: firing ProcessSummitOrderPaymentConfirmation for order id %s", $order_id));
ProcessSummitOrderPaymentConfirmation::dispatch($order_id)->delay(now()->addMinutes(1));
ProcessSummitOrderPaymentConfirmation::dispatch($order_id)->delay(now()->addSecond(10));
});
Event::listen(NewMember::class, function($event){

View File

@ -1700,14 +1700,17 @@ final class SummitOrderService
$owner = null;
$ticket_type = $this->ticket_type_repository->getByIdExclusiveLock(intval($payload['ticket_type_id']));
if (is_null($ticket_type) || !$ticket_type instanceof SummitTicketType || $ticket_type->getSummitId() != $summit->getId())
if (is_null($ticket_type) || !$ticket_type instanceof SummitTicketType || $ticket_type->getSummitId() != $summit->getId()) {
Log::warning("SummitOrderService::createOrderSingleTicket ticket type not found");
throw new EntityNotFoundException("ticket type not found");
}
// check owner
if (isset($payload['owner_id'])) {
Log::debug(sprintf("SummitOrderService::createOrderSingleTicket trying to get member by id %s", $payload['owner_id']));
$owner = $this->member_repository->getById(intval($payload['owner_id']));
if (is_null($owner)) {
Log::warning("SummitOrderService::createOrderSingleTicket owner not found");
throw new EntityNotFoundException("owner not found");
}
}
@ -1723,7 +1726,11 @@ final class SummitOrderService
if (is_null($attendee) && isset($payload['owner_email'])) {
Log::debug(sprintf("SummitOrderService::createOrderSingleTicket trying to get attendee by email %s", $payload['owner_email']));
$attendee = $summit->getAttendeeByEmail(trim($payload['owner_email']));
$attendee = $this->attendee_repository->getBySummitAndEmail($summit, trim($payload['owner_email']));
}
if(is_null($attendee) && isset($payload['attendee'])){
$attendee = $payload['attendee'];
}
if (is_null($attendee)) {
@ -1732,21 +1739,27 @@ final class SummitOrderService
//first name
$first_name = isset($payload['owner_first_name']) ? trim($payload['owner_first_name']) : null;
if (empty($first_name) && !is_null($owner)) $first_name = $owner->getFirstName();
if (empty($first_name))
if (empty($first_name)) {
Log::warning("SummitOrderService::createOrderSingleTicket owner firstname is null");
throw new ValidationException("you must provide an owner_first_name or a valid owner_id");
}
// surname
$surname = isset($payload['owner_last_name']) ? trim($payload['owner_last_name']) : null;
if (empty($surname) && !is_null($owner)) $surname = $owner->getLastName();
if (empty($surname))
if (empty($surname)) {
Log::warning("SummitOrderService::createOrderSingleTicket owner surname is null");
throw new ValidationException("you must provide an owner_last_name or a valid owner_id");
}
// mail
$email = isset($payload['owner_email']) ? trim($payload['owner_email']) : null;
$company = isset($payload['owner_company']) ? trim($payload['owner_company']) : null;
if (empty($email) && !is_null($owner)) $email = $owner->getEmail();
if (empty($email))
if (empty($email)) {
Log::warning("SummitOrderService::createOrderSingleTicket owner email is null");
throw new ValidationException("you must provide an owner_email or a valid owner_id");
}
$attendee = SummitAttendeeFactory::build($summit, [
'first_name' => $first_name,
@ -1769,8 +1782,10 @@ final class SummitOrderService
Log::debug(sprintf("SummitOrderService::createOrderSingleTicket order number %s", $order->getNumber()));
$default_badge_type = $summit->getDefaultBadgeType();
if (is_null($default_badge_type))
if (is_null($default_badge_type)) {
Log::warning("SummitOrderService::createOrderSingleTicket default_badge_type is null");
throw new ValidationException(sprintf("summit %s does not has a default badge type", $summit->getId()));
}
$order->setPaymentMethodOffline();
@ -2719,6 +2734,7 @@ final class SummitOrderService
Log::debug(sprintf("SummitOrderService::processTicketData processing row %s", json_encode($row)));
$ticket = null;
$attendee = null;
if ($ticket_data_present) {
Log::debug("SummitOrderService::processTicketData - has ticket data present ... trying to get ticket");
@ -2764,9 +2780,12 @@ final class SummitOrderService
$attendee = SummitAttendeeFactory::build($summit, $payload, $member);
$this->attendee_repository->add($attendee, true);
//$this->attendee_repository->add($attendee, true);
$this->attendee_repository->add($attendee);
}
}
if(!is_null($attendee)) {
if (is_null($ticket)) {
Log::debug(sprintf("SummitOrderService::processTicketData ticket is null, trying to create a new one"));
@ -2795,13 +2814,16 @@ final class SummitOrderService
return;
}
$order = $this->createOrderSingleTicket($summit, [
'ticket_type_id' => $ticket_type->getId(),
'owner_email' => $attendee->getEmail(),
'owner_first_name' => $attendee->getFirstName(),
'owner_last_name' => $attendee->getSurname(),
'owner_company' => $attendee->getCompanyName(),
]);
$order = $this->createOrderSingleTicket($summit,
[
'ticket_type_id' => $ticket_type->getId(),
'attendee' => $attendee,
'owner_email' => $attendee->getEmail(),
'owner_first_name' => $attendee->getFirstName(),
'owner_last_name' => $attendee->getSurname(),
'owner_company' => $attendee->getCompanyName(),
]
);
$ticket = $order->getFirstTicket();
@ -2828,6 +2850,7 @@ final class SummitOrderService
}
}
if (is_null($ticket)) {
Log::warning("SummitOrderService::processTicketData ticket is null stop current row processing.");
return;
@ -2932,7 +2955,8 @@ final class SummitOrderService
throw new ValidationException("need to set a value for external_registration_feed_api_key");
}
IngestSummitExternalRegistrationData::dispatch(
IngestSummitExternalRegistrationData::dispatch
(
$summit->getId(),
$email_to
);

View File

@ -11,6 +11,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\TransactionIsolationLevel;
use Illuminate\Support\Facades\Log;
use libs\utils\ITransactionService;
use Closure;
@ -43,11 +46,12 @@ final class DoctrineTransactionService implements ITransactionService
* Execute a Closure within a transaction.
*
* @param Closure $callback
* @param int $isolationLevel
* @return mixed
*
* @throws \Exception
*/
public function transaction(Closure $callback)
public function transaction(Closure $callback, int $isolationLevel = TransactionIsolationLevel::READ_COMMITTED)
{
$retry = 0;
$done = false;
@ -75,15 +79,18 @@ final class DoctrineTransactionService implements ITransactionService
// new entity manager
$con = $em->getConnection();
}
$con->setTransactionIsolation($isolationLevel);
Log::debug("DoctrineTransactionService::transaction con->beginTransaction");
$con->beginTransaction(); // suspend auto-commit
$result = $callback($this);
$em->flush();
$con->commit();
Log::debug("DoctrineTransactionService::transaction con->commit");
$done = true;
} catch (RetryableException $ex) {
Log::warning("retrying ...");
Registry::resetManager($this->manager_name);
Log::warning("DoctrineTransactionService::transaction con->rollBack");
$con->rollBack();
Log::warning($ex);
$retry++;
@ -94,6 +101,7 @@ final class DoctrineTransactionService implements ITransactionService
Log::warning("rolling back transaction");
Log::warning($ex);
$em->close();
Log::warning("DoctrineTransactionService::transaction con->rollBack");
$con->rollBack();
throw $ex;
}

View File

@ -32,7 +32,7 @@ final class EloquentTransactionService implements ITransactionService {
*
* @throws \Exception
*/
public function transaction(Closure $callback)
public function transaction(Closure $callback, int $isolationLevel)
{
return DB::transaction($callback);
}

View File

@ -115,12 +115,10 @@ final class OAuth2SummitTicketsApiTest extends ProtectedApiTest
return $csv;
}
public function testIngestTicketData($summit_id = 1){
public function testIngestTicketData($summit_id = 21){
$csv_content = <<<CSV
id,number,attendee_email,attendee_first_name,attendee_last_name,attendee_company,ticket_type_name,ticket_type_id,badge_type_id,badge_type_name,Commander,VIP Access
,,xmarcet+1@gmail.com,sebastian,marcet,pumant,Full Pass,,,,1,1
,DEVSUMMIT2019_TICKET_5DEE50D8DC3B4768174853,,,,,,,,,1,0
684,,xmarcet+2@gmail.com,,,,,,,,1,0
attendee_email,attendee_first_name,attendee_last_name,ticket_type_name,badge_type_name,Bloomreach Connect Summit - Day 1,User Group - Day 2,Tech Track - Day 3,Partner Summit - Day 4
smarcet+json12@gmail.com,Jason12,Marcet,General Admission,General Admission,1,1,1,1
CSV;
$path = "/tmp/tickets.csv";