Search in sources :

Example 1 with LoadLockedException

use of bio.terra.service.load.exception.LoadLockedException in project jade-data-repo by DataBiosphere.

the class LoadDao method lockLoad.

// -- load tags public methods --
// This must be serializable so that conflicting updates of the locked state and flightid
// are detected. We lock the table so that we avoid serialization errors.
/**
 * We implement a rule that one load job can use one load tag at a time. That rule is needed to control
 * concurrent operations. For example, a delete-by-load-tag cannot compete with a load; two loads cannot
 * run in parallel with the same load tag - it confuses the algorithm for re-running a load with a load tag
 * and skipping already-loaded files.
 *
 * This call and the unlock call use a load table in the database to record that a load tag is in use.
 * The load tag is associated with a load id (a guid); that guid is a foreign key to the load_file table
 * that maintains the state of files being loaded.
 *
 * We expect conflicts on load tags to be rare. The typical case will be: a load starts, runs, and ends
 * without conflict and with a re-run.
 *
 * We learned from the first implementation of this code that when there were conflicts, we would get
 * serialization errors from Postgres. Those require building retry logic. Instead, we chose to use
 * table locks to serialize access to the load table during the time we are setting and freeing the
 * our load lock state.
 *
 * A lock is taken by creating the load tag row and storing the flight id holding the lock.
 * The lock is freed by deleting the load tag row. Code can safely re-lock a load tag lock it holds and
 * unlock a load tag lock it has freed.
 *
 * There is never a case where a lock row is updated. They are only ever inserted or deleted.
 *
 * @param loadTag tag identifying this load
 * @param flightId flight id taking the lock
 * @return Load object including the load id
 */
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.SERIALIZABLE)
public Load lockLoad(String loadTag, String flightId) throws InterruptedException {
    jdbcTemplate.getJdbcTemplate().execute("LOCK TABLE load IN EXCLUSIVE MODE");
    String upsert = "INSERT INTO load (load_tag, locked, locking_flight_id)" + " VALUES (:load_tag, true, :flight_id)" + " ON CONFLICT ON CONSTRAINT load_load_tag_key DO NOTHING";
    MapSqlParameterSource params = new MapSqlParameterSource().addValue("load_tag", loadTag).addValue("flight_id", flightId);
    DaoKeyHolder keyHolder = new DaoKeyHolder();
    int rows = jdbcTemplate.update(upsert, params, keyHolder);
    Load load;
    if (rows == 0) {
        // We did not insert. Therefore, someone has the load tag locked.
        // Retrieve it, in case it is us re-locking
        load = lookupLoadByTag(loadTag);
        if (load == null) {
            throw new CorruptMetadataException("Load row should exist! Load tag: " + loadTag);
        }
        // It is locked by someone else
        if (!StringUtils.equals(load.getLockingFlightId(), flightId)) {
            throw new LoadLockedException("Load '" + loadTag + "' is locked by flight '" + load.getLockingFlightId() + "'");
        }
    } else {
        load = new Load().id(keyHolder.getId()).loadTag(keyHolder.getString("load_tag")).locked(keyHolder.getField("locked", Boolean.class)).lockingFlightId(keyHolder.getString("locking_flight_id"));
    }
    return load;
}
Also used : MapSqlParameterSource(org.springframework.jdbc.core.namedparam.MapSqlParameterSource) DaoKeyHolder(bio.terra.common.DaoKeyHolder) LoadLockedException(bio.terra.service.load.exception.LoadLockedException) CorruptMetadataException(bio.terra.service.snapshot.exception.CorruptMetadataException) Transactional(org.springframework.transaction.annotation.Transactional)

Aggregations

DaoKeyHolder (bio.terra.common.DaoKeyHolder)1 LoadLockedException (bio.terra.service.load.exception.LoadLockedException)1 CorruptMetadataException (bio.terra.service.snapshot.exception.CorruptMetadataException)1 MapSqlParameterSource (org.springframework.jdbc.core.namedparam.MapSqlParameterSource)1 Transactional (org.springframework.transaction.annotation.Transactional)1