diff --git a/docs/guides/feature-flags.rst b/docs/guides/feature-flags.rst index 1d132aedb89..effa4c356ee 100644 --- a/docs/guides/feature-flags.rst +++ b/docs/guides/feature-flags.rst @@ -27,6 +27,18 @@ In case you prefer to use the latest ``conda`` version available, this is the fl Makes Read the Docs to install all the requirements at once on ``conda create`` step. This helps users to pin dependencies on conda and to improve build time. +``CONDA_USES_MAMBA``: :featureflags:`CONDA_USES_MAMBA` + +``conda`` solver consumes 1Gb minimum when installing any package using ``conda-forge`` channel. +This seems to be `a known issue`_ due conda forge has so many packages on it, among others. +Using this feature flag allows you to use mamba_ instead of ``conda`` to create the environment +and install the dependencies. +``mamba`` is a drop-in replacement for conda that it's much faster and also +reduces considerably the amount of memory required to solve the dependencies. + +.. _mamba: https://quantstack.net/mamba.html +.. _a known issue: https://www.anaconda.com/understanding-and-improving-condas-performance/ + ``DONT_OVERWRITE_SPHINX_CONTEXT``: :featureflags:`DONT_OVERWRITE_SPHINX_CONTEXT` ``DONT_SHALLOW_CLONE``: :featureflags:`DONT_SHALLOW_CLONE` diff --git a/readthedocs/doc_builder/python_environments.py b/readthedocs/doc_builder/python_environments.py index 82273c8235e..4dc03d81517 100644 --- a/readthedocs/doc_builder/python_environments.py +++ b/readthedocs/doc_builder/python_environments.py @@ -493,6 +493,23 @@ class Conda(PythonEnvironment): def venv_path(self): return os.path.join(self.project.doc_path, 'conda', self.version.slug) + def conda_bin_name(self): + """ + Decide whether use ``mamba`` or ``conda`` to create the environment. + + Return ``mamba`` if the project has ``CONDA_USES_MAMBA`` feature and + ``conda`` otherwise. This will be the executable name to be used when + creating the conda environment. + + ``mamba`` is really fast to solve dependencies and download channel + metadata on startup. + + See https://github.com/QuantStack/mamba + """ + if self.project.has_feature(Feature.CONDA_USES_MAMBA): + return 'mamba' + return 'conda' + def _update_conda_startup(self): """ Update ``conda`` before use it for the first time. @@ -501,6 +518,8 @@ def _update_conda_startup(self): independently the version of Miniconda that it has installed. """ self.build_env.run( + # TODO: use ``self.conda_bin_name()`` once ``mamba`` is installed in + # the Docker image 'conda', 'update', '--yes', @@ -511,6 +530,18 @@ def _update_conda_startup(self): cwd=self.checkout_path, ) + def _install_mamba(self): + self.build_env.run( + 'conda', + 'install', + '--yes', + '--quiet', + '--name=base', + '--channel=conda-forge', + 'mamba', + cwd=self.checkout_path, + ) + def setup_base(self): conda_env_path = os.path.join(self.project.doc_path, 'conda') version_path = os.path.join(conda_env_path, self.version.slug) @@ -534,8 +565,12 @@ def setup_base(self): self._append_core_requirements() self._show_environment_yaml() + # TODO: remove it when ``mamba`` is installed in the Docker image + if self.project.has_feature(Feature.CONDA_USES_MAMBA): + self._install_mamba() + self.build_env.run( - 'conda', + self.conda_bin_name(), 'env', 'create', '--quiet', @@ -621,6 +656,9 @@ def _get_core_requirements(self): 'pillow', ] + if self.project.has_feature(Feature.CONDA_USES_MAMBA): + conda_requirements.append('pip') + # Install pip-only things. pip_requirements = [ 'recommonmark', @@ -648,7 +686,7 @@ def install_core_requirements(self): # Install requirements via ``conda install`` command if they were # not appended to the ``environment.yml`` file. cmd = [ - 'conda', + self.conda_bin_name(), 'install', '--yes', '--quiet', diff --git a/readthedocs/projects/models.py b/readthedocs/projects/models.py index b44c46a35b9..b6740702ce0 100644 --- a/readthedocs/projects/models.py +++ b/readthedocs/projects/models.py @@ -1579,6 +1579,7 @@ def add_features(sender, **kwargs): EXTERNAL_VERSION_BUILD = 'external_version_build' UPDATE_CONDA_STARTUP = 'update_conda_startup' CONDA_APPEND_CORE_REQUIREMENTS = 'conda_append_core_requirements' + CONDA_USES_MAMBA = 'conda_uses_mamba' ALL_VERSIONS_IN_HTML_CONTEXT = 'all_versions_in_html_context' SKIP_SYNC_TAGS = 'skip_sync_tags' SKIP_SYNC_BRANCHES = 'skip_sync_branches' @@ -1661,6 +1662,10 @@ def add_features(sender, **kwargs): CONDA_APPEND_CORE_REQUIREMENTS, _('Append Read the Docs core requirements to environment.yml file'), ), + ( + CONDA_USES_MAMBA, + _('Uses mamba binary instead of conda to create the environment'), + ), ( ALL_VERSIONS_IN_HTML_CONTEXT, _(