Skip to content

DeleteGuests task issues #7847

@danFbach

Description

@danFbach

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;
}

Metadata

Metadata

Assignees

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions