Skip to content

Commit

Permalink
Enhance /repositories/ansible/ansible/ endpoint performance (#1507)
Browse files Browse the repository at this point in the history
By annotating the last sync task to the queryset.

fixes: #1506
  • Loading branch information
jerabekjiri committed Aug 29, 2023
1 parent ec85318 commit 0122fb4
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGES/1506.misc
@@ -0,0 +1 @@
Refactor ``last_sync_task`` in AnsibleRepository model due to performance issues.
13 changes: 0 additions & 13 deletions pulp_ansible/app/models.py
Expand Up @@ -419,11 +419,6 @@ class Meta:
permissions = [("manage_roles_roleremote", "Can manage roles on role remotes")]


def _get_last_sync_task(pk):
sync_tasks = Task.objects.filter(name__contains="sync", reserved_resources_record__icontains=pk)
return sync_tasks.order_by("-pulp_created").first()


class CollectionRemote(Remote, AutoAddObjPermsMixin):
"""
A Remote for Collection content.
Expand Down Expand Up @@ -458,10 +453,6 @@ def download_factory(self):
self._download_factory = AnsibleDownloaderFactory(self)
return self._download_factory

@property
def last_sync_task(self):
return _get_last_sync_task(self.pk)

@hook(
AFTER_UPDATE,
when_any=["url", "requirements_file", "sync_dependencies", "signed_only"],
Expand Down Expand Up @@ -534,10 +525,6 @@ class AnsibleRepository(Repository, AutoAddObjPermsMixin):
gpgkey = models.TextField(null=True)
private = models.BooleanField(default=False)

@property
def last_sync_task(self):
return _get_last_sync_task(self.pk)

class Meta:
default_related_name = "%(app_label)s_%(model_name)s"

Expand Down
18 changes: 15 additions & 3 deletions pulp_ansible/app/serializers.py
Expand Up @@ -25,7 +25,6 @@
DistributionSerializer,
RepositoryVersionRelatedField,
validate_unknown_fields,
TaskSerializer,
)
from pulpcore.plugin.util import get_url
from rest_framework.exceptions import ValidationError
Expand Down Expand Up @@ -137,13 +136,14 @@ class AnsibleRepositorySerializer(RepositorySerializer):
last_synced_metadata_time = serializers.DateTimeField(
help_text=_("Last synced metadata time."), allow_null=True, required=False
)
last_sync_task = TaskSerializer(read_only=True)
gpgkey = serializers.CharField(
help_text="Gpg public key to verify collection signatures against",
required=False,
allow_null=True,
)

last_sync_task = serializers.SerializerMethodField()

class Meta:
fields = RepositorySerializer.Meta.fields + (
"last_synced_metadata_time",
Expand All @@ -153,6 +153,12 @@ class Meta:
)
model = AnsibleRepository

def get_last_sync_task(self, obj):
if hasattr(obj, "last_sync_task"):
return obj.last_sync_task

return None


class AnsibleRepositorySyncURLSerializer(RepositorySyncURLSerializer):
"""
Expand Down Expand Up @@ -225,7 +231,7 @@ class CollectionRemoteSerializer(RemoteSerializer):
default=False,
)

last_sync_task = TaskSerializer(read_only=True)
last_sync_task = serializers.SerializerMethodField()

def validate(self, data):
"""
Expand Down Expand Up @@ -271,6 +277,12 @@ def _validate_url(url):

return data

def get_last_sync_task(self, obj):
if hasattr(obj, "last_sync_task"):
return obj.last_sync_task

return None

class Meta:
fields = RemoteSerializer.Meta.fields + (
"requirements_file",
Expand Down
26 changes: 24 additions & 2 deletions pulp_ansible/app/utils.py
@@ -1,5 +1,6 @@
from django.db.models import Q
from pulpcore.plugin.models import RepositoryVersion, RepositoryContent
from django.db.models import Q, OuterRef, Subquery, CharField
from pulpcore.plugin.models import RepositoryVersion, RepositoryContent, Task
from django.db.models.functions import Cast, JSONObject


def filter_content_for_repo_version(qs, repo_version):
Expand All @@ -26,3 +27,24 @@ def filter_content_for_repo_version(qs, repo_version):
content_rel = RepositoryContent.objects.filter(f)

return qs.filter(pk__in=content_rel.values_list("content_id"))


def get_queryset_annotated_with_last_sync_task(qs):
last_task = (
Task.objects.filter(
name__contains="sync",
reserved_resources_record__icontains=Cast(OuterRef("pk"), output_field=CharField()),
)
.values(
json=JSONObject(
pk="pk",
state="state",
pulp_created="pulp_created",
finished_at="finished_at",
error="error",
)
)
.order_by("-pulp_created")[:1]
)

return qs.annotate(last_sync_task=Subquery(last_task))
17 changes: 17 additions & 0 deletions pulp_ansible/app/viewsets.py
Expand Up @@ -36,6 +36,7 @@
)
from pulpcore.plugin.util import extract_pk, raise_for_unknown_content_units, get_artifact_url
from pulp_ansible.app.galaxy.mixins import UploadGalaxyCollectionMixin
from pulp_ansible.app.utils import get_queryset_annotated_with_last_sync_task
from .models import (
AnsibleCollectionDeprecated,
AnsibleDistribution,
Expand Down Expand Up @@ -554,6 +555,14 @@ class AnsibleRepositoryViewSet(RepositoryViewSet, ModifyRepositoryActionMixin, R
queryset = AnsibleRepository.objects.all()
serializer_class = AnsibleRepositorySerializer

def get_queryset(self):
qs = super().get_queryset()
action = getattr(self, "action", "")
if action == "list" or action == "retrieve":
qs = get_queryset_annotated_with_last_sync_task(qs)

return qs

queryset_filtering_required_permission = "ansible.view_ansiblerepository"

DEFAULT_ACCESS_POLICY = {
Expand Down Expand Up @@ -987,6 +996,14 @@ class CollectionRemoteViewSet(RemoteViewSet, RolesMixin):
serializer_class = CollectionRemoteSerializer
filterset_class = CollectionRemoteFilter

def get_queryset(self):
qs = super().get_queryset()
action = getattr(self, "action", "")
if action == "list" or action == "retrieve":
qs = get_queryset_annotated_with_last_sync_task(qs)

return qs

queryset_filtering_required_permission = "ansible.view_collectionremote"

DEFAULT_ACCESS_POLICY = {
Expand Down

0 comments on commit 0122fb4

Please sign in to comment.