package voldemort.rest.server; import static org.jboss.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.log4j.Logger; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.handler.codec.http.HttpResponseStatus; import voldemort.common.VoldemortOpCode; import voldemort.rest.DeleteResponseSender; import voldemort.rest.GetAllResponseSender; import voldemort.rest.GetResponseSender; import voldemort.rest.GetVersionResponseSender; import voldemort.rest.PutResponseSender; import voldemort.rest.RestDeleteErrorHandler; import voldemort.rest.RestErrorHandler; import voldemort.rest.RestGetErrorHandler; import voldemort.rest.RestGetVersionErrorHandler; import voldemort.rest.RestPutErrorHandler; import voldemort.store.CompositeVoldemortRequest; import voldemort.store.Store; import voldemort.store.stats.StoreStats; import voldemort.store.stats.StoreStatsJmx; import voldemort.utils.ByteArray; import voldemort.utils.JmxUtils; import voldemort.versioning.Version; import voldemort.versioning.Versioned; public class StorageWorkerThread implements Runnable { private final static RestGetErrorHandler getErrorHandler = new RestGetErrorHandler(); private final static RestDeleteErrorHandler deleteErrorHandler = new RestDeleteErrorHandler(); private final static RestGetVersionErrorHandler getVersionErrorHandler = new RestGetVersionErrorHandler(); private final static RestPutErrorHandler putErrorHandler = new RestPutErrorHandler(); private ConcurrentHashMap<String, StoreStats> storeStatsMap; private StoreStats performanceStats = null, aggregatedStoreStats = null; CompositeVoldemortRequest<ByteArray, byte[]> requestObject; private MessageEvent messageEvent; private Store<ByteArray, byte[], byte[]> store; private final int localZoneId; private boolean isJmxEnabled = false; private final Logger logger = Logger.getLogger(StorageWorkerThread.class); public StorageWorkerThread(MessageEvent messageEvent, ConcurrentHashMap<String, StoreStats> storeStatsMap, StoreStats aggregatedStoreStats, boolean isJmxEnabled, int localZoneId) { this.messageEvent = messageEvent; this.storeStatsMap = storeStatsMap; this.aggregatedStoreStats = aggregatedStoreStats; this.isJmxEnabled = isJmxEnabled; this.localZoneId = localZoneId; } @Override public void run() { Object message = messageEvent.getMessage(); boolean fromLocalZone = false; if(message instanceof VoldemortStoreRequest) { VoldemortStoreRequest storeRequestObject = (VoldemortStoreRequest) message; this.requestObject = storeRequestObject.getRequestObject(); long now = System.currentTimeMillis(); if(requestObject.getRequestOriginTimeInMs() + requestObject.getRoutingTimeoutInMs() <= now) { RestErrorHandler.writeErrorResponse(messageEvent, HttpResponseStatus.REQUEST_TIMEOUT, "current time: " + now + "\torigin time: " + requestObject.getRequestOriginTimeInMs() + "\ttimeout in ms: " + requestObject.getRoutingTimeoutInMs()); return; } else { fromLocalZone = isLocalZoneId(storeRequestObject.getZoneId()); this.store = storeRequestObject.getStore(); String storeName = store.getName(); performanceStats = storeStatsMap.get(storeName); if(performanceStats == null) { // Add to concurrentHashMap storeStatsMap.putIfAbsent(storeName, new StoreStats(storeName, aggregatedStoreStats)); performanceStats = storeStatsMap.get(storeName); // Register MBeans for new store performance stats if(isJmxEnabled) { JmxUtils.registerMbean(new StoreStatsJmx(performanceStats), JmxUtils.createObjectName(JmxUtils.getPackageName(this.getClass()) + ".store.stats", storeName)); } } switch(requestObject.getOperationType()) { case VoldemortOpCode.GET_OP_CODE: if(logger.isDebugEnabled()) { logger.debug("Incoming get request"); } try { List<Versioned<byte[]>> versionedValues = store.get(requestObject.getKey(), null); // handle non existing key if(versionedValues.size() > 0) { GetResponseSender responseConstructor = new GetResponseSender(messageEvent, requestObject.getKey(), versionedValues, storeName); responseConstructor.sendResponse(performanceStats, fromLocalZone, requestObject.getRequestOriginTimeInMs()); } else { logger.error("Error when doing get. Key does not exist"); RestErrorHandler.writeErrorResponse(messageEvent, NOT_FOUND, "Key does not exist"); } } catch(Exception e) { getErrorHandler.handleExceptions(messageEvent, e); } break; case VoldemortOpCode.GET_ALL_OP_CODE: if(logger.isDebugEnabled()) { logger.debug("Incoming get all request"); } try { Map<ByteArray, List<Versioned<byte[]>>> keyValuesMap = store.getAll(requestObject.getIterableKeys(), null); // check if there is at least one valid key // before sending response boolean hasAtleastOneValidKey = false; for(List<Versioned<byte[]>> values: keyValuesMap.values()) { if(values.size() > 0) { hasAtleastOneValidKey = true; break; } } if(hasAtleastOneValidKey) { GetAllResponseSender responseConstructor = new GetAllResponseSender(messageEvent, keyValuesMap, storeName); responseConstructor.sendResponse(performanceStats, fromLocalZone, requestObject.getRequestOriginTimeInMs()); } else { logger.error("Error when doing getall. Key does not exist or key is null"); RestErrorHandler.writeErrorResponse(messageEvent, NOT_FOUND, "Key does not exist or key is null"); } } catch(Exception e) { getErrorHandler.handleExceptions(messageEvent, e); } break; case VoldemortOpCode.PUT_OP_CODE: if(logger.isDebugEnabled()) { logger.debug("Incoming put request"); } try { store.put(requestObject.getKey(), requestObject.getValue(), null); PutResponseSender responseConstructor = new PutResponseSender(messageEvent); responseConstructor.sendResponse(performanceStats, fromLocalZone, requestObject.getRequestOriginTimeInMs()); } catch(Exception e) { putErrorHandler.handleExceptions(messageEvent, e); } break; case VoldemortOpCode.DELETE_OP_CODE: if(logger.isDebugEnabled()) { logger.debug("Incoming delete request"); } try { boolean result = store.delete(requestObject.getKey(), requestObject.getVersion()); if(!result) { logger.error("Error when doing delete. Non Existing key/version. Nothing to delete"); RestErrorHandler.writeErrorResponse(messageEvent, NOT_FOUND, "Non Existing key/version. Nothing to delete"); break; } DeleteResponseSender responseConstructor = new DeleteResponseSender(messageEvent); responseConstructor.sendResponse(performanceStats, fromLocalZone, requestObject.getRequestOriginTimeInMs()); } catch(Exception e) { deleteErrorHandler.handleExceptions(messageEvent, e); } break; case VoldemortOpCode.GET_VERSION_OP_CODE: if(logger.isDebugEnabled()) { logger.debug("Incoming get version request"); } try { List<Version> versions = store.getVersions(requestObject.getKey()); // handle non existing key if(versions.size() > 0) { GetVersionResponseSender responseConstructor = new GetVersionResponseSender(messageEvent, requestObject.getKey(), versions, storeName); responseConstructor.sendResponse(performanceStats, fromLocalZone, requestObject.getRequestOriginTimeInMs()); } else { logger.error("Error when doing getversion. Key does not exist or key is null"); RestErrorHandler.writeErrorResponse(messageEvent, NOT_FOUND, "Key does not exist or key is null"); } } catch(Exception e) { getVersionErrorHandler.handleExceptions(messageEvent, e); } break; default: // Since we don't add any other operations than the 5 // above, the code stops here. return; } } } } private boolean isLocalZoneId(int requestZoneId) { if(requestZoneId == this.localZoneId) { return true; } else { return false; } } }