Skip to content

Commit e342d14

Browse files
committed
Improve content_ids handling by defered loading
The array_field can be huge and most of the time we don't need the values in python. Some uses can even be handled by clever SQL without ever channelling all the data through the network.
1 parent 4f05456 commit e342d14

1 file changed

Lines changed: 42 additions & 17 deletions

File tree

pulpcore/app/models/repository.py

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,37 @@ def with_content(self, content):
908908
return self.filter(content_ids__overlap=content_pks)
909909

910910

911+
class RepositoryVersionManager(models.Manager):
912+
# `RepositoryVersionQuerySet.as_manager()` does not allow us to redefine `get_queryset`.
913+
# Sadly, we have to replicate the filtering amenities too.
914+
#
915+
def get_queryset(self):
916+
# Prevent the content_ids to be automatically hydrated.
917+
return RepositoryVersionQuerySet(self.model, using=self._db).defer("content_ids")
918+
919+
def complete(self):
920+
return self.get_queryset().filter(complete=True)
921+
922+
def with_content(self, content):
923+
"""
924+
Filters repository versions that contain the provided content units.
925+
926+
Args:
927+
content (django.db.models.QuerySet or list): Content queryset or list of PKs
928+
929+
Returns:
930+
django.db.models.QuerySet: Repository versions which contains content.
931+
"""
932+
if isinstance(content, models.QuerySet):
933+
content_pks = content.values_list("pk", flat=True)
934+
elif not content:
935+
return self.none()
936+
else:
937+
content_pks = content
938+
939+
return self.get_queryset().filter(content_ids__overlap=content_pks)
940+
941+
911942
class RepositoryVersion(BaseModel):
912943
"""
913944
A version of a repository's content set.
@@ -936,7 +967,7 @@ class RepositoryVersion(BaseModel):
936967
base_version (models.ForeignKey): The repository version this was created from.
937968
"""
938969

939-
objects = RepositoryVersionQuerySet.as_manager()
970+
objects = RepositoryVersionManager()
940971

941972
repository = models.ForeignKey(Repository, on_delete=models.CASCADE)
942973
number = models.PositiveIntegerField(db_index=True)
@@ -997,15 +1028,13 @@ def get_content(self, content_qs=None):
9971028
if content_qs is None:
9981029
content_qs = Content.objects
9991030

1000-
content_ids = self.content_ids
1001-
if len(content_ids) >= 65535:
1002-
# Workaround for PostgreSQL's limit on the number of parameters in a query
1003-
content_ids = (
1004-
RepositoryVersion.objects.filter(pk=self.pk)
1005-
.annotate(cids=Func(F("content_ids"), function="unnest"))
1006-
.values_list("cids", flat=True)
1007-
)
1008-
return content_qs.filter(pk__in=content_ids)
1031+
# Try to not even attempt to evaluate the content_ids on the python side.
1032+
content_ids_subquery = (
1033+
RepositoryVersion.objects.filter(pk=self.pk)
1034+
.annotate(cids=Func(F("content_ids"), function="unnest"))
1035+
.values_list("cids", flat=True)
1036+
)
1037+
return content_qs.filter(pk__in=content_ids_subquery)
10091038

10101039
@property
10111040
def content(self):
@@ -1119,9 +1148,7 @@ def added(self, base_version=None):
11191148
if not base_version:
11201149
return Content.objects.filter(version_memberships__version_added=self)
11211150

1122-
return Content.objects.filter(pk__in=self.content_ids).exclude(
1123-
pk__in=base_version.content_ids
1124-
)
1151+
return Content.objects.filter(pk__in=self.content).exclude(pk__in=base_version.content)
11251152

11261153
def removed(self, base_version=None):
11271154
"""
@@ -1134,9 +1161,7 @@ def removed(self, base_version=None):
11341161
if not base_version:
11351162
return Content.objects.filter(version_memberships__version_removed=self)
11361163

1137-
return Content.objects.filter(pk__in=base_version.content_ids).exclude(
1138-
pk__in=self.content_ids
1139-
)
1164+
return Content.objects.filter(pk__in=base_version.content).exclude(pk__in=self.content)
11401165

11411166
def contains(self, content):
11421167
"""
@@ -1145,7 +1170,7 @@ def contains(self, content):
11451170
Returns:
11461171
bool: True if the repository version contains the content, False otherwise
11471172
"""
1148-
return content.pk in self.content_ids
1173+
return content.pk in self.content
11491174

11501175
def add_content(self, content):
11511176
"""

0 commit comments

Comments
 (0)