From 698ad33ec9828b894a64f3ed466388d29cfda514 Mon Sep 17 00:00:00 2001 From: Iliyan Iliev Date: Thu, 18 Jun 2020 11:10:03 +0300 Subject: [PATCH 1/3] Release lock if we have already reserved voucher --- .../VouchersService.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/MAVN.Service.SmartVouchers.DomainServices/VouchersService.cs b/src/MAVN.Service.SmartVouchers.DomainServices/VouchersService.cs index 4ffcf94..30cad2e 100644 --- a/src/MAVN.Service.SmartVouchers.DomainServices/VouchersService.cs +++ b/src/MAVN.Service.SmartVouchers.DomainServices/VouchersService.cs @@ -124,11 +124,14 @@ public async Task ReserveVoucherAsync(Guid voucherCamp var alreadyReservedVoucher = await _vouchersRepository.GetReservedVoucherForCustomerAsync(ownerId); if (alreadyReservedVoucher != null) + { + await _redisLocksService.ReleaseLockAsync(voucherCampaignIdStr, ownerId.ToString()); return new VoucherReservationResult { ErrorCode = ProcessingVoucherError.CustomerHaveAnotherReservedVoucher, AlreadyReservedVoucherShortCode = alreadyReservedVoucher.ShortCode }; + } var vouchers = await _vouchersRepository.GetByCampaignIdAndStatusAsync(voucherCampaignId, VoucherStatus.InStock); Voucher voucher = null; From 137304fe5c259f20e919958d720410cf7354a879 Mon Sep 17 00:00:00 2001 From: Iliyan Iliev Date: Thu, 18 Jun 2020 11:13:42 +0300 Subject: [PATCH 2/3] Add try finally to release redis lock --- .../VouchersService.cs | 247 +++++++++--------- 1 file changed, 127 insertions(+), 120 deletions(-) diff --git a/src/MAVN.Service.SmartVouchers.DomainServices/VouchersService.cs b/src/MAVN.Service.SmartVouchers.DomainServices/VouchersService.cs index 30cad2e..73d378b 100644 --- a/src/MAVN.Service.SmartVouchers.DomainServices/VouchersService.cs +++ b/src/MAVN.Service.SmartVouchers.DomainServices/VouchersService.cs @@ -122,104 +122,108 @@ public async Task ReserveVoucherAsync(Guid voucherCamp continue; } - var alreadyReservedVoucher = await _vouchersRepository.GetReservedVoucherForCustomerAsync(ownerId); - if (alreadyReservedVoucher != null) + try { - await _redisLocksService.ReleaseLockAsync(voucherCampaignIdStr, ownerId.ToString()); - return new VoucherReservationResult + var alreadyReservedVoucher = await _vouchersRepository.GetReservedVoucherForCustomerAsync(ownerId); + if (alreadyReservedVoucher != null) { - ErrorCode = ProcessingVoucherError.CustomerHaveAnotherReservedVoucher, - AlreadyReservedVoucherShortCode = alreadyReservedVoucher.ShortCode - }; - } + return new VoucherReservationResult + { + ErrorCode = ProcessingVoucherError.CustomerHaveAnotherReservedVoucher, + AlreadyReservedVoucherShortCode = alreadyReservedVoucher.ShortCode + }; + } - var vouchers = await _vouchersRepository.GetByCampaignIdAndStatusAsync(voucherCampaignId, VoucherStatus.InStock); - Voucher voucher = null; - if (vouchers.Any()) - { - try + var vouchers = await _vouchersRepository.GetByCampaignIdAndStatusAsync(voucherCampaignId, VoucherStatus.InStock); + Voucher voucher = null; + if (vouchers.Any()) { - voucher = vouchers.First(); - if (voucherPriceIsZero) + try { - voucher.Status = VoucherStatus.Sold; - voucher.OwnerId = ownerId; - voucher.PurchaseDate = DateTime.UtcNow; - await _vouchersRepository.UpdateAsync(voucher); + voucher = vouchers.First(); + if (voucherPriceIsZero) + { + voucher.Status = VoucherStatus.Sold; + voucher.OwnerId = ownerId; + voucher.PurchaseDate = DateTime.UtcNow; + await _vouchersRepository.UpdateAsync(voucher); + } + else + { + await _vouchersRepository.ReserveAsync(voucher, ownerId); + } } - else + catch (Exception e) { - await _vouchersRepository.ReserveAsync(voucher, ownerId); + _log.Error(e); + return new VoucherReservationResult { ErrorCode = ProcessingVoucherError.NoAvailableVouchers }; } } - catch (Exception e) - { - _log.Error(e); - await _redisLocksService.ReleaseLockAsync(voucherCampaignIdStr, ownerId.ToString()); - return new VoucherReservationResult { ErrorCode = ProcessingVoucherError.NoAvailableVouchers }; - } - } - else - { - var vouchersPage = await _vouchersRepository.GetByCampaignIdAsync(voucherCampaignId, 0, 1); - if (vouchersPage.TotalCount >= campaign.VouchersTotalCount) + else { - await _redisLocksService.ReleaseLockAsync(voucherCampaignIdStr, ownerId.ToString()); - return new VoucherReservationResult { ErrorCode = ProcessingVoucherError.NoAvailableVouchers }; - } + var vouchersPage = await _vouchersRepository.GetByCampaignIdAsync(voucherCampaignId, 0, 1); + if (vouchersPage.TotalCount >= campaign.VouchersTotalCount) + { + return new VoucherReservationResult { ErrorCode = ProcessingVoucherError.NoAvailableVouchers }; + } - var validationCode = GenerateValidation(); - voucher = new Voucher - { - CampaignId = voucherCampaignId, - Status = voucherPriceIsZero ? VoucherStatus.Sold : VoucherStatus.Reserved, - OwnerId = ownerId, - PurchaseDate = DateTime.UtcNow, - }; + var validationCode = GenerateValidation(); + voucher = new Voucher + { + CampaignId = voucherCampaignId, + Status = voucherPriceIsZero ? VoucherStatus.Sold : VoucherStatus.Reserved, + OwnerId = ownerId, + PurchaseDate = DateTime.UtcNow, + }; - voucher.Id = await _vouchersRepository.CreateAsync(voucher); - voucher.ShortCode = GenerateShortCodeFromId(voucher.Id); + voucher.Id = await _vouchersRepository.CreateAsync(voucher); + voucher.ShortCode = GenerateShortCodeFromId(voucher.Id); - await _vouchersRepository.UpdateAsync(voucher, validationCode); - } + await _vouchersRepository.UpdateAsync(voucher, validationCode); + } - await _redisLocksService.ReleaseLockAsync(voucherCampaignIdStr, ownerId.ToString()); + await _redisLocksService.ReleaseLockAsync(voucherCampaignIdStr, ownerId.ToString()); - if (voucherPriceIsZero) - { - await PublishVoucherSoldEvent(null, campaign, voucher); - return new VoucherReservationResult + if (voucherPriceIsZero) { - ErrorCode = ProcessingVoucherError.None - }; - } + await PublishVoucherSoldEvent(null, campaign, voucher); + return new VoucherReservationResult + { + ErrorCode = ProcessingVoucherError.None + }; + } - var paymentRequestResult = await _paymentManagementClient.Api.GeneratePaymentAsync( - new PaymentGenerationRequest + var paymentRequestResult = await _paymentManagementClient.Api.GeneratePaymentAsync( + new PaymentGenerationRequest + { + CustomerId = ownerId, + Amount = campaign.VoucherPrice, + Currency = campaign.Currency, + PartnerId = campaign.PartnerId, + ExternalPaymentEntityId = voucher.ShortCode, + }); + + if (paymentRequestResult.ErrorCode != PaymentGenerationErrorCode.None) { - CustomerId = ownerId, - Amount = campaign.VoucherPrice, - Currency = campaign.Currency, - PartnerId = campaign.PartnerId, - ExternalPaymentEntityId = voucher.ShortCode, - }); + await CancelReservationAsync(voucher.ShortCode); + return new VoucherReservationResult + { + ErrorCode = ProcessingVoucherError.InvalidPartnerPaymentConfiguration, + }; + } + + await _paymentRequestsRepository.CreatePaymentRequestAsync(paymentRequestResult.PaymentRequestId, voucher.ShortCode); - if (paymentRequestResult.ErrorCode != PaymentGenerationErrorCode.None) - { - await CancelReservationAsync(voucher.ShortCode); return new VoucherReservationResult { - ErrorCode = ProcessingVoucherError.InvalidPartnerPaymentConfiguration, + ErrorCode = ProcessingVoucherError.None, + PaymentUrl = paymentRequestResult.PaymentPageUrl, }; } - - await _paymentRequestsRepository.CreatePaymentRequestAsync(paymentRequestResult.PaymentRequestId, voucher.ShortCode); - - return new VoucherReservationResult + finally { - ErrorCode = ProcessingVoucherError.None, - PaymentUrl = paymentRequestResult.PaymentPageUrl, - }; + await _redisLocksService.ReleaseLockAsync(voucherCampaignIdStr, ownerId.ToString()); + } } _log.Warning($"Couldn't get a lock for voucher campaign {voucherCampaignId}"); @@ -260,64 +264,67 @@ public async Task PresentVouchersAsync(Guid campaignId, G continue; } - var reservedVouchersCount = await _vouchersRepository.GetReservedVouchersCountForCampaign(campaignId); - var availableVouchersCount = campaign.VouchersTotalCount - campaign.BoughtVouchersCount - reservedVouchersCount; - if (availableVouchersCount < customerIds.Count) + try { - await _redisLocksService.ReleaseLockAsync(voucherCampaignIdStr, adminIdStr); - return new PresentVouchersResult { Error = PresentVouchersErrorCodes.NotEnoughVouchersInStock }; - } + var reservedVouchersCount = await _vouchersRepository.GetReservedVouchersCountForCampaign(campaignId); + var availableVouchersCount = campaign.VouchersTotalCount - campaign.BoughtVouchersCount - reservedVouchersCount; - var vouchers = await _vouchersRepository.GetByCampaignIdAndStatusAsync(campaignId, VoucherStatus.InStock); - foreach (var customerId in customerIds) - { - Voucher voucher = null; - if (vouchers.Any()) - { - voucher = vouchers.First(); + if (availableVouchersCount < customerIds.Count) + return new PresentVouchersResult { Error = PresentVouchersErrorCodes.NotEnoughVouchersInStock }; - voucher.Status = VoucherStatus.Sold; - voucher.OwnerId = customerId; - voucher.PurchaseDate = DateTime.UtcNow; - await _vouchersRepository.UpdateAsync(voucher); - vouchers.Remove(voucher); - } - else + var vouchers = await _vouchersRepository.GetByCampaignIdAndStatusAsync(campaignId, VoucherStatus.InStock); + foreach (var customerId in customerIds) { - var validationCode = GenerateValidation(); - voucher = new Voucher + Voucher voucher = null; + if (vouchers.Any()) { - CampaignId = campaignId, - Status = VoucherStatus.Sold, - OwnerId = customerId, - PurchaseDate = DateTime.UtcNow, - }; + voucher = vouchers.First(); - voucher.Id = await _vouchersRepository.CreateAsync(voucher); - voucher.ShortCode = GenerateShortCodeFromId(voucher.Id); + voucher.Status = VoucherStatus.Sold; + voucher.OwnerId = customerId; + voucher.PurchaseDate = DateTime.UtcNow; + await _vouchersRepository.UpdateAsync(voucher); + vouchers.Remove(voucher); + } + else + { + var validationCode = GenerateValidation(); + voucher = new Voucher + { + CampaignId = campaignId, + Status = VoucherStatus.Sold, + OwnerId = customerId, + PurchaseDate = DateTime.UtcNow, + }; + + voucher.Id = await _vouchersRepository.CreateAsync(voucher); + voucher.ShortCode = GenerateShortCodeFromId(voucher.Id); + + await _vouchersRepository.UpdateAsync(voucher, validationCode); + } - await _vouchersRepository.UpdateAsync(voucher, validationCode); + await _voucherSoldPublisher.PublishAsync(new SmartVoucherSoldEvent + { + CampaignId = campaignId, + PartnerId = campaign.PartnerId, + Amount = 0, + Currency = campaign.Currency, + CustomerId = customerId, + Timestamp = DateTime.UtcNow, + VoucherShortCode = voucher.ShortCode, + }); } - await _voucherSoldPublisher.PublishAsync(new SmartVoucherSoldEvent + return new PresentVouchersResult { - CampaignId = campaignId, - PartnerId = campaign.PartnerId, - Amount = 0, - Currency = campaign.Currency, - CustomerId = customerId, - Timestamp = DateTime.UtcNow, - VoucherShortCode = voucher.ShortCode, - }); + Error = PresentVouchersErrorCodes.None, + NotRegisteredEmails = notRegisteredEmails.ToList(), + }; } - - await _redisLocksService.ReleaseLockAsync(voucherCampaignIdStr, adminIdStr); - - return new PresentVouchersResult + finally { - Error = PresentVouchersErrorCodes.None, - NotRegisteredEmails = notRegisteredEmails.ToList(), - }; + await _redisLocksService.ReleaseLockAsync(voucherCampaignIdStr, adminIdStr); + } } _log.Warning($"Couldn't get a lock when trying to present vouchers for voucher campaign {campaign}"); From 10ad763153e397e3f3d8ac284b1cb2e420ed07f3 Mon Sep 17 00:00:00 2001 From: Iliyan Iliev Date: Thu, 18 Jun 2020 11:21:01 +0300 Subject: [PATCH 3/3] remove not needed redis lock release --- .../VouchersService.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/MAVN.Service.SmartVouchers.DomainServices/VouchersService.cs b/src/MAVN.Service.SmartVouchers.DomainServices/VouchersService.cs index 73d378b..91aa8a7 100644 --- a/src/MAVN.Service.SmartVouchers.DomainServices/VouchersService.cs +++ b/src/MAVN.Service.SmartVouchers.DomainServices/VouchersService.cs @@ -182,8 +182,6 @@ public async Task ReserveVoucherAsync(Guid voucherCamp await _vouchersRepository.UpdateAsync(voucher, validationCode); } - await _redisLocksService.ReleaseLockAsync(voucherCampaignIdStr, ownerId.ToString()); - if (voucherPriceIsZero) { await PublishVoucherSoldEvent(null, campaign, voucher);