Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Partial cache read fix #3834

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
- Fix occasional test failures caused by not being able to find a file or directory fixture
when running multiple tests with multiple jobs.

From Raven Kopelman (@ravenAtSafe on GitHub):
- Prevent files from being partially read from the cache. If there's an error while copying the file
is now removed.

From Joachim Kuebart:
- Suppress missing SConscript deprecation warning if `must_exist=False`
is used.
Expand All @@ -48,6 +52,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
From Daniel Moody:
- Fix issue where java parsed a class incorrectly from lambdas used after a new.


From Mats Wichmann:
- Complete tests for Dictionary, env.keys() and env.values() for
OverrideEnvironment. Enable env.setdefault() method, add tests.
Expand Down
15 changes: 13 additions & 2 deletions SCons/CacheDir.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import os
import stat
import sys
import shutil

import SCons.Action
import SCons.Errors
Expand All @@ -50,18 +51,28 @@ def CacheRetrieveFunc(target, source, env):
cd.CacheDebug('CacheRetrieve(%s): %s not in cache\n', t, cachefile)
return 1
cd.hits += 1
cd.CacheDebug('CacheRetrieve(%s): retrieving from %s\n', t, cachefile)
if SCons.Action.execute_actions:
if fs.islink(cachefile):
fs.symlink(fs.readlink(cachefile), t.get_internal_path())
else:
env.copy_from_cache(cachefile, t.get_internal_path())
try:
env.copy_from_cache(cachefile, t.get_internal_path())
except (shutil.SameFileError, IOError) as e:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the SameFileError is going to leave a remnant, as it's raised before any copying is attempted.

# In case file was partially retrieved (and now corrupt)
# delete it to avoid poisoning commands like 'ar' that
# read from the initial state of the file they are writing
# to.
t.fs.unlink(t.get_internal_path())
cd.CacheDebug('CacheRetrieve(%s): Error while retrieving from %s deleting %s\n', t, cachefile)
raise e
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here shouldn't we behave as if the file were not in the cache, i.e. return 1, rather than raise?


try:
os.utime(cachefile, None)
except OSError:
pass
st = fs.stat(cachefile)
fs.chmod(t.get_internal_path(), stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
cd.CacheDebug('CacheRetrieve(%s): retrieved from %s\n', t, cachefile)
return 0

def CacheRetrieveString(target, source, env):
Expand Down
7 changes: 5 additions & 2 deletions SCons/Taskmaster.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,10 +223,13 @@ def execute(self):
cached_targets.append(t)
if len(cached_targets) < len(self.targets):
# Remove targets before building. It's possible that we
# partially retrieved targets from the cache, leaving
# them in read-only mode. That might cause the command
# retrieved a subset of targets from the cache, leaving
# them in an inconsistent state. That might cause the command
# to fail.
#
# Note that retrieve_from_cache() ensures no single target can
# be partially retrieved (file left in corrupt state).
#
for t in cached_targets:
try:
t.fs.unlink(t.get_internal_path())
Expand Down
19 changes: 9 additions & 10 deletions test/CacheDir/debug.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
# __COPYRIGHT__
# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
Expand All @@ -22,7 +22,6 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#

__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"

"""
Test the --cache-debug option to see if it prints the expected messages.
Expand Down Expand Up @@ -145,16 +144,16 @@ def cat(env, source, target):

expect = \
r"""Retrieved `aaa.out' from cache
CacheRetrieve\(aaa.out\): retrieving from [0-9a-fA-F]+
CacheRetrieve\(aaa.out\): retrieved from [0-9a-fA-F]+
requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}%
Retrieved `bbb.out' from cache
CacheRetrieve\(bbb.out\): retrieving from [0-9a-fA-F]+
CacheRetrieve\(bbb.out\): retrieved from [0-9a-fA-F]+
requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}%
Retrieved `ccc.out' from cache
CacheRetrieve\(ccc.out\): retrieving from [0-9a-fA-F]+
CacheRetrieve\(ccc.out\): retrieved from [0-9a-fA-F]+
requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}%
Retrieved `all' from cache
CacheRetrieve\(all\): retrieving from [0-9a-fA-F]+
CacheRetrieve\(all\): retrieved from [0-9a-fA-F]+
requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}%
"""

Expand All @@ -179,13 +178,13 @@ def cat(env, source, target):
stdout=expect)

expect = \
r"""CacheRetrieve\(aaa.out\): retrieving from [0-9a-fA-F]+
r"""CacheRetrieve\(aaa.out\): retrieved from [0-9a-fA-F]+
requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}%
CacheRetrieve\(bbb.out\): retrieving from [0-9a-fA-F]+
CacheRetrieve\(bbb.out\): retrieved from [0-9a-fA-F]+
requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}%
CacheRetrieve\(ccc.out\): retrieving from [0-9a-fA-F]+
CacheRetrieve\(ccc.out\): retrieved from [0-9a-fA-F]+
requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}%
CacheRetrieve\(all\): retrieving from [0-9a-fA-F]+
CacheRetrieve\(all\): retrieved from [0-9a-fA-F]+
requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}%
"""

Expand Down