package edu.berkeley.thebes.hat.server.replica;
import com.yammer.metrics.Metrics;
import com.yammer.metrics.core.Meter;
import com.yammer.metrics.core.Timer;
import com.yammer.metrics.core.TimerContext;
import org.apache.thrift.TException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import edu.berkeley.thebes.common.data.DataItem;
import edu.berkeley.thebes.common.data.Version;
import edu.berkeley.thebes.common.persistence.IPersistenceEngine;
import edu.berkeley.thebes.common.thrift.ThriftDataItem;
import edu.berkeley.thebes.common.thrift.ThriftVersion;
import edu.berkeley.thebes.hat.common.thrift.ReplicaService;
import edu.berkeley.thebes.hat.server.antientropy.clustering.AntiEntropyServiceRouter;
import edu.berkeley.thebes.hat.server.dependencies.DependencyResolver;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class ReplicaServiceHandler implements ReplicaService.Iface {
private IPersistenceEngine persistenceEngine;
private AntiEntropyServiceRouter antiEntropyRouter;
private DependencyResolver dependencyResolver;
private static Logger logger = LoggerFactory.getLogger(ReplicaServiceHandler.class);
Meter putMeter = Metrics.newMeter(ReplicaServiceHandler.class,
"put-requests",
"requests",
TimeUnit.SECONDS);
Meter getMeter = Metrics.newMeter(ReplicaServiceHandler.class,
"get-requests",
"requests",
TimeUnit.SECONDS);
Meter nullVersionsMeter = Metrics.newMeter(ReplicaServiceHandler.class, "num-null-get-versions",
"requests", TimeUnit.SECONDS);
Meter depResRequestsMeter = Metrics.newMeter(ReplicaServiceHandler.class, "dep-res-requests",
"requests", TimeUnit.SECONDS);
private final Timer putTimer = Metrics.newTimer(ReplicaServiceHandler.class, "put-latency");
private final Timer getTimer = Metrics.newTimer(ReplicaServiceHandler.class, "get-latency");
public ReplicaServiceHandler(IPersistenceEngine persistenceEngine,
AntiEntropyServiceRouter antiEntropyRouter,
DependencyResolver dependencyResolver) {
this.persistenceEngine = persistenceEngine;
this.antiEntropyRouter = antiEntropyRouter;
this.dependencyResolver = dependencyResolver;
}
@Override
public boolean put(String key,
ThriftDataItem valueThrift) throws TException {
TimerContext context = putTimer.time();
try {
DataItem value = new DataItem(valueThrift);
if(logger.isTraceEnabled())
logger.trace("received PUT request for key: '"+key+
"' value: '"+value+
"' transactionKeys: "+value.getTransactionKeys());
antiEntropyRouter.sendWriteToSiblings(key, valueThrift);
// TODO: Hmm, if siblings included us, we wouldn't even need to do this...
// persistenceEngine.put_if_newer(key, value);
if (value.getTransactionKeys() == null || value.getTransactionKeys().isEmpty()) {
persistenceEngine.put_if_newer(key, value);
} else {
dependencyResolver.addPendingWrite(key, value);
}
putMeter.mark();
// todo: remove this return value--it's really not necessary
} finally {
context.stop();
}
return true;
}
@Override
public ThriftDataItem get(String key, ThriftVersion requiredVersionThrift) throws TException {
TimerContext context = getTimer.time();
try {
DataItem ret = persistenceEngine.get(key);
Version requiredVersion = Version.fromThrift(requiredVersionThrift);
if(logger.isTraceEnabled())
logger.trace("received GET request for key: '"+key+
"' requiredVersion: "+ requiredVersion+
", found version: " + (ret == null ? null : ret.getVersion()));
if (ret == null || ret.getVersion().compareTo(Version.NULL_VERSION) == 0) {
logger.debug("Could not find key " + key);
nullVersionsMeter.mark();
}
if (requiredVersion != null && requiredVersion.compareTo(Version.NULL_VERSION) != 0 &&
(ret == null || requiredVersion.compareTo(ret.getVersion()) > 0)) {
depResRequestsMeter.mark();
ret = dependencyResolver.retrievePendingItem(key, requiredVersion);
// race?
if(ret == null) {
logger.warn(String.format("Didn't find suitable version (timestamp=%d) for key %s in pending or persistenceEngine, so fetching again", requiredVersion.getTimestamp(), key));
ret = persistenceEngine.get(key);
}
if(ret == null || requiredVersion.compareTo(ret.getVersion()) > 0) {
logger.error(String.format("suitable version was not found! required time: %d clientID: %d only got %s",
requiredVersion.getTimestamp(), requiredVersion.getClientID(),
ret == null ? "null" : Long.toString(ret.getVersion().getTimestamp())));
ret = null;
}
}
if(ret == null) {
return new ThriftDataItem().setVersion(Version.toThrift(Version.NULL_VERSION));
}
getMeter.mark();
return ret.toThrift();
} finally {
context.stop();
}
}
}