package org.dcache.pinmanager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.dao.DataAccessException; import org.springframework.remoting.RemoteConnectFailureException; import org.springframework.transaction.annotation.Transactional; import javax.jdo.JDOException; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import diskCacheV111.poolManager.PoolSelectionUnit; import diskCacheV111.util.CacheException; import diskCacheV111.vehicles.PoolSetStickyMessage; import dmg.cells.nucleus.CellPath; import org.dcache.cells.AbstractMessageCallback; import org.dcache.cells.CellStub; import org.dcache.pinmanager.model.Pin; import org.dcache.poolmanager.PoolMonitor; /** * Performs the work of unpinning files. * * When an unpin request is received a pin is put into state * UNPINNING. The actual work to unpin a file is performed * independently of the unpin request. */ public class UnpinProcessor implements Runnable { private static final Logger _logger = LoggerFactory.getLogger(UnpinProcessor.class); private static final int MAX_RUNNING = 1000; private final PinDao _dao; private final CellStub _poolStub; private final PoolMonitor _poolMonitor; public UnpinProcessor(PinDao dao, CellStub poolStub, PoolMonitor poolMonitor) { _dao = dao; _poolStub = poolStub; _poolMonitor = poolMonitor; } @Override public void run() { final ExecutorService executor = Executors.newSingleThreadExecutor(); try { Semaphore idle = new Semaphore(MAX_RUNNING); unpin(idle, executor); idle.acquire(MAX_RUNNING); } catch (InterruptedException e) { _logger.debug(e.toString()); } catch (JDOException | DataAccessException e) { _logger.error("Database failure while unpinning: {}", e.getMessage()); } catch (RemoteConnectFailureException e) { _logger.error("Remote connection failure while unpinning: {)", e.getMessage()); } catch (RuntimeException e) { _logger.error("Unexpected failure while unpinning", e); } finally { executor.shutdown(); } } @Transactional protected void unpin(final Semaphore idle, final Executor executor) throws InterruptedException { _dao.foreach(_dao.where().state(Pin.State.UNPINNING), pin -> upin(idle, executor, pin)); } private void upin(Semaphore idle, Executor executor, Pin pin) throws InterruptedException { if (pin.getPool() == null) { _dao.delete(pin); } else { clearStickyFlag(idle, pin, executor); } } private void clearStickyFlag(final Semaphore idle, final Pin pin, Executor executor) throws InterruptedException { PoolSelectionUnit.SelectionPool pool = _poolMonitor.getPoolSelectionUnit().getPool(pin.getPool()); if (pool == null || !pool.isActive()) { _logger.warn("Unable to clear sticky flag because pool {} is unavailable", pin.getPool()); return; } idle.acquire(); PoolSetStickyMessage msg = new PoolSetStickyMessage(pin.getPool(), pin.getPnfsId(), false, pin.getSticky(), 0); CellStub.addCallback(_poolStub.send(new CellPath(pool.getAddress()), msg), new AbstractMessageCallback<PoolSetStickyMessage>() { @Override public void success(PoolSetStickyMessage msg) { idle.release(); _dao.delete(pin); } @Override public void failure(int rc, Object error) { idle.release(); switch (rc) { case CacheException.FILE_NOT_IN_REPOSITORY: _dao.delete(pin); break; default: _logger.warn("Failed to clear sticky flag: {} [{}]", error, rc); break; } } }, executor); } }