@Override public <KOut, VOut> Map<KOut, VOut> reduce(ReduceCommand<KOut, VOut> reduceCommand) throws InterruptedException { Cache<?, ?> cache = cacheManager.getCache(reduceCommand.getCacheName()); Set<KOut> keys = reduceCommand.getKeys(); String taskId = reduceCommand.getTaskId(); Reducer<KOut, VOut> reducer = reduceCommand.getReducer(); boolean useIntermediateKeys = reduceCommand.isEmitCompositeIntermediateKeys(); boolean noInputKeys = keys == null || keys.isEmpty(); Cache<Object, List<VOut>> tmpCache = cacheManager.getCache(reduceCommand.getCacheName()); Map<KOut, VOut> result = new HashMap<KOut, VOut>(); if (noInputKeys) { // illegal state, raise exception throw new IllegalStateException( "Reduce phase of MapReduceTask " + taskId + " on node " + cdl.getAddress() + " executed with empty input keys"); } else { // first hook into lifecycle MapReduceTaskLifecycleService taskLifecycleService = MapReduceTaskLifecycleService.getInstance(); log.tracef("For m/r task %s invoking %s at %s", taskId, reduceCommand, cdl.getAddress()); int interruptCount = 0; long start = log.isTraceEnabled() ? timeService.time() : 0; try { taskLifecycleService.onPreExecute(reducer, cache); for (KOut key : keys) { interruptCount++; if (checkInterrupt(interruptCount++) && Thread.currentThread().isInterrupted()) throw new InterruptedException(); // load result value from map phase List<VOut> value; if (useIntermediateKeys) { value = tmpCache.get(new IntermediateCompositeKey<KOut>(taskId, key)); } else { value = tmpCache.get(key); } // and reduce it VOut reduced = reducer.reduce(key, value.iterator()); result.put(key, reduced); log.tracef( "For m/r task %s reduced %s to %s at %s ", taskId, key, reduced, cdl.getAddress()); } } finally { if (log.isTraceEnabled()) { log.tracef( "Reduce for task %s took %s milliseconds", reduceCommand.getTaskId(), timeService.timeDuration(start, TimeUnit.MILLISECONDS)); } taskLifecycleService.onPostExecute(reducer); } } return result; }