package voldemort.server.protocol.admin;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import voldemort.VoldemortException;
import voldemort.client.protocol.pb.ProtoUtils;
import voldemort.client.protocol.pb.VAdminProto;
import voldemort.client.protocol.pb.VAdminProto.UpdateSlopEntriesRequest;
import voldemort.server.StoreRepository;
import voldemort.server.VoldemortConfig;
import voldemort.server.protocol.StreamRequestHandler;
import voldemort.store.ErrorCodeMapper;
import voldemort.store.StorageEngine;
import voldemort.store.metadata.MetadataStore;
import voldemort.store.stats.StreamingStats;
import voldemort.store.stats.StreamingStats.Operation;
import voldemort.utils.ByteArray;
import voldemort.utils.ByteUtils;
import voldemort.utils.Utils;
import voldemort.versioning.ObsoleteVersionException;
import voldemort.versioning.VectorClock;
import voldemort.versioning.Versioned;
public class UpdateSlopEntriesRequestHandler implements StreamRequestHandler {
private VAdminProto.UpdateSlopEntriesRequest request;
private final VAdminProto.UpdateSlopEntriesResponse.Builder responseBuilder = VAdminProto.UpdateSlopEntriesResponse.newBuilder();
private final ErrorCodeMapper errorCodeMapper;
private final StoreRepository storeRepository;
private final MetadataStore metadataStore;
private final long startTime;
private long networkTimeNs;
private boolean isJmxEnabled;
private long counter = 0L;
private final Logger logger = Logger.getLogger(getClass());
public UpdateSlopEntriesRequestHandler(UpdateSlopEntriesRequest request,
ErrorCodeMapper errorCodeMapper,
StoreRepository storeRepository,
VoldemortConfig voldemortConfig,
MetadataStore metadataStore) {
super();
this.request = request;
this.errorCodeMapper = errorCodeMapper;
this.storeRepository = storeRepository;
startTime = System.currentTimeMillis();
networkTimeNs = 0;
this.isJmxEnabled = voldemortConfig.isJmxEnabled();
this.metadataStore = metadataStore;
}
public StreamRequestDirection getDirection() {
return StreamRequestDirection.READING;
}
public void close(DataOutputStream outputStream) throws IOException {
ProtoUtils.writeMessage(outputStream, responseBuilder.build());
}
public void handleError(DataOutputStream outputStream, VoldemortException e) throws IOException {
responseBuilder.setError(ProtoUtils.encodeError(errorCodeMapper, e));
if(logger.isEnabledFor(Level.ERROR))
logger.error("handleUpdateSlopEntries failed for request(" + request + ")", e);
}
public StreamRequestHandlerState handleRequest(DataInputStream inputStream,
DataOutputStream outputStream)
throws IOException {
if(!metadataStore.getSlopStreamingEnabledUnlocked()) {
throw new VoldemortException("Slop streaming is disabled on node "
+ metadataStore.getNodeId() + " under "
+ metadataStore.getServerStateUnlocked() + " state.");
}
long startNs = System.nanoTime();
if(request == null) {
int size = 0;
try {
size = inputStream.readInt();
} catch(EOFException e) {
if(logger.isTraceEnabled())
logger.trace("Incomplete read for message size");
networkTimeNs += System.nanoTime() - startNs;
return StreamRequestHandlerState.INCOMPLETE_READ;
}
if(size == -1) {
if(logger.isTraceEnabled())
logger.trace("Message size -1, completed slop update");
return StreamRequestHandlerState.COMPLETE;
}
if(logger.isTraceEnabled())
logger.trace("UpdateSlopEntriesRequest message size: " + size);
byte[] input = new byte[size];
try {
ByteUtils.read(inputStream, input);
networkTimeNs += Utils.elapsedTimeNs(startNs, System.nanoTime());
} catch(EOFException e) {
if(logger.isTraceEnabled())
logger.trace("Incomplete read for message");
return StreamRequestHandlerState.INCOMPLETE_READ;
}
VAdminProto.UpdateSlopEntriesRequest.Builder builder = VAdminProto.UpdateSlopEntriesRequest.newBuilder();
builder.mergeFrom(input);
request = builder.build();
}
StorageEngine<ByteArray, byte[], byte[]> storageEngine = AdminServiceRequestHandler.getStorageEngine(storeRepository,
request.getStore());
StreamingStats streamStats = null;
if(isJmxEnabled) {
streamStats = storeRepository.getStreamingStats(storageEngine.getName());
streamStats.reportNetworkTime(Operation.SLOP_UPDATE, networkTimeNs);
}
networkTimeNs = 0;
ByteArray key = ProtoUtils.decodeBytes(request.getKey());
VectorClock vectorClock = ProtoUtils.decodeClock(request.getVersion());
switch(request.getRequestType()) {
case PUT:
try {
// Retrieve the transform if its exists
byte[] transforms = null;
if(request.hasTransform()) {
transforms = ProtoUtils.decodeBytes(request.getTransform()).get();
}
// Retrieve the value
byte[] value = ProtoUtils.decodeBytes(request.getValue()).get();
startNs = System.nanoTime();
storageEngine.put(key, Versioned.value(value, vectorClock), transforms);
if(isJmxEnabled)
streamStats.reportStorageTime(Operation.SLOP_UPDATE,
Utils.elapsedTimeNs(startNs,
System.nanoTime()));
if(logger.isTraceEnabled())
logger.trace("updateSlopEntries (Streaming put) successful on key:" + key
+ " of store: " + request.getStore());
} catch(ObsoleteVersionException e) {
// log and ignore
if(logger.isDebugEnabled())
logger.debug("updateSlopEntries (Streaming put) threw ObsoleteVersionException, Ignoring.");
}
break;
case DELETE:
try {
startNs = System.nanoTime();
storageEngine.delete(key, vectorClock);
if(isJmxEnabled)
streamStats.reportStorageTime(Operation.SLOP_UPDATE, System.nanoTime()
- startNs);
if(logger.isTraceEnabled())
logger.trace("updateSlopEntries (Streaming delete) successful");
} catch(ObsoleteVersionException e) {
// log and ignore
if(logger.isDebugEnabled())
logger.debug("updateSlopEntries (Streaming delete) threw ObsoleteVersionException, Ignoring.");
}
break;
default:
throw new VoldemortException("Unsupported operation ");
}
// log progress
counter++;
if(isJmxEnabled)
streamStats.reportStreamingPut(Operation.SLOP_UPDATE);
if(0 == counter % 100000) {
long totalTime = (System.currentTimeMillis() - startTime) / 1000;
if(logger.isDebugEnabled())
logger.debug("updateSlopEntries() updated " + counter + " entries in " + totalTime
+ " s");
}
request = null;
return StreamRequestHandlerState.READING;
}
}