Skip to content

Commit d34b8d0

Browse files
committed
Use mamba under a feature flag to create conda environments
`mamba` is a a fast drop-in replacement for the conda command-line utility, in C++. I'm adding a feature flag so we can test it out in selective projects that are failing over and over again because of OOM when solving dependencies, even if they have just one, but they are adding conda-forge as channel.
1 parent 8f9cb19 commit d34b8d0

File tree

3 files changed

+56
-4
lines changed

3 files changed

+56
-4
lines changed

docs/guides/feature-flags.rst

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,18 @@ In case you prefer to use the latest ``conda`` version available, this is the fl
2525
Makes Read the Docs to install all the requirements at once on ``conda create`` step.
2626
This helps users to pin dependencies on conda and to improve build time.
2727

28+
``CONDA_USES_MAMBA``: :featureflags:`CONDA_USES_MAMBA`
29+
30+
``conda`` solver consumes 1Gb minimum when installing any package using ``conda-forge`` channel.
31+
This seems to be `a known issue`_ due conda forge has so many packages on it, among others.
32+
Using this feature flag allows you to use mamba_ instead of ``conda`` to create the environment
33+
and install the dependencies.
34+
``mamba`` is a drop-in replacement for conda that it's much faster and also
35+
reduces considerably the amount of memory required to solve the dependencies.
36+
37+
.. _mamba: https://quantstack.net/mamba.html
38+
.. _a known issue: https://www.anaconda.com/understanding-and-improving-condas-performance/
39+
2840
``DONT_OVERWRITE_SPHINX_CONTEXT``: :featureflags:`DONT_OVERWRITE_SPHINX_CONTEXT`
2941

3042
``DONT_SHALLOW_CLONE``: :featureflags:`DONT_SHALLOW_CLONE`
@@ -35,4 +47,4 @@ e.g. python-reno release notes manager is known to do that
3547

3648
``USE_TESTING_BUILD_IMAGE``: :featureflags:`USE_TESTING_BUILD_IMAGE`
3749

38-
``EXTERNAL_VERSION_BUILD``: :featureflags:`EXTERNAL_VERSION_BUILD`
50+
``EXTERNAL_VERSION_BUILD``: :featureflags:`EXTERNAL_VERSION_BUILD`

readthedocs/doc_builder/python_environments.py

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,23 @@ class Conda(PythonEnvironment):
438438
def venv_path(self):
439439
return os.path.join(self.project.doc_path, 'conda', self.version.slug)
440440

441+
def conda_bin_name(self):
442+
"""
443+
Decide whether use ``mamba`` or ``conda`` to create the environment.
444+
445+
Return ``mamba`` if the project has ``CONDA_USES_MAMBA`` feature and
446+
``conda`` otherwise. This will be the executable name to be used when
447+
creating the conda environment.
448+
449+
``mamba`` is really fast to solve dependencies and download channel
450+
metadata on startup.
451+
452+
See https://github.com/QuantStack/mamba
453+
"""
454+
if self.project.has_feature(Feature.CONDA_USES_MAMBA):
455+
return 'mamba'
456+
return 'conda'
457+
441458
def _update_conda_startup(self):
442459
"""
443460
Update ``conda`` before use it for the first time.
@@ -446,7 +463,7 @@ def _update_conda_startup(self):
446463
independently the version of Miniconda that it has installed.
447464
"""
448465
self.build_env.run(
449-
'conda',
466+
self.conda_bin_name(),
450467
'update',
451468
'--yes',
452469
'--quiet',
@@ -456,6 +473,18 @@ def _update_conda_startup(self):
456473
cwd=self.checkout_path,
457474
)
458475

476+
def _install_mamba(self):
477+
self.build_env.run(
478+
'conda',
479+
'install',
480+
'--yes',
481+
'--quiet',
482+
'--name=base',
483+
'--channel=conda-forge',
484+
'mamba',
485+
cwd=self.checkout_path,
486+
)
487+
459488
def setup_base(self):
460489
conda_env_path = os.path.join(self.project.doc_path, 'conda')
461490
version_path = os.path.join(conda_env_path, self.version.slug)
@@ -479,8 +508,11 @@ def setup_base(self):
479508
self._append_core_requirements()
480509
self._show_environment_yaml()
481510

511+
if self.project.has_feature(Feature.CONDA_USES_MAMBA):
512+
self._install_mamba()
513+
482514
self.build_env.run(
483-
'conda',
515+
self.conda_bin_name(),
484516
'env',
485517
'create',
486518
'--quiet',
@@ -566,6 +598,9 @@ def _get_core_requirements(self):
566598
'pillow',
567599
]
568600

601+
if self.project.has_feature(Feature.CONDA_USES_MAMBA):
602+
conda_requirements.append('pip')
603+
569604
# Install pip-only things.
570605
pip_requirements = [
571606
'recommonmark',
@@ -592,7 +627,7 @@ def install_core_requirements(self):
592627
# Install requirements via ``conda install`` command if they were
593628
# not appended to the ``environment.yml`` file.
594629
cmd = [
595-
'conda',
630+
self.conda_bin_name(),
596631
'install',
597632
'--yes',
598633
'--quiet',

readthedocs/projects/models.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1511,6 +1511,7 @@ def add_features(sender, **kwargs):
15111511
EXTERNAL_VERSION_BUILD = 'external_version_build'
15121512
UPDATE_CONDA_STARTUP = 'update_conda_startup'
15131513
CONDA_APPEND_CORE_REQUIREMENTS = 'conda_append_core_requirements'
1514+
CONDA_USES_MAMBA = 'conda_uses_mamba'
15141515
ALL_VERSIONS_IN_HTML_CONTEXT = 'all_versions_in_html_context'
15151516
SKIP_SYNC_TAGS = 'skip_sync_tags'
15161517
SKIP_SYNC_BRANCHES = 'skip_sync_branches'
@@ -1566,6 +1567,10 @@ def add_features(sender, **kwargs):
15661567
CONDA_APPEND_CORE_REQUIREMENTS,
15671568
_('Append Read the Docs core requirements to environment.yml file'),
15681569
),
1570+
(
1571+
CONDA_USES_MAMBA,
1572+
_('Uses mamba binary instead of conda to create the environment'),
1573+
),
15691574
(
15701575
ALL_VERSIONS_IN_HTML_CONTEXT,
15711576
_(

0 commit comments

Comments
 (0)