package org.dcache.pinmanager;
import com.google.common.base.Throwables;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.leader.LeaderLatch;
import org.apache.curator.framework.recipes.leader.LeaderLatchListener;
import org.apache.curator.utils.CloseableUtils;
import org.apache.curator.utils.ZKPaths;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.dao.DataAccessException;
import javax.jdo.JDOException;
import java.util.Date;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import diskCacheV111.vehicles.PnfsDeleteEntryNotificationMessage;
import dmg.cells.nucleus.CellAddressCore;
import dmg.cells.nucleus.CellIdentityAware;
import dmg.cells.nucleus.CellLifeCycleAware;
import dmg.cells.nucleus.CellMessageReceiver;
import org.dcache.cells.CellStub;
import org.dcache.cells.CuratorFrameworkAware;
import org.dcache.poolmanager.PoolMonitor;
import org.dcache.util.FireAndForgetTask;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.dcache.pinmanager.model.Pin.State.UNPINNING;
public class PinManager
implements CellMessageReceiver, CuratorFrameworkAware, CellIdentityAware, CellLifeCycleAware
{
private static final Logger _log =
LoggerFactory.getLogger(PinManager.class);
private static final long INITIAL_EXPIRATION_DELAY = SECONDS.toMillis(15);
private static final long INITIAL_UNPIN_DELAY = SECONDS.toMillis(30);
private ScheduledExecutorService executor;
private PinDao dao;
private CellStub poolStub;
private long expirationPeriod;
private TimeUnit expirationPeriodUnit;
private PoolMonitor poolMonitor;
private CuratorFramework client;
private CellAddressCore address;
private LeaderLatch leaderLatch;
private String zkPath;
@Override
public void setCuratorFramework(CuratorFramework client)
{
this.client = client;
}
@Override
public void setCellAddress(CellAddressCore address)
{
this.address = address;
}
@Required
public void setExecutor(ScheduledExecutorService executor)
{
this.executor = executor;
}
@Required
public void setDao(PinDao dao)
{
this.dao = dao;
}
@Required
public void setPoolStub(CellStub stub)
{
poolStub = stub;
}
@Required
public void setPoolMonitor(PoolMonitor poolMonitor)
{
this.poolMonitor = poolMonitor;
}
@Required
public void setExpirationPeriod(long period)
{
expirationPeriod = period;
}
public long getExpirationPeriod()
{
return expirationPeriod;
}
@Required
public void setExpirationPeriodUnit(TimeUnit unit)
{
expirationPeriodUnit = unit;
}
public TimeUnit getExpirationPeriodUnit()
{
return expirationPeriodUnit;
}
@Required
public void setServiceName(String serviceName)
{
zkPath = getZooKeeperLeaderPath(serviceName);
}
@Override
public void afterStart()
{
try {
leaderLatch = new LeaderLatch(client, zkPath, address.toString());
leaderLatch.addListener(new LeaderListener());
leaderLatch.start();
} catch (Exception e) {
Throwables.throwIfUnchecked(e);
throw new RuntimeException(e);
}
}
@Override
public void beforeStop()
{
if (leaderLatch != null) {
CloseableUtils.closeQuietly(leaderLatch);
}
}
public PnfsDeleteEntryNotificationMessage messageArrived(PnfsDeleteEntryNotificationMessage message)
{
dao.delete(dao.where().pnfsId(message.getPnfsId()));
return message;
}
public static String getZooKeeperLeaderPath(String serviceName)
{
return ZKPaths.makePath("/dcache/pinmanager", serviceName, "leader");
}
private class ExpirationTask implements Runnable
{
@Override
public void run()
{
try {
dao.update(dao.where()
.expirationTimeBefore(new Date())
.stateIsNot(UNPINNING),
dao.set().
state(UNPINNING));
} catch (JDOException | DataAccessException e) {
_log.error("Database failure while expiring pins: {}",
e.getMessage());
} catch (RuntimeException e) {
_log.error("Unexpected failure while expiring pins", e);
}
}
}
private class LeaderListener implements LeaderLatchListener
{
private final FireAndForgetTask unpinTask =
new FireAndForgetTask(new UnpinProcessor(dao, poolStub, poolMonitor));
private final ExpirationTask expirationTask =
new ExpirationTask();
private ScheduledFuture<?> unpinFuture;
private ScheduledFuture<?> expirationFuture;
@Override
public void isLeader()
{
expirationFuture = executor.scheduleWithFixedDelay(
new FireAndForgetTask(expirationTask),
INITIAL_EXPIRATION_DELAY,
expirationPeriodUnit.toMillis(expirationPeriod),
MILLISECONDS);
unpinFuture = executor.scheduleWithFixedDelay(
unpinTask,
INITIAL_UNPIN_DELAY,
expirationPeriodUnit.toMillis(expirationPeriod),
MILLISECONDS);
}
@Override
public void notLeader()
{
expirationFuture.cancel(false);
unpinFuture.cancel(true);
}
}
}