diff --git a/saleor/discount/utils.py b/saleor/discount/utils.py index c3db0fb11a1..da5c1b75422 100644 --- a/saleor/discount/utils.py +++ b/saleor/discount/utils.py @@ -1532,10 +1532,20 @@ def mark_active_catalogue_promotion_rules_as_dirty(channel_ids: Iterable[int]): rules = get_active_catalogue_promotion_rules() PromotionRuleChannel = PromotionRule.channels.through promotion_rules = PromotionRuleChannel.objects.filter(channel_id__in=channel_ids) - rules = rules.filter( + rule_ids = rules.filter( Exists(promotion_rules.filter(promotionrule_id=OuterRef("id"))) - ) - rules.update(variants_dirty=True) + ).values_list("id", flat=True) + + with transaction.atomic(): + rule_ids_to_update = list( + PromotionRule.objects.select_for_update(of=("self",)) + .filter(id__in=rule_ids, variants_dirty=False) + .order_by("pk") + .values_list("id", flat=True) + ) + PromotionRule.objects.filter(id__in=rule_ids_to_update).update( + variants_dirty=True + ) def mark_catalogue_promotion_rules_as_dirty(promotion_pks: Iterable[UUID]): @@ -1546,6 +1556,13 @@ def mark_catalogue_promotion_rules_as_dirty(promotion_pks: Iterable[UUID]): """ if not promotion_pks: return - PromotionRule.objects.filter(promotion_id__in=promotion_pks).update( - variants_dirty=True - ) + with transaction.atomic(): + rule_ids_to_update = list( + PromotionRule.objects.select_for_update(of=(["self"])) + .filter(promotion_id__in=promotion_pks, variants_dirty=False) + .order_by("pk") + .values_list("id", flat=True) + ) + PromotionRule.objects.filter(id__in=rule_ids_to_update).update( + variants_dirty=True + ) diff --git a/saleor/product/tasks.py b/saleor/product/tasks.py index 7107c74d760..86f6fd3e165 100644 --- a/saleor/product/tasks.py +++ b/saleor/product/tasks.py @@ -7,6 +7,7 @@ from celery.utils.log import get_task_logger from django.conf import settings from django.core.exceptions import ObjectDoesNotExist +from django.db import transaction from django.db.models import Exists, OuterRef, Q, QuerySet from django.utils import timezone @@ -185,8 +186,17 @@ def update_variant_relations_for_active_promotion_rules_task(): channel_to_product_map = _get_channel_to_products_map( existing_variant_relation + new_rule_to_variant_list ) + with transaction.atomic(): + promotion_rule_ids = list( + PromotionRule.objects.select_for_update(of=("self",)) + .filter(pk__in=ids, variants_dirty=True) + .order_by("pk") + .values_list("id", flat=True) + ) + PromotionRule.objects.filter(pk__in=promotion_rule_ids).update( + variants_dirty=False + ) - PromotionRule.objects.filter(pk__in=ids).update(variants_dirty=False) mark_products_in_channels_as_dirty(channel_to_product_map, allow_replica=True) update_variant_relations_for_active_promotion_rules_task.delay() @@ -224,9 +234,16 @@ def recalculate_discounted_price_for_products_task(): settings.DATABASE_CONNECTION_REPLICA_NAME ).filter(id__in=products_ids) update_discounted_prices_for_promotion(products, only_dirty_products=True) - ProductChannelListing.objects.filter(id__in=listing_ids).update( - discounted_price_dirty=False - ) + with transaction.atomic(): + channel_listings_ids = list( + ProductChannelListing.objects.select_for_update(of=("self",)) + .filter(id__in=listing_ids, discounted_price_dirty=True) + .order_by("pk") + .values_list("id", flat=True) + ) + ProductChannelListing.objects.filter(id__in=channel_listings_ids).update( + discounted_price_dirty=False + ) recalculate_discounted_price_for_products_task.delay() diff --git a/saleor/product/utils/product.py b/saleor/product/utils/product.py index 3e37a0fb957..624b37b443e 100644 --- a/saleor/product/utils/product.py +++ b/saleor/product/utils/product.py @@ -1,6 +1,7 @@ from collections import defaultdict from django.conf import settings +from django.db import transaction from django.db.models import Exists, OuterRef, QuerySet from ...discount.models import PromotionRule @@ -121,6 +122,13 @@ def mark_products_in_channels_as_dirty( listing_ids_to_update.append(id) if listing_ids_to_update: - ProductChannelListing.objects.filter(id__in=listing_ids_to_update).update( - discounted_price_dirty=True - ) + with transaction.atomic(): + channel_listing_ids = list( + ProductChannelListing.objects.select_for_update(of=("self",)) + .filter(id__in=listing_ids_to_update, discounted_price_dirty=False) + .order_by("pk") + .values_list("id", flat=True) + ) + ProductChannelListing.objects.filter(id__in=channel_listing_ids).update( + discounted_price_dirty=True + ) diff --git a/saleor/product/utils/variant_prices.py b/saleor/product/utils/variant_prices.py index 141af70eb59..6eb87e91cb1 100644 --- a/saleor/product/utils/variant_prices.py +++ b/saleor/product/utils/variant_prices.py @@ -117,11 +117,13 @@ def _update_or_create_listings( ): if changed_products_listings_to_update: ProductChannelListing.objects.bulk_update( - changed_products_listings_to_update, ["discounted_price_amount"] + sorted(changed_products_listings_to_update, key=lambda listing: listing.id), + ["discounted_price_amount"], ) if changed_variants_listings_to_update: ProductVariantChannelListing.objects.bulk_update( - changed_variants_listings_to_update, ["discounted_price_amount"] + sorted(changed_variants_listings_to_update, key=lambda listing: listing.id), + ["discounted_price_amount"], ) if changed_variant_listing_promotion_rule_to_create: _create_variant_listing_promotion_rule( @@ -129,7 +131,11 @@ def _update_or_create_listings( ) if changed_variant_listing_promotion_rule_to_update: VariantChannelListingPromotionRule.objects.bulk_update( - changed_variant_listing_promotion_rule_to_update, ["discount_amount"] + sorted( + changed_variant_listing_promotion_rule_to_update, + key=lambda listing: listing.id, + ), + ["discount_amount"], )