def __init__(self, root, db_dir=None): """Create a Database for Spack installations under ``root``. A Database is a cache of Specs data from ``$prefix/spec.yaml`` files in Spack installation directories. By default, Database files (data and lock files) are stored under ``root/.spack-db``, which is created if it does not exist. This is the ``db_dir``. The Database will attempt to read an ``index.json`` file in ``db_dir``. If it does not find one, it will fall back to read an ``index.yaml`` if one is present. If that does not exist, it will create a database when needed by scanning the entire Database root for ``spec.yaml`` files according to Spack's ``DirectoryLayout``. Caller may optionally provide a custom ``db_dir`` parameter where data will be stored. This is intended to be used for testing the Database class. """ self.root = root if db_dir is None: # If the db_dir is not provided, default to within the db root. self._db_dir = os.path.join(self.root, _db_dirname) else: # Allow customizing the database directory location for testing. self._db_dir = db_dir # Set up layout of database files within the db dir self._old_yaml_index_path = os.path.join(self._db_dir, 'index.yaml') self._index_path = os.path.join(self._db_dir, 'index.json') self._lock_path = os.path.join(self._db_dir, 'lock') # This is for other classes to use to lock prefix directories. self.prefix_lock_path = os.path.join(self._db_dir, 'prefix_lock') # Create needed directories and files if not os.path.exists(self._db_dir): mkdirp(self._db_dir) # initialize rest of state. self.db_lock_timeout = ( spack.config.get('config:db_lock_timeout') or _db_lock_timeout) self.package_lock_timeout = ( spack.config.get('config:package_lock_timeout') or None) tty.debug('DATABASE LOCK TIMEOUT: {0}s'.format( str(self.db_lock_timeout))) timeout_format_str = ('{0}s'.format(str(self.package_lock_timeout)) if self.package_lock_timeout else 'No timeout') tty.debug('PACKAGE LOCK TIMEOUT: {0}'.format( str(timeout_format_str))) self.lock = Lock(self._lock_path, default_timeout=self.db_lock_timeout) self._data = {} # whether there was an error at the start of a read transaction self._error = None
def prefix_lock(self, spec): """Get a lock on a particular spec's installation directory. NOTE: The installation directory **does not** need to exist. Prefix lock is a byte range lock on the nth byte of a file. The lock file is ``spack.store.db.prefix_lock`` -- the DB tells us what to call it and it lives alongside the install DB. n is the sys.maxsize-bit prefix of the DAG hash. This makes likelihood of collision is very low AND it gives us readers-writer lock semantics with just a single lockfile, so no cleanup required. """ prefix = spec.prefix if prefix not in self._prefix_locks: self._prefix_locks[prefix] = Lock( self.prefix_lock_path, spec.dag_hash_bit_prefix(bit_length(sys.maxsize)), 1) return self._prefix_locks[prefix]
def _get_lock(self, key): """Create a lock for a key, if necessary, and return a lock object.""" if key not in self._locks: self._locks[key] = Lock(self._lock_path(key), default_timeout=self.lock_timeout) return self._locks[key]