2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-28 11:36:44 +00:00

[Bug] Fix for create_child_builds (#8399)

* Fix for create_child_builds

- Account for concurrency between multiple worker processes
- Ensure db commits are atomic
- Add random delays between build creation

* Check for existing build order

* Initially force  task off to background worker

* Revert force_async change
This commit is contained in:
Oliver 2024-10-31 13:59:53 +11:00 committed by GitHub
parent feefa60a8f
commit 913a05cf45
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 47 additions and 20 deletions

View File

@ -180,6 +180,7 @@ class BuildSerializer(NotesFieldMixin, DataImportExportSerializerMixin, InvenTre
return reference
@transaction.atomic
def create(self, validated_data):
"""Save the Build object."""
@ -192,7 +193,7 @@ class BuildSerializer(NotesFieldMixin, DataImportExportSerializerMixin, InvenTre
InvenTree.tasks.offload_task(
build.tasks.create_child_builds,
build_order.pk,
group='build',
group='build'
)
return build_order

View File

@ -1,11 +1,15 @@
"""Background task definitions for the BuildOrder app."""
import logging
import random
import time
from datetime import timedelta
from decimal import Decimal
from django.contrib.auth.models import User
from django.template.loader import render_to_string
from django.db import transaction
from django.utils.translation import gettext_lazy as _
from allauth.account.models import EmailAddress
@ -198,27 +202,49 @@ def create_child_builds(build_id: int) -> None:
assembly_items = build_order.part.get_bom_items().filter(sub_part__assembly=True)
for item in assembly_items:
quantity = item.quantity * build_order.quantity
# Random delay, to reduce likelihood of race conditions from multiple build orders being created simultaneously
time.sleep(random.random())
sub_order = build_models.Build.objects.create(
part=item.sub_part,
quantity=quantity,
title=build_order.title,
batch=build_order.batch,
parent=build_order,
target_date=build_order.target_date,
sales_order=build_order.sales_order,
issued_by=build_order.issued_by,
responsible=build_order.responsible,
)
with transaction.atomic():
# Atomic transaction to ensure that all child build orders are created together, or not at all
# This is critical to prevent duplicate child build orders being created (e.g. if the task is re-run)
# Offload the child build order creation to the background task queue
InvenTree.tasks.offload_task(
create_child_builds,
sub_order.pk,
group='build'
)
sub_build_ids = []
for item in assembly_items:
quantity = item.quantity * build_order.quantity
# Check if the child build order has already been created
if build_models.Build.objects.filter(
part=item.sub_part,
parent=build_order,
quantity=quantity,
status__in=BuildStatusGroups.ACTIVE_CODES
).exists():
continue
sub_order = build_models.Build.objects.create(
part=item.sub_part,
quantity=quantity,
title=build_order.title,
batch=build_order.batch,
parent=build_order,
target_date=build_order.target_date,
sales_order=build_order.sales_order,
issued_by=build_order.issued_by,
responsible=build_order.responsible,
)
sub_build_ids.append(sub_order.pk)
for pk in sub_build_ids:
# Offload the child build order creation to the background task queue
InvenTree.tasks.offload_task(
create_child_builds,
pk,
group='build'
)
def notify_overdue_build_order(bo: build_models.Build):