-
Notifications
You must be signed in to change notification settings - Fork 5.7k
Open
Description
I know that the DeleteGuests task failing has been brought up before - i have myself brought it up before here and on the forums.
This is still frequently posted about in the forums and people have devised all kinds of crazy work-arounds simply to cleanup their guest accounts. Personally my "DeleteGuests" task hasn't run successfully in many months.
I had chatgpt refactor it to a batched query instead of one mega query which is known to time out, lock up the database, leaves behind artifact tmp tables, etc...
The batch size could probably be tweaked, but the batch size of 500 runs pretty quickly and without error.
I strongly suggest you implement this, or something similar and put this issue to rest.
public override async Task<int> DeleteGuestCustomersAsync(DateTime? createdFromUtc, DateTime? createdToUtc, bool onlyWithoutShoppingCart)
{
var guestRole = await GetCustomerRoleBySystemNameAsync(NopCustomerDefaults.GuestsRoleName);
var batchSize = 500;
var totalDeleted = 0;
while (true)
{
// Step 1: Find a batch of guest customer IDs to delete
var guestIdsQuery =
from guest in _customerRepository.Table
join ccm in _customerCustomerRoleMappingRepository.Table on guest.Id equals ccm.CustomerId
where ccm.CustomerRoleId == guestRole.Id
&& !guest.IsSystemAccount
&& (createdFromUtc == null || guest.CreatedOnUtc > createdFromUtc)
&& (createdToUtc == null || guest.CreatedOnUtc < createdToUtc)
select guest.Id;
// Exclude guests with related data if needed
if (onlyWithoutShoppingCart)
{
guestIdsQuery = from id in guestIdsQuery
where !_shoppingCartRepository.Table.Any(sci => sci.CustomerId == id)
select id;
}
guestIdsQuery = from id in guestIdsQuery
where !_orderRepository.Table.Any(o => o.CustomerId == id)
&& !_blogCommentRepository.Table.Any(bc => bc.CustomerId == id)
&& !_newsCommentRepository.Table.Any(nc => nc.CustomerId == id)
&& !_productReviewRepository.Table.Any(pr => pr.CustomerId == id)
&& !_productReviewHelpfulnessRepository.Table.Any(prh => prh.CustomerId == id)
&& !_pollVotingRecordRepository.Table.Any(pvr => pvr.CustomerId == id)
&& !_forumTopicRepository.Table.Any(ft => ft.CustomerId == id)
&& !_forumPostRepository.Table.Any(fp => fp.CustomerId == id)
select id;
var guestIds = guestIdsQuery.Take(batchSize).ToList();
if (!guestIds.Any())
break;
// Step 2: Delete related data in batch
await _gaRepository.DeleteAsync(ga => guestIds.Contains(ga.EntityId) && ga.KeyGroup == nameof(Customer));
await _customerAddressRepository.DeleteAsync(a => _customerAddressMappingRepository.Table.Any(ca => ca.AddressId == a.Id && guestIds.Contains(ca.CustomerId)));
await _customerAddressMappingRepository.DeleteAsync(ca => guestIds.Contains(ca.CustomerId));
// Step 3: Delete the customers
var deleted = await _customerRepository.DeleteAsync(c => guestIds.Contains(c.Id));
totalDeleted += deleted;
}
return totalDeleted;
}