Repository: pulp/pulpcore

Issue:
Gzip level 1 compression is still too high for many users of import/export
Even the lowest level of gzip compression, level 1, consumes an inordinate and disproportionately high amount of compute time compared to the benefit received from the compression (which is in most cases tiny, since often the data is compressed to begin with)

For example with no compression at all, an export of CentOS Stream 9 BaseOS + RHEL 9 BaseOS required 11.8gb of disk.

With Level 1 compression, it required 11.4gb.  With Level 9 compression, it required 11.3gb.

For this result, on my system, 63% of the export time was spent on compressing the exports.  Going from level 9 to level 1 to level 0 brought the runtime from 10.5min, to 8.5min, to 4min respectively.

Users of import/export are often dealing with data on the scale of hundreds of gigabytes or multiple terabytes, and disk space seems to be less an issue than the amount of time these exports take.  It's best that we default to no compression at all, and look at making it configurable later.

Luckily "gzip level 0" does exist, and packs everything into a gzip archive without performing compression.  So we can drop this change in without breaking compatibility with anything.


Discussion:

Resolving patch:
diff --git a/pulpcore/app/monkeypatch.py b/pulpcore/app/monkeypatch.py
--- a/pulpcore/app/monkeypatch.py
+++ b/pulpcore/app/monkeypatch.py
@@ -1,7 +1,4 @@
-import sys
-
-# This is a monkeypatch for https://github.com/pulp/pulpcore/issues/3869
-if sys.version_info.major == 3 and sys.version_info.minor < 12:
+def patch_tarfile_default_compression_level(level):
     # Code copied from the Python 3.12 standard library
     # We modify the default gzip compression level for writing streams from
     # 9 to 1, attempting to vendor the minimum amount of code.
@@ -52,7 +49,7 @@ class _Stream(tarfile._Stream):
         def _init_write_gz(self):
             """Initialize for writing with gzip compression."""
             self.cmp = self.zlib.compressobj(
-                1, self.zlib.DEFLATED, -self.zlib.MAX_WBITS, self.zlib.DEF_MEM_LEVEL, 0
+                level, self.zlib.DEFLATED, -self.zlib.MAX_WBITS, self.zlib.DEF_MEM_LEVEL, 0
             )
             timestamp = struct.pack("<L", int(time.time()))
             self.__write(b"\037\213\010\010" + timestamp + b"\002\377")
diff --git a/pulpcore/app/tasks/export.py b/pulpcore/app/tasks/export.py
--- a/pulpcore/app/tasks/export.py
+++ b/pulpcore/app/tasks/export.py
@@ -29,8 +29,6 @@
 from pulpcore.app.models.content import ContentArtifact
 from pulpcore.app.serializers import PulpExportSerializer
 
-# ensure the compression patch is loaded
-from pulpcore.app import monkeypatch  # noqa
 from pulpcore.app.util import get_version_from_model
 from pulpcore.app.importexport import (
     export_versions,
@@ -281,6 +279,8 @@ def pulp_export(exporter_pk, params):
         ValidationError: When path is not in the ALLOWED_EXPORT_PATHS setting,
             OR path exists and is not a directory
     """
+    DEFAULT_COMPRESSION = 0
+
     pulp_exporter = PulpExporter.objects.get(pk=exporter_pk)
     serializer = PulpExportSerializer(data=params, context={"exporter": pulp_exporter})
     serializer.is_valid(raise_exception=True)
@@ -316,12 +316,20 @@ def pulp_export(exporter_pk, params):
             ) as split_process:
                 try:
                     # on Python < 3.12 we have a monkeypatch which enables compression levels
+                    # see https://github.com/pulp/pulpcore/issues/3869
                     if sys.version_info.major == 3 and sys.version_info.minor < 12:
+                        from pulpcore.app import monkeypatch
+
+                        monkeypatch.patch_tarfile_default_compression_level(DEFAULT_COMPRESSION)
+
                         with tarfile.open(tarfile_fp, "w|gz", fileobj=split_process.stdin) as tar:
                             _do_export(pulp_exporter, tar, the_export)
                     else:
                         with tarfile.open(
-                            tarfile_fp, "w|gz", fileobj=split_process.stdin, compresslevel=1
+                            tarfile_fp,
+                            "w|gz",
+                            fileobj=split_process.stdin,
+                            compresslevel=DEFAULT_COMPRESSION,
                         ) as tar:
                             _do_export(pulp_exporter, tar, the_export)
                 except Exception:
@@ -341,7 +349,7 @@ def pulp_export(exporter_pk, params):
         else:
             # write into the file
             try:
-                with tarfile.open(tarfile_fp, "w:gz", compresslevel=1) as tar:
+                with tarfile.open(tarfile_fp, "w:gz", compresslevel=DEFAULT_COMPRESSION) as tar:
                     _do_export(pulp_exporter, tar, the_export)
             except Exception:
                 # no matter what went wrong, we can't trust the file we created.


Test patch: