From 9870187af5a769454d000475134f1762206bb570 Mon Sep 17 00:00:00 2001 From: Hao Liu Date: Tue, 18 Apr 2023 15:56:44 -0400 Subject: [PATCH] Fix copy API In web/task split deployment web and task container no longer share the same redis cache In the original code we use redis cache to pass the list of sub objects that need to be copied to the new object In this PR we extracted out the logic that computes the sub_object_list and move it into deep_copy_model_obj task --- awx/api/generics.py | 9 +-------- awx/main/tasks/system.py | 31 +++++++++++++++++++++++-------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/awx/api/generics.py b/awx/api/generics.py index c86639ab95..3371e0bc09 100644 --- a/awx/api/generics.py +++ b/awx/api/generics.py @@ -5,13 +5,11 @@ import inspect import logging import time -import uuid # Django from django.conf import settings from django.contrib.auth import views as auth_views from django.contrib.contenttypes.models import ContentType -from django.core.cache import cache from django.core.exceptions import FieldDoesNotExist from django.db import connection, transaction from django.db.models.fields.related import OneToOneRel @@ -967,16 +965,11 @@ class CopyAPIView(GenericAPIView): if hasattr(new_obj, 'admin_role') and request.user not in new_obj.admin_role.members.all(): new_obj.admin_role.members.add(request.user) if sub_objs: - # store the copied object dict into cache, because it's - # often too large for postgres' notification bus - # (which has a default maximum message size of 8k) - key = 'deep-copy-{}'.format(str(uuid.uuid4())) - cache.set(key, sub_objs, timeout=3600) permission_check_func = None if hasattr(type(self), 'deep_copy_permission_check_func'): permission_check_func = (type(self).__module__, type(self).__name__, 'deep_copy_permission_check_func') trigger_delayed_deep_copy( - self.model.__module__, self.model.__name__, obj.pk, new_obj.pk, request.user.pk, key, permission_check_func=permission_check_func + self.model.__module__, self.model.__name__, obj.pk, new_obj.pk, request.user.pk, permission_check_func=permission_check_func ) serializer = self._get_copy_return_serializer(new_obj) headers = {'Location': new_obj.get_absolute_url(request=request)} diff --git a/awx/main/tasks/system.py b/awx/main/tasks/system.py index 36fc266803..12b1981a85 100644 --- a/awx/main/tasks/system.py +++ b/awx/main/tasks/system.py @@ -893,15 +893,8 @@ def _reconstruct_relationships(copy_mapping): @task(queue=get_task_queuename) -def deep_copy_model_obj(model_module, model_name, obj_pk, new_obj_pk, user_pk, uuid, permission_check_func=None): - sub_obj_list = cache.get(uuid) - if sub_obj_list is None: - logger.error('Deep copy {} from {} to {} failed unexpectedly.'.format(model_name, obj_pk, new_obj_pk)) - return - +def deep_copy_model_obj(model_module, model_name, obj_pk, new_obj_pk, user_pk, permission_check_func=None): logger.debug('Deep copy {} from {} to {}.'.format(model_name, obj_pk, new_obj_pk)) - from awx.api.generics import CopyAPIView - from awx.main.signals import disable_activity_stream model = getattr(importlib.import_module(model_module), model_name, None) if model is None: @@ -913,6 +906,28 @@ def deep_copy_model_obj(model_module, model_name, obj_pk, new_obj_pk, user_pk, u except ObjectDoesNotExist: logger.warning("Object or user no longer exists.") return + + o2m_to_preserve = {} + fields_to_preserve = set(getattr(model, 'FIELDS_TO_PRESERVE_AT_COPY', [])) + + for field in model._meta.get_fields(): + if field.name in fields_to_preserve: + if field.one_to_many: + try: + field_val = getattr(obj, field.name) + except AttributeError: + continue + o2m_to_preserve[field.name] = field_val + + sub_obj_list = [] + for o2m in o2m_to_preserve: + for sub_obj in o2m_to_preserve[o2m].all(): + sub_model = type(sub_obj) + sub_obj_list.append((sub_model.__module__, sub_model.__name__, sub_obj.pk)) + + from awx.api.generics import CopyAPIView + from awx.main.signals import disable_activity_stream + with transaction.atomic(), ignore_inventory_computed_fields(), disable_activity_stream(): copy_mapping = {} for sub_obj_setup in sub_obj_list: