This is an automated email from the ASF dual-hosted git repository. tvb pushed a commit to branch jonathan/cache-cache-size in repository https://gitbox.apache.org/repos/asf/buildstream.git
commit d6bf2feffab79b1f4b93a61338f137fba08335ca Author: Jonathan Maw <[email protected]> AuthorDate: Wed Aug 15 18:08:00 2018 +0100 Use ArtifactCache's get_cache_size when calculating the quota The changes to tests are required because I moved the cache size validation into `ArtifactCache`. This matters because some tests instantiate `ArtifactCache`s without running the validation done in `Context.load`. --- buildstream/_artifactcache/artifactcache.py | 67 ++++++++++++++++++++++++-- buildstream/_artifactcache/cascache.py | 2 + buildstream/_context.py | 74 ++--------------------------- buildstream/_frontend/app.py | 6 ++- buildstream/_scheduler/queues/buildqueue.py | 2 +- buildstream/_scheduler/scheduler.py | 4 +- tests/testutils/artifactshare.py | 3 +- 7 files changed, 79 insertions(+), 79 deletions(-) diff --git a/buildstream/_artifactcache/artifactcache.py b/buildstream/_artifactcache/artifactcache.py index d98c291..95dbc08 100644 --- a/buildstream/_artifactcache/artifactcache.py +++ b/buildstream/_artifactcache/artifactcache.py @@ -82,7 +82,6 @@ class ArtifactCache(): self.extractdir = os.path.join(context.artifactdir, 'extract') self.tmpdir = os.path.join(context.artifactdir, 'tmp') - self.max_size = context.cache_quota self.estimated_size = None self.global_remote_specs = [] @@ -90,6 +89,8 @@ class ArtifactCache(): self._local = False self.cache_size = None + self.cache_quota = None + self.cache_lower_threshold = None os.makedirs(self.extractdir, exist_ok=True) os.makedirs(self.tmpdir, exist_ok=True) @@ -227,7 +228,7 @@ class ArtifactCache(): def clean(self): artifacts = self.list_artifacts() - while self.calculate_cache_size() >= self.context.cache_quota - self.context.cache_lower_threshold: + while self.calculate_cache_size() >= self.cache_quota - self.cache_lower_threshold: try: to_remove = artifacts.pop(0) except IndexError: @@ -241,7 +242,7 @@ class ArtifactCache(): "Please increase the cache-quota in {}." .format(self.context.config_origin or default_conf)) - if self.calculate_cache_size() > self.context.cache_quota: + if self.calculate_cache_size() > self.cache_quota: raise ArtifactError("Cache too full. Aborting.", detail=detail, reason="cache-too-full") @@ -551,6 +552,66 @@ class ArtifactCache(): def _set_cache_size(self, cache_size): self.estimated_size = cache_size + def _calculate_cache_quota(self): + # Headroom intended to give BuildStream a bit of leeway. + # This acts as the minimum size of cache_quota and also + # is taken from the user requested cache_quota. + # + if 'BST_TEST_SUITE' in os.environ: + headroom = 0 + else: + headroom = 2e9 + + artifactdir_volume = self.context.artifactdir + while not os.path.exists(artifactdir_volume): + artifactdir_volume = os.path.dirname(artifactdir_volume) + + try: + cache_quota = utils._parse_size(self.context.config_cache_quota, artifactdir_volume) + except utils.UtilError as e: + raise LoadError(LoadErrorReason.INVALID_DATA, + "{}\nPlease specify the value in bytes or as a % of full disk space.\n" + "\nValid values are, for example: 800M 10G 1T 50%\n" + .format(str(e))) from e + + stat = os.statvfs(artifactdir_volume) + available_space = (stat.f_bsize * stat.f_bavail) + + cache_size = self.calculate_cache_size() + + # Ensure system has enough storage for the cache_quota + # + # If cache_quota is none, set it to the maximum it could possibly be. + # + # Also check that cache_quota is atleast as large as our headroom. + # + if cache_quota is None: # Infinity, set to max system storage + cache_quota = cache_size + available_space + if cache_quota < headroom: # Check minimum + raise LoadError(LoadErrorReason.INVALID_DATA, + "Invalid cache quota ({}): ".format(utils._pretty_size(cache_quota)) + + "BuildStream requires a minimum cache quota of 2G.") + elif cache_quota > cache_size + available_space: # Check maximum + raise LoadError(LoadErrorReason.INVALID_DATA, + ("Your system does not have enough available " + + "space to support the cache quota specified.\n" + + "You currently have:\n" + + "- {used} of cache in use at {local_cache_path}\n" + + "- {available} of available system storage").format( + used=utils._pretty_size(cache_size), + local_cache_path=self.context.artifactdir, + available=utils._pretty_size(available_space))) + + # Place a slight headroom (2e9 (2GB) on the cache_quota) into + # cache_quota to try and avoid exceptions. + # + # Of course, we might still end up running out during a build + # if we end up writing more than 2G, but hey, this stuff is + # already really fuzzy. + # + self.cache_quota = cache_quota - headroom + self.cache_lower_threshold = self.cache_quota / 2 + # _configured_remote_artifact_cache_specs(): # diff --git a/buildstream/_artifactcache/cascache.py b/buildstream/_artifactcache/cascache.py index 074899d..ac21cc0 100644 --- a/buildstream/_artifactcache/cascache.py +++ b/buildstream/_artifactcache/cascache.py @@ -61,6 +61,8 @@ class CASCache(ArtifactCache): os.makedirs(os.path.join(self.casdir, 'refs', 'heads'), exist_ok=True) os.makedirs(os.path.join(self.casdir, 'objects'), exist_ok=True) + self._calculate_cache_quota() + self._enable_push = enable_push # Per-project list of _CASRemote instances. diff --git a/buildstream/_context.py b/buildstream/_context.py index 8ebb61d..a94d374 100644 --- a/buildstream/_context.py +++ b/buildstream/_context.py @@ -64,12 +64,6 @@ class Context(): # The locations from which to push and pull prebuilt artifacts self.artifact_cache_specs = [] - # The artifact cache quota - self.cache_quota = None - - # The lower threshold to which we aim to reduce the cache size - self.cache_lower_threshold = None - # The directory to store build logs self.logdir = None @@ -124,6 +118,8 @@ class Context(): self._workspaces = None self._log_handle = None self._log_filename = None + self.config_cache_quota = 'infinity' + self.artifactdir_volume = None # load() # @@ -183,71 +179,7 @@ class Context(): cache = _yaml.node_get(defaults, Mapping, 'cache') _yaml.node_validate(cache, ['quota']) - artifactdir_volume = self.artifactdir - while not os.path.exists(artifactdir_volume): - artifactdir_volume = os.path.dirname(artifactdir_volume) - - # We read and parse the cache quota as specified by the user - cache_quota = _yaml.node_get(cache, str, 'quota', default_value='infinity') - try: - cache_quota = utils._parse_size(cache_quota, artifactdir_volume) - except utils.UtilError as e: - raise LoadError(LoadErrorReason.INVALID_DATA, - "{}\nPlease specify the value in bytes or as a % of full disk space.\n" - "\nValid values are, for example: 800M 10G 1T 50%\n" - .format(str(e))) from e - - # Headroom intended to give BuildStream a bit of leeway. - # This acts as the minimum size of cache_quota and also - # is taken from the user requested cache_quota. - # - if 'BST_TEST_SUITE' in os.environ: - headroom = 0 - else: - headroom = 2e9 - - stat = os.statvfs(artifactdir_volume) - available_space = (stat.f_bsize * stat.f_bavail) - - # Again, the artifact directory may not yet have been created yet - # - if not os.path.exists(self.artifactdir): - cache_size = 0 - else: - cache_size = utils._get_dir_size(self.artifactdir) - - # Ensure system has enough storage for the cache_quota - # - # If cache_quota is none, set it to the maximum it could possibly be. - # - # Also check that cache_quota is atleast as large as our headroom. - # - if cache_quota is None: # Infinity, set to max system storage - cache_quota = cache_size + available_space - if cache_quota < headroom: # Check minimum - raise LoadError(LoadErrorReason.INVALID_DATA, - "Invalid cache quota ({}): ".format(utils._pretty_size(cache_quota)) + - "BuildStream requires a minimum cache quota of 2G.") - elif cache_quota > cache_size + available_space: # Check maximum - raise LoadError(LoadErrorReason.INVALID_DATA, - ("Your system does not have enough available " + - "space to support the cache quota specified.\n" + - "You currently have:\n" + - "- {used} of cache in use at {local_cache_path}\n" + - "- {available} of available system storage").format( - used=utils._pretty_size(cache_size), - local_cache_path=self.artifactdir, - available=utils._pretty_size(available_space))) - - # Place a slight headroom (2e9 (2GB) on the cache_quota) into - # cache_quota to try and avoid exceptions. - # - # Of course, we might still end up running out during a build - # if we end up writing more than 2G, but hey, this stuff is - # already really fuzzy. - # - self.cache_quota = cache_quota - headroom - self.cache_lower_threshold = self.cache_quota / 2 + self.config_cache_quota = _yaml.node_get(cache, str, 'quota', default_value='infinity') # Load artifact share configuration self.artifact_cache_specs = ArtifactCache.specs_from_config_node(defaults) diff --git a/buildstream/_frontend/app.py b/buildstream/_frontend/app.py index 1550fbc..1e357f1 100644 --- a/buildstream/_frontend/app.py +++ b/buildstream/_frontend/app.py @@ -198,8 +198,10 @@ class App(): option_value = self._main_options.get(cli_option) if option_value is not None: setattr(self.context, context_attr, option_value) - - Platform.create_instance(self.context) + try: + Platform.create_instance(self.context) + except BstError as e: + self._error_exit(e, "Error instantiating platform") # Create the logger right before setting the message handler self.logger = LogLine(self.context, diff --git a/buildstream/_scheduler/queues/buildqueue.py b/buildstream/_scheduler/queues/buildqueue.py index 5967fbf..2009fce 100644 --- a/buildstream/_scheduler/queues/buildqueue.py +++ b/buildstream/_scheduler/queues/buildqueue.py @@ -97,7 +97,7 @@ class BuildQueue(Queue): cache = element._get_artifact_cache() cache._add_artifact_size(artifact_size) - if cache.get_approximate_cache_size() > self._scheduler.context.cache_quota: + if cache.get_approximate_cache_size() > cache.cache_quota: self._scheduler._check_cache_size_real() def done(self, job, element, result, success): diff --git a/buildstream/_scheduler/scheduler.py b/buildstream/_scheduler/scheduler.py index 3d1d79b..38d38be 100644 --- a/buildstream/_scheduler/scheduler.py +++ b/buildstream/_scheduler/scheduler.py @@ -29,6 +29,7 @@ from contextlib import contextmanager # Local imports from .resources import Resources, ResourceType from .jobs import CacheSizeJob, CleanupJob +from .._platform import Platform # A decent return code for Scheduler.run() @@ -316,7 +317,8 @@ class Scheduler(): self._sched() def _run_cleanup(self, cache_size): - if cache_size and cache_size < self.context.cache_quota: + platform = Platform.get_platform() + if cache_size and cache_size < platform.artifactcache.cache_quota: return job = CleanupJob(self, 'cleanup', 'cleanup', diff --git a/tests/testutils/artifactshare.py b/tests/testutils/artifactshare.py index 76b729e..05e87a4 100644 --- a/tests/testutils/artifactshare.py +++ b/tests/testutils/artifactshare.py @@ -140,6 +140,7 @@ class ArtifactShare(): return statvfs_result(f_blocks=self.total_space, f_bfree=self.free_space - repo_size, + f_bavail=self.free_space - repo_size, f_bsize=1) @@ -156,4 +157,4 @@ def create_artifact_share(directory, *, total_space=None, free_space=None): share.close() -statvfs_result = namedtuple('statvfs_result', 'f_blocks f_bfree f_bsize') +statvfs_result = namedtuple('statvfs_result', 'f_blocks f_bfree f_bsize f_bavail')
