package com.xiaomi.infra.chronos;
import java.io.IOException;
import java.util.Arrays;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import com.xiaomi.infra.chronos.exception.FatalChronosException;
import com.xiaomi.infra.chronos.exception.ChronosException;
import com.xiaomi.infra.chronos.zookeeper.FailoverWatcher;
import com.xiaomi.infra.chronos.zookeeper.ZooKeeperUtil;
/**
* The ZooKeeper watcher for ChronosServer provides the interface to get the persistent timestamp
* which is the max value allocated currently.
*/
public class ChronosServerWatcher extends FailoverWatcher {
private static final Log LOG = LogFactory.getLog(ChronosServerWatcher.class);
private final String persistentTimestampZnode;
private boolean beenActiveMaster = false;
private long persistentTimestamp;
/**
* Construct ChronosServerWatcher with properties.
*
* @param properties the properties of ChronosServerWatcher
* @param canInitZnode whether it can create znode or not
* @throws IOException when it's error to construct ZooKeeper watcher
*/
public ChronosServerWatcher(Properties properties, boolean canInitZnode) throws IOException {
super(properties, canInitZnode);
persistentTimestampZnode = baseZnode + "/persistent-timestamp";
}
/**
* Construct ChronosServerWatcher with properties.
*
* @param properties the properties of ChronosServerWatcher
* @throws IOException when it's error to construct ZooKeeper watcher
*/
public ChronosServerWatcher(Properties properties) throws IOException {
this(properties, true);
}
/**
* Create the base znode of chronos.
*/
@Override
protected void initZnode() {
try {
ZooKeeperUtil.createAndFailSilent(this, "/chronos");
super.initZnode();
ZooKeeperUtil.createAndFailSilent(this, baseZnode + "/persistent-timestamp");
} catch (Exception e) {
LOG.fatal("Error to create znode of chronos, exit immediately");
System.exit(0);
}
}
/**
* Get persistent timestamp in ZooKeeper with retries.
*
* @return the persistent timestamp in ZooKeeper
* @throws ChronosException error to get data from ZooKeeper after retrying
*/
public long getPersistentTimestamp() throws ChronosException {
byte[] persistentTimesampBytes = null;
for (int i = 0; i <= connectRetryTimes; ++i) {
try {
persistentTimesampBytes = ZooKeeperUtil.getDataAndWatch(this, persistentTimestampZnode);
break;
} catch (KeeperException e) {
if (i == connectRetryTimes) {
throw new ChronosException(
"Can't get persistent timestamp from ZooKeeper after retrying", e);
}
LOG.info("Exception to get persistent timestamp from ZooKeeper, retry " + (i + 1) + " times");
}
}
if (Arrays.equals(persistentTimesampBytes, new byte[0])) {
persistentTimestamp = 0; // for the very first time
} else {
persistentTimestamp = ZooKeeperUtil.bytesToLong(persistentTimesampBytes);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Return persistent timestamp " + persistentTimestamp + " to timestamp server");
}
return persistentTimestamp;
}
/**
* Get the cached timestamp in ChronosServerWatcher.
*
* @return the local persistentTimestamp
*/
public long getCachedPersistentTimestamp() {
return persistentTimestamp;
}
/**
* Set persistent timestamp in ZooKeeper.
*
* @param newTimestamp the new value to set
* @throws FatalChronosException if try to set a small value
* @throws ChronosException if error to set value in ZooKeeper
*/
public void setPersistentTimestamp(long newTimestamp) throws FatalChronosException,
ChronosException {
if (newTimestamp <= persistentTimestamp) {
throw new FatalChronosException("Fatal error to set a smaller timestamp");
}
if (LOG.isDebugEnabled()) {
LOG.debug("Setting persistent timestamp " + newTimestamp + " in ZooKeeper");
}
for (int i = 0; i <= connectRetryTimes; ++i) {
try {
ZooKeeperUtil.setData(this, persistentTimestampZnode, ZooKeeperUtil.longToBytes(newTimestamp));
persistentTimestamp = newTimestamp;
break;
} catch (KeeperException e) {
if (i == connectRetryTimes) {
throw new ChronosException(
"Error to set persistent timestamp in ZooKeeper after retrying", e);
}
LOG.info("Exception to set persistent timestamp in ZooKeeper, retry " + (i + 1) + " times");
}
}
}
/**
* Deal with the connection event.
*
* @param event the ZooKeeper event.
*/
@Override
protected void processConnection(WatchedEvent event) {
super.processConnection(event);
switch (event.getState()) {
case Disconnected:
if (beenActiveMaster) {
LOG.fatal(hostPort.getHostPort()
+ " disconnected from ZooKeeper, stop serving and exit immediately");
System.exit(0);
} else {
LOG.warn(hostPort.getHostPort()
+ " disconnected from ZooKeeper, wait to sync and try to become active master");
}
break;
default:
break;
}
}
public String getPersistentTimestampZnode(){
return persistentTimestampZnode;
}
public void setBeenActiveMaster(boolean beenActiveMaster) {
this.beenActiveMaster = beenActiveMaster;
}
}