package proj.zoie.perf.server; import it.unimi.dsi.fastutil.longs.Long2IntLinkedOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2IntMap; import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import org.apache.log4j.Logger; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.MultiReader; import org.apache.lucene.queryParser.QueryParser; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.Searcher; import org.apache.lucene.search.TopDocs; import org.apache.lucene.util.Version; import proj.zoie.api.IndexReaderFactory; import proj.zoie.api.ZoieException; import proj.zoie.service.api.SearchRequest; import proj.zoie.service.api.SearchResult; import proj.zoie.service.api.ZoieSearchService; public class MonitoredZoieService<R extends IndexReader> implements ZoieSearchService { protected static int NANOS_IN_MILLI = 1000000; private static int SAMPLE_SIZE = 10000; protected int[] _latencies = new int[SAMPLE_SIZE]; protected int[] _numHits = new int[SAMPLE_SIZE]; protected Long2IntMap _qps = new Long2IntLinkedOpenHashMap(); protected final AtomicInteger _numSearches = new AtomicInteger(0); private static final Logger log = Logger.getLogger(MonitoredZoieService.class); private final IndexReaderFactory<R> _idxReaderFactory; private long _sum = 0L; public MonitoredZoieService(IndexReaderFactory<R> idxReaderFactory) { _idxReaderFactory = idxReaderFactory; } public SearchResult search(SearchRequest req) throws ZoieException { long start = System.nanoTime(); String queryString=req.getQuery(); Analyzer analyzer=_idxReaderFactory.getAnalyzer(); QueryParser qparser=new QueryParser(Version.LUCENE_CURRENT,"content",analyzer); SearchResult result=new SearchResult(); List<R> readers=null; Searcher searcher = null; MultiReader multiReader=null; try { Query q=null; if (queryString == null || queryString.length() ==0) { q = new MatchAllDocsQuery(); } else { q = qparser.parse(queryString); } readers=_idxReaderFactory.getIndexReaders(); multiReader=new MultiReader(readers.toArray(new IndexReader[readers.size()])); searcher=new IndexSearcher(multiReader); TopDocs hits = searcher.search(q,10); result.setTotalDocs(multiReader.numDocs()); result.setTotalHits(hits.totalHits); long end = System.nanoTime(); long latency = (end-start)/NANOS_IN_MILLI; result.setTime(latency); int count = _numSearches.get(); int index = count%SAMPLE_SIZE; _sum+=latency; if (count>SAMPLE_SIZE){ _sum-=_latencies[index]; } // countQuery((end / 1000 * NANOS_IN_MILLI)); // now = end - now; // _latencies.add( (int)(now / NANOS_IN_MILLI)); // _numHits.add(hits.totalHits); _latencies[index]=(int)latency; _numHits[index]=hits.totalHits; log.info("search=[query=" + req.getQuery() + "]" + ", searchResult=[numSearchResults=" + result.getTotalHits() + ";numTotalDocs=" + result.getTotalDocs() + "]" + "in " + result.getTime() + "ms"); _numSearches.incrementAndGet(); return result; } catch(Exception e) { log.error(e.getMessage(),e); throw new ZoieException(e.getMessage(),e); } finally { try{ if (searcher!=null){ try { searcher.close(); } catch (IOException e) { log.error(e.getMessage(),e); } } } finally{ _idxReaderFactory.returnIndexReaders(readers); } } } protected void countQuery(long time) { if(!_qps.containsKey(time)) { _qps.put(time, 0); } _qps.put(time, _qps.get(time) + 1); } public int percentileLatency(int pct) { return percentile(_latencies, pct); } public int percentileHits(int pct) { return percentile(_numHits, pct); } public int percentileQps(int pct) { int[] qps = _qps.values().toIntArray(); Arrays.sort(qps); return percentile(qps, pct); } public int numSearches() { return _numSearches.get(); } public long getAverage(){ return _sum/Math.min(_numSearches.get(), SAMPLE_SIZE); } private int percentile(int[] values, int pct) { Arrays.sort(values); return values[(pct/100)*values.length]; } }