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;
}
Aggregations