[Notes] [Git][BuildStream/buildstream][master] 2 commits: _artifactcache.py: Don't require the quota to be available on disk.



Title: GitLab

Tristan Van Berkom pushed to branch master at BuildStream / buildstream

Commits:

4 changed files:

Changes:

  • buildstream/_artifactcache.py
    ... ... @@ -98,6 +98,7 @@ class ArtifactCache():
    98 98
             self._cache_size = None               # The current cache size, sometimes it's an estimate
    
    99 99
             self._cache_quota = None              # The cache quota
    
    100 100
             self._cache_quota_original = None     # The cache quota as specified by the user, in bytes
    
    101
    +        self._cache_quota_headroom = None     # The headroom in bytes before reaching the quota or full disk
    
    101 102
             self._cache_lower_threshold = None    # The target cache size for a cleanup
    
    102 103
             self._remotes_setup = False           # Check to prevent double-setup of remotes
    
    103 104
     
    
    ... ... @@ -314,7 +315,7 @@ class ArtifactCache():
    314 315
                                       len(self._required_elements),
    
    315 316
                                       (context.config_origin or default_conf)))
    
    316 317
     
    
    317
    -                if self.has_quota_exceeded():
    
    318
    +                if self.full():
    
    318 319
                         raise ArtifactError("Cache too full. Aborting.",
    
    319 320
                                             detail=detail,
    
    320 321
                                             reason="cache-too-full")
    
    ... ... @@ -431,15 +432,25 @@ class ArtifactCache():
    431 432
             self._cache_size = cache_size
    
    432 433
             self._write_cache_size(self._cache_size)
    
    433 434
     
    
    434
    -    # has_quota_exceeded()
    
    435
    +    # full()
    
    435 436
         #
    
    436
    -    # Checks if the current artifact cache size exceeds the quota.
    
    437
    +    # Checks if the artifact cache is full, either
    
    438
    +    # because the user configured quota has been exceeded
    
    439
    +    # or because the underlying disk is almost full.
    
    437 440
         #
    
    438 441
         # Returns:
    
    439
    -    #    (bool): True of the quota is exceeded
    
    442
    +    #    (bool): True if the artifact cache is full
    
    440 443
         #
    
    441
    -    def has_quota_exceeded(self):
    
    442
    -        return self.get_cache_size() > self._cache_quota
    
    444
    +    def full(self):
    
    445
    +
    
    446
    +        if self.get_cache_size() > self._cache_quota:
    
    447
    +            return True
    
    448
    +
    
    449
    +        _, volume_avail = self._get_cache_volume_size()
    
    450
    +        if volume_avail < self._cache_quota_headroom:
    
    451
    +            return True
    
    452
    +
    
    453
    +        return False
    
    443 454
     
    
    444 455
         # preflight():
    
    445 456
         #
    
    ... ... @@ -936,9 +947,9 @@ class ArtifactCache():
    936 947
             # is taken from the user requested cache_quota.
    
    937 948
             #
    
    938 949
             if 'BST_TEST_SUITE' in os.environ:
    
    939
    -            headroom = 0
    
    950
    +            self._cache_quota_headroom = 0
    
    940 951
             else:
    
    941
    -            headroom = 2e9
    
    952
    +            self._cache_quota_headroom = 2e9
    
    942 953
     
    
    943 954
             try:
    
    944 955
                 cache_quota = utils._parse_size(self.context.config_cache_quota,
    
    ... ... @@ -960,27 +971,39 @@ class ArtifactCache():
    960 971
             #
    
    961 972
             if cache_quota is None:  # Infinity, set to max system storage
    
    962 973
                 cache_quota = cache_size + available_space
    
    963
    -        if cache_quota < headroom:  # Check minimum
    
    974
    +        if cache_quota < self._cache_quota_headroom:  # Check minimum
    
    964 975
                 raise LoadError(LoadErrorReason.INVALID_DATA,
    
    965 976
                                 "Invalid cache quota ({}): ".format(utils._pretty_size(cache_quota)) +
    
    966 977
                                 "BuildStream requires a minimum cache quota of 2G.")
    
    967
    -        elif cache_quota > cache_size + available_space:  # Check maximum
    
    968
    -            if '%' in self.context.config_cache_quota:
    
    969
    -                available = (available_space / total_size) * 100
    
    970
    -                available = '{}% of total disk space'.format(round(available, 1))
    
    971
    -            else:
    
    972
    -                available = utils._pretty_size(available_space)
    
    973
    -
    
    978
    +        elif cache_quota > total_size:
    
    979
    +            # A quota greater than the total disk size is certianly an error
    
    974 980
                 raise ArtifactError("Your system does not have enough available " +
    
    975 981
                                     "space to support the cache quota specified.",
    
    976 982
                                     detail=("You have specified a quota of {quota} total disk space.\n" +
    
    977 983
                                             "The filesystem containing {local_cache_path} only " +
    
    978
    -                                        "has {available_size} available.")
    
    984
    +                                        "has {total_size} total disk space.")
    
    979 985
                                     .format(
    
    980 986
                                         quota=self.context.config_cache_quota,
    
    981 987
                                         local_cache_path=self.context.artifactdir,
    
    982
    -                                    available_size=available),
    
    988
    +                                    total_size=utils._pretty_size(total_size)),
    
    983 989
                                     reason='insufficient-storage-for-quota')
    
    990
    +        elif cache_quota > cache_size + available_space:
    
    991
    +            # The quota does not fit in the available space, this is a warning
    
    992
    +            if '%' in self.context.config_cache_quota:
    
    993
    +                available = (available_space / total_size) * 100
    
    994
    +                available = '{}% of total disk space'.format(round(available, 1))
    
    995
    +            else:
    
    996
    +                available = utils._pretty_size(available_space)
    
    997
    +
    
    998
    +            self._message(MessageType.WARN,
    
    999
    +                          "Your system does not have enough available " +
    
    1000
    +                          "space to support the cache quota specified.",
    
    1001
    +                          detail=("You have specified a quota of {quota} total disk space.\n" +
    
    1002
    +                                  "The filesystem containing {local_cache_path} only " +
    
    1003
    +                                  "has {available_size} available.")
    
    1004
    +                          .format(quota=self.context.config_cache_quota,
    
    1005
    +                                  local_cache_path=self.context.artifactdir,
    
    1006
    +                                  available_size=available))
    
    984 1007
     
    
    985 1008
             # Place a slight headroom (2e9 (2GB) on the cache_quota) into
    
    986 1009
             # cache_quota to try and avoid exceptions.
    
    ... ... @@ -990,7 +1013,7 @@ class ArtifactCache():
    990 1013
             # already really fuzzy.
    
    991 1014
             #
    
    992 1015
             self._cache_quota_original = cache_quota
    
    993
    -        self._cache_quota = cache_quota - headroom
    
    1016
    +        self._cache_quota = cache_quota - self._cache_quota_headroom
    
    994 1017
             self._cache_lower_threshold = self._cache_quota / 2
    
    995 1018
     
    
    996 1019
         # _get_cache_volume_size()
    

  • buildstream/_scheduler/queues/buildqueue.py
    ... ... @@ -100,7 +100,7 @@ class BuildQueue(Queue):
    100 100
             # If the estimated size outgrows the quota, ask the scheduler
    
    101 101
             # to queue a job to actually check the real cache size.
    
    102 102
             #
    
    103
    -        if artifacts.has_quota_exceeded():
    
    103
    +        if artifacts.full():
    
    104 104
                 self._scheduler.check_cache_size()
    
    105 105
     
    
    106 106
         def done(self, job, element, result, status):
    

  • buildstream/_scheduler/scheduler.py
    ... ... @@ -303,7 +303,7 @@ class Scheduler():
    303 303
             # starts while we are checking the cache.
    
    304 304
             #
    
    305 305
             artifacts = self.context.artifactcache
    
    306
    -        if artifacts.has_quota_exceeded():
    
    306
    +        if artifacts.full():
    
    307 307
                 self._sched_cache_size_job(exclusive=True)
    
    308 308
     
    
    309 309
         # _spawn_job()
    
    ... ... @@ -338,7 +338,7 @@ class Scheduler():
    338 338
             context = self.context
    
    339 339
             artifacts = context.artifactcache
    
    340 340
     
    
    341
    -        if artifacts.has_quota_exceeded():
    
    341
    +        if artifacts.full():
    
    342 342
                 self._cleanup_scheduled = True
    
    343 343
     
    
    344 344
         # Callback for the cleanup job
    

  • tests/artifactcache/expiry.py
    ... ... @@ -317,6 +317,16 @@ def test_never_delete_required_track(cli, datafiles, tmpdir):
    317 317
     # has 10K total disk space, and 6K of it is already in use (not
    
    318 318
     # including any space used by the artifact cache).
    
    319 319
     #
    
    320
    +# Parameters:
    
    321
    +#    quota (str): A quota size configuration for the config file
    
    322
    +#    err_domain (str): An ErrorDomain, or 'success' or 'warning'
    
    323
    +#    err_reason (str): A reson to compare with an error domain
    
    324
    +#
    
    325
    +# If err_domain is 'success', then err_reason is unused.
    
    326
    +#
    
    327
    +# If err_domain is 'warning', then err_reason is asserted to
    
    328
    +# be in the stderr.
    
    329
    +#
    
    320 330
     @pytest.mark.parametrize("quota,err_domain,err_reason", [
    
    321 331
         # Valid configurations
    
    322 332
         ("1", 'success', None),
    
    ... ... @@ -328,9 +338,13 @@ def test_never_delete_required_track(cli, datafiles, tmpdir):
    328 338
         ("-1", ErrorDomain.LOAD, LoadErrorReason.INVALID_DATA),
    
    329 339
         ("pony", ErrorDomain.LOAD, LoadErrorReason.INVALID_DATA),
    
    330 340
         ("200%", ErrorDomain.LOAD, LoadErrorReason.INVALID_DATA),
    
    341
    +
    
    342
    +    # Not enough space on disk even if you cleaned up
    
    343
    +    ("11K", ErrorDomain.ARTIFACT, 'insufficient-storage-for-quota'),
    
    344
    +
    
    331 345
         # Not enough space for these caches
    
    332
    -    ("7K", ErrorDomain.ARTIFACT, 'insufficient-storage-for-quota'),
    
    333
    -    ("70%", ErrorDomain.ARTIFACT, 'insufficient-storage-for-quota')
    
    346
    +    ("7K", 'warning', 'Your system does not have enough available'),
    
    347
    +    ("70%", 'warning', 'Your system does not have enough available')
    
    334 348
     ])
    
    335 349
     @pytest.mark.datafiles(DATA_DIR)
    
    336 350
     def test_invalid_cache_quota(cli, datafiles, tmpdir, quota, err_domain, err_reason):
    
    ... ... @@ -374,6 +388,8 @@ def test_invalid_cache_quota(cli, datafiles, tmpdir, quota, err_domain, err_reas
    374 388
     
    
    375 389
         if err_domain == 'success':
    
    376 390
             res.assert_success()
    
    391
    +    elif err_domain == 'warning':
    
    392
    +        assert err_reason in res.stderr
    
    377 393
         else:
    
    378 394
             res.assert_main_error(err_domain, err_reason)
    
    379 395
     
    



  • [Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]