Skip to content

Commit

Permalink
Fix spack reindex so that it will work if DB is corrupt (duh).
Browse files Browse the repository at this point in the history
- Transaction logic had gotten complicated -- DB would not reindex when
  corrupt, rather the error would be reported (ugh).

- DB will now print the error and force a rebuild when errors are
  detected reading the old databse.
  • Loading branch information
tgamblin committed Sep 1, 2016
1 parent bee5c05 commit 69b6815
Showing 1 changed file with 45 additions and 16 deletions.
61 changes: 45 additions & 16 deletions lib/spack/spack/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ def __init__(self, root, db_dir=None):
self.lock = Lock(self._lock_path)
self._data = {}

# whether there was an error at the start of a read transaction
self._error = None

def write_transaction(self, timeout=_db_lock_timeout):
"""Get a write lock context manager for use in a `with` block."""
return WriteTransaction(self.lock, self._read, self._write, timeout)
Expand Down Expand Up @@ -256,7 +259,8 @@ def _read_from_yaml(self, stream):

def check(cond, msg):
if not cond:
raise CorruptDatabaseError(self._index_path, msg)
raise CorruptDatabaseError(
"Spack database is corrupt: %s" % msg, self._index_path)

check('database' in yfile, "No 'database' attribute in YAML.")

Expand All @@ -275,6 +279,12 @@ def check(cond, msg):
self.reindex(spack.install_layout)
installs = dict((k, v.to_dict()) for k, v in self._data.items())

def invalid_record(hash_key, error):
msg = ("Invalid record in Spack database: "
"hash: %s, cause: %s: %s")
msg %= (hash_key, type(e).__name__, str(e))
raise CorruptDatabaseError(msg, self._index_path)

# Build up the database in three passes:
#
# 1. Read in all specs without dependencies.
Expand All @@ -298,15 +308,14 @@ def check(cond, msg):
data[hash_key] = InstallRecord.from_dict(spec, rec)

except Exception as e:
tty.warn("Invalid database reecord:",
"file: %s" % self._index_path,
"hash: %s" % hash_key,
"cause: %s: %s" % (type(e).__name__, str(e)))
raise
invalid_record(hash_key, e)

# Pass 2: Assign dependencies once all specs are created.
for hash_key in data:
self._assign_dependencies(hash_key, installs, data)
try:
self._assign_dependencies(hash_key, installs, data)
except Exception as e:
invalid_record(hash_key, e)

# Pass 3: Mark all specs concrete. Specs representing real
# installations must be explicitly marked.
Expand All @@ -324,7 +333,26 @@ def reindex(self, directory_layout):
Locks the DB if it isn't locked already.
"""
with self.write_transaction():
# Special transaction to avoid recursive reindex calls and to
# ignore errors if we need to rebuild a corrupt database.
def _read_suppress_error():
try:
if os.path.isfile(self._index_path):
self._read_from_yaml(self._index_path)
except CorruptDatabaseError as e:
self._error = e
self._data = {}

transaction = WriteTransaction(
self.lock, _read_suppress_error, self._write, _db_lock_timeout)

with transaction:
if self._error:
tty.warn(
"Spack database was corrupt. Will rebuild. Error was:",
str(self._error))
self._error = None

old_data = self._data
try:
self._data = {}
Expand All @@ -333,10 +361,15 @@ def reindex(self, directory_layout):
for spec in directory_layout.all_specs():
# Create a spec for each known package and add it.
path = directory_layout.path_for_spec(spec)
old_info = old_data.get(spec.dag_hash())

# Try to recover explicit value from old DB, but
# default it to False if DB was corrupt.
explicit = False
if old_info is not None:
explicit = old_info.explicit
if old_data is not None:
old_info = old_data.get(spec.dag_hash())
if old_info is not None:
explicit = old_info.explicit

self._add(spec, path, directory_layout, explicit=explicit)

self._check_ref_counts()
Expand Down Expand Up @@ -621,11 +654,7 @@ def missing(self, spec):


class CorruptDatabaseError(SpackError):

def __init__(self, path, msg=''):
super(CorruptDatabaseError, self).__init__(
"Spack database is corrupt: %s. %s." % (path, msg),
"Try running `spack reindex` to fix.")
"""Raised when errors are found while reading the database."""


class InvalidDatabaseVersionError(SpackError):
Expand Down

0 comments on commit 69b6815

Please sign in to comment.