Fix spack reindex
so that it will work if DB is corrupt (duh).
- 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.
This commit is contained in:
parent
bee5c05568
commit
69b68153a1
@ -164,6 +164,9 @@ def __init__(self, root, db_dir=None):
|
|||||||
self.lock = Lock(self._lock_path)
|
self.lock = Lock(self._lock_path)
|
||||||
self._data = {}
|
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):
|
def write_transaction(self, timeout=_db_lock_timeout):
|
||||||
"""Get a write lock context manager for use in a `with` block."""
|
"""Get a write lock context manager for use in a `with` block."""
|
||||||
return WriteTransaction(self.lock, self._read, self._write, timeout)
|
return WriteTransaction(self.lock, self._read, self._write, timeout)
|
||||||
@ -256,7 +259,8 @@ def _read_from_yaml(self, stream):
|
|||||||
|
|
||||||
def check(cond, msg):
|
def check(cond, msg):
|
||||||
if not cond:
|
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.")
|
check('database' in yfile, "No 'database' attribute in YAML.")
|
||||||
|
|
||||||
@ -275,6 +279,12 @@ def check(cond, msg):
|
|||||||
self.reindex(spack.install_layout)
|
self.reindex(spack.install_layout)
|
||||||
installs = dict((k, v.to_dict()) for k, v in self._data.items())
|
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:
|
# Build up the database in three passes:
|
||||||
#
|
#
|
||||||
# 1. Read in all specs without dependencies.
|
# 1. Read in all specs without dependencies.
|
||||||
@ -298,15 +308,14 @@ def check(cond, msg):
|
|||||||
data[hash_key] = InstallRecord.from_dict(spec, rec)
|
data[hash_key] = InstallRecord.from_dict(spec, rec)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
tty.warn("Invalid database reecord:",
|
invalid_record(hash_key, e)
|
||||||
"file: %s" % self._index_path,
|
|
||||||
"hash: %s" % hash_key,
|
|
||||||
"cause: %s: %s" % (type(e).__name__, str(e)))
|
|
||||||
raise
|
|
||||||
|
|
||||||
# Pass 2: Assign dependencies once all specs are created.
|
# Pass 2: Assign dependencies once all specs are created.
|
||||||
for hash_key in data:
|
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
|
# Pass 3: Mark all specs concrete. Specs representing real
|
||||||
# installations must be explicitly marked.
|
# installations must be explicitly marked.
|
||||||
@ -324,7 +333,26 @@ def reindex(self, directory_layout):
|
|||||||
Locks the DB if it isn't locked already.
|
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
|
old_data = self._data
|
||||||
try:
|
try:
|
||||||
self._data = {}
|
self._data = {}
|
||||||
@ -333,10 +361,15 @@ def reindex(self, directory_layout):
|
|||||||
for spec in directory_layout.all_specs():
|
for spec in directory_layout.all_specs():
|
||||||
# Create a spec for each known package and add it.
|
# Create a spec for each known package and add it.
|
||||||
path = directory_layout.path_for_spec(spec)
|
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
|
explicit = False
|
||||||
if old_info is not None:
|
if old_data is not None:
|
||||||
explicit = old_info.explicit
|
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._add(spec, path, directory_layout, explicit=explicit)
|
||||||
|
|
||||||
self._check_ref_counts()
|
self._check_ref_counts()
|
||||||
@ -621,11 +654,7 @@ def missing(self, spec):
|
|||||||
|
|
||||||
|
|
||||||
class CorruptDatabaseError(SpackError):
|
class CorruptDatabaseError(SpackError):
|
||||||
|
"""Raised when errors are found while reading the database."""
|
||||||
def __init__(self, path, msg=''):
|
|
||||||
super(CorruptDatabaseError, self).__init__(
|
|
||||||
"Spack database is corrupt: %s. %s." % (path, msg),
|
|
||||||
"Try running `spack reindex` to fix.")
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidDatabaseVersionError(SpackError):
|
class InvalidDatabaseVersionError(SpackError):
|
||||||
|
Loading…
Reference in New Issue
Block a user