From 7fc2ed072fca56da0a4ab87ff6575cc6cb548cad Mon Sep 17 00:00:00 2001 From: Gerrod Ubben Date: Mon, 23 Feb 2026 13:18:54 -0500 Subject: [PATCH] Fix monolithic push memory issues (cherry picked from commit c81d74400d54c5c4596212f2092445ad24ebcbac) --- CHANGES/+mono-push.bugfix | 1 + pulp_container/app/registry_api.py | 15 +++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) create mode 100644 CHANGES/+mono-push.bugfix diff --git a/CHANGES/+mono-push.bugfix b/CHANGES/+mono-push.bugfix new file mode 100644 index 000000000..78662e749 --- /dev/null +++ b/CHANGES/+mono-push.bugfix @@ -0,0 +1 @@ +Fixed memory usage when pushing large images with monolithic upload. diff --git a/pulp_container/app/registry_api.py b/pulp_container/app/registry_api.py index 6ec915ce1..df985cd98 100644 --- a/pulp_container/app/registry_api.py +++ b/pulp_container/app/registry_api.py @@ -880,24 +880,27 @@ def put(self, request, path, pk=None): """ Create a blob from uploaded chunks. - This request makes the upload complete. It can whether carry a zero-length - body or last chunk can be uploaded. + This request makes the upload complete. It can either carry a zero-length + body or contain the last chunk. """ _, repository = self.get_dr_push(request, path) digest = request.query_params["digest"] chunk = request.META["wsgi.input"] - # last chunk (and the only one) from monolitic upload - # or last chunk from chunked upload - last_chunk = ContentFile(chunk.read()) upload = get_object_or_404(models.Upload, pk=pk, repository=repository) - if artifact := upload.artifact: + if upload.size == 0: + # monolithic upload to be completed in PUT + artifact = self.create_single_chunk_artifact(chunk) + elif artifact := upload.artifact: + # chunked upload was completed in PATCH if artifact.sha256 != digest[len("sha256:") :]: raise Exception("The digest did not match") artifact.touch() else: + # chunked upload to be completed in PUT + last_chunk = ContentFile(chunk.read()) chunks = UploadChunk.objects.filter(upload=upload).order_by("offset") with NamedTemporaryFile("ab") as temp_file: for chunk in chunks: