/* * Copyright 2012 Future Systems * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.krakenapps.logdb.query.command; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import org.krakenapps.logdb.LogQueryCommand; import org.krakenapps.logdb.query.ObjectComparator; import org.krakenapps.logdb.sort.CloseableIterator; import org.krakenapps.logdb.sort.Item; import org.krakenapps.logdb.sort.ParallelMergeSorter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Stats2 extends LogQueryCommand { private final Logger logger = LoggerFactory.getLogger(Stats2.class); private int inputCount; private List<String> clauses; private Function[] values; private ParallelMergeSorter sorter; private Map<List<Object>, Function[]> buffer; public Stats2(List<String> clause, Function[] values) { this.clauses = clause; this.values = values; this.sorter = new ParallelMergeSorter(new ItemComparer()); this.buffer = new HashMap<List<Object>, Function[]>(); } @Override public void init() { super.init(); for (Function f : values) f.clean(); } @Override public void push(LogMap m) { List<Object> keys = new ArrayList<Object>(clauses.size()); for (String clause : clauses) { Object keyValue = m.get(clause); if (keyValue == null) return; keys.add(keyValue); } try { inputCount++; Function[] fs = buffer.get(keys); if (fs == null) { fs = new Function[values.length]; for (int i = 0; i < fs.length; i++) fs[i] = values[i].clone(); buffer.put(keys, fs); } for (Function f : fs) f.put(m); // flush if (buffer.keySet().size() > 50000) flush(); } catch (IOException e) { throw new IllegalStateException("sort failed, query " + logQuery, e); } } private void flush() throws IOException { logger.debug("kraken logdb: flushing stats2 buffer, [{}] keys", buffer.keySet().size()); for (List<Object> keys : buffer.keySet()) { Function[] fs = buffer.get(keys); Object[] l = new Object[fs.length]; int i = 0; for (Function f : fs) l[i++] = f.serialize(); sorter.add(new Item(keys.toArray(), l)); } buffer.clear(); } @Override public boolean isReducer() { return true; } @Override public void eof() { this.status = Status.Finalizing; logger.debug("kraken logdb: stats2 sort input count [{}]", inputCount); CloseableIterator it = null; try { // last flush flush(); // reclaim buffer (GC support) buffer = null; // sort it = sorter.sort(); Object[] lastKeys = null; Function[] fs = null; Item item = null; int count = 0; while (it.hasNext()) { item = (Item) it.next(); count++; // first record or need to change merge set? if (lastKeys == null || !Arrays.equals(lastKeys, (Object[]) item.getKey())) { if (logger.isDebugEnabled() && lastKeys != null) logger.debug("kraken logdb: stats2 key compare [{}] != [{}]", lastKeys[0], ((Object[]) item.getKey())[0]); // finalize last record (only if changing set) if (fs != null) { pass(fs, lastKeys); } // load new record fs = new Function[values.length]; int i = 0; Object[] rawFuncs = (Object[]) item.getValue(); for (Object rawFunc : rawFuncs) { Object[] l = (Object[]) rawFunc; String name = (String) l[0]; String target = (String) l[1]; String keyName = (String) l[2]; Function f = Function.getFunction(name, target, keyName, Timechart.func); f.load(l); fs[i++] = f; } } else { // merge int i = 0; for (Function f : fs) { Object[] l = (Object[]) ((Object[]) item.getValue())[i]; String name = (String) l[0]; String target = (String) l[1]; String keyName = (String) l[2]; Function f2 = Function.getFunction(name, target, keyName, Timechart.func); f2.load(l); f.merge(f2); i++; } } lastKeys = (Object[]) item.getKey(); } // write last merge set if (item != null) pass(fs, lastKeys); logger.debug("kraken logdb: sorted stats2 input [{}]", count); } catch (IOException e) { throw new IllegalStateException("sort failed, query " + logQuery, e); } finally { if (it != null) { try { // close and delete final sorted run file it.close(); } catch (IOException e) { } } // support sorter cache GC when query processing is ended sorter = null; super.eof(); } } private void pass(Function[] fs, Object[] keys) { Map<String, Object> m = new HashMap<String, Object>(); for (int i = 0; i < clauses.size(); i++) m.put(clauses.get(i), keys[i]); for (int i = 0; i < values.length; i++) m.put(values[i].toString(), fs[i].getResult()); write(new LogMap(m)); } private static class ItemComparer implements Comparator<Item> { private ObjectComparator cmp = new ObjectComparator(); @Override public int compare(Item o1, Item o2) { return cmp.compare(o1.getKey(), o2.getKey()); } } }