Author: natalia Date: Sun Aug 26 18:27:50 2007 New Revision: 569949 URL: http://svn.apache.org/viewvc?rev=569949&view=rev Log: Make search not synchronized
Modified: xml/xindice/trunk/java/src/org/apache/xindice/core/indexer/LuceneIndexer.java Modified: xml/xindice/trunk/java/src/org/apache/xindice/core/indexer/LuceneIndexer.java URL: http://svn.apache.org/viewvc/xml/xindice/trunk/java/src/org/apache/xindice/core/indexer/LuceneIndexer.java?rev=569949&r1=569948&r2=569949&view=diff ============================================================================== --- xml/xindice/trunk/java/src/org/apache/xindice/core/indexer/LuceneIndexer.java (original) +++ xml/xindice/trunk/java/src/org/apache/xindice/core/indexer/LuceneIndexer.java Sun Aug 26 18:27:50 2007 @@ -99,10 +99,18 @@ private File idxFile; private IndexWriter iw; - private IndexReader ir; - private IndexSearcher is; private Analyzer an; + /** + * Most recently opened searcher. The same Searcher instance is going to + * be used for all the searches unless index has changed and new Searcher + * is required to access the changes. + * + * Searcher cannot be closed if it is being used (if there is a query in + * progress or hits are iterated). + */ + private Searcher searcher; + private Configuration config; private Collection collection; @@ -113,6 +121,8 @@ private int docsAdded; private int docsDeleted; + private final Object lock = new Object(); + private String defaultField = ""; private void setFile(File f) { @@ -214,7 +224,7 @@ return config; } - public synchronized boolean exists() { + public boolean exists() { return IndexReader.indexExists(idxFile); } @@ -246,22 +256,24 @@ return false; } - public synchronized boolean open() throws DBException { + public boolean open() throws DBException { openWrite(false); return true; } - public synchronized boolean isOpened() { - return (null != iw) || (null != ir); + public boolean isOpened() { + return null != iw; } public synchronized boolean close() throws DBException { closeWrite(); - closeRead(); + if (searcher != null) { + searcher.close(true); + } return true; } - public synchronized boolean drop() throws DBException { + public boolean drop() throws DBException { try { if (IndexReader.indexExists(idxFile)) { close(); @@ -271,7 +283,7 @@ } } catch (IOException e) { throw new DBException(FaultCodes.IDX_CORRUPTED, - "Failed to delete index " + name + ", collection " + collection.getCanonicalName(), e); + "Failed to delete index " + name + ", collection " + collection.getCanonicalName(), e); } } @@ -287,50 +299,22 @@ return an; } - private void openRead() throws DBException { - if (log.isTraceEnabled()) { - log.trace("Calling openRead()"); - } - - if (null == ir) { - closeWrite(); - try { - ir = IndexReader.open(getFile()); - } catch (IOException e) { - throw new DBException(FaultCodes.IDX_CORRUPTED, - "Failed to open index " + name + ", collection " + collection.getCanonicalName(), e); - } - } - } - - private void openSearch() throws DBException { - if (log.isTraceEnabled()) { - log.trace("Calling openSearch()"); - } - - if (null == is) { - openRead(); - is = new IndexSearcher(ir); - } - } - private void openWrite(boolean create) throws DBException { if (log.isTraceEnabled()) { log.trace("Calling openWrite(" + create + ")"); } - if (null == iw) { - closeRead(); - try { + try { + if (iw == null) { iw = new IndexWriter(getFile(), getAnalyzer(), create); - } catch (IOException e) { - if (create) { - throw new DBException(FaultCodes.IDX_CANNOT_CREATE, - "Failed to cleate index " + name + ", collection " + collection.getCanonicalName(), e); - } else { - throw new DBException(FaultCodes.IDX_CORRUPTED, - "Failed to open index " + name + ", collection " + collection.getCanonicalName(), e); - } + } + } catch (IOException e) { + if (create) { + throw new DBException(FaultCodes.IDX_CANNOT_CREATE, + "Failed to cleate index " + name + ", collection " + collection.getCanonicalName(), e); + } else { + throw new DBException(FaultCodes.IDX_CORRUPTED, + "Failed to open index " + name + ", collection " + collection.getCanonicalName(), e); } } } @@ -341,63 +325,14 @@ } } - private void assertRead() throws DBException { - assertOpen(); - openRead(); - } - - private void assertWrite() throws DBException { - assertOpen(); - openWrite(false); - } - - private void closeRead() throws DBException { - if (null != ir) { - closeSearch(); - try { - ir.close(); - ir = null; - } catch (IOException e) { - throw new DBException(FaultCodes.IDX_CORRUPTED, - "Failed to close reader for index " + name + ", collection " + collection.getCanonicalName(), e); - } - } - } - - private void closeSearch() throws DBException { - if (null != is) { - try { - is.close(); - is = null; - } catch (IOException e) { - throw new DBException(FaultCodes.IDX_CORRUPTED, - "Failed to close searcher for index " + name + ", collection " + collection.getCanonicalName(), e); - } - } - } - private void closeWrite() throws DBException { if (null != iw) { try { - int nDocs = iw.docCount(); - /* Fairly arbitrary rules for triggering index optimisation. Need to - * play with these. - */ - if (docsAdded > nDocs / 10 || docsAdded > 50 || docsDeleted > 10) { - if (log.isDebugEnabled()) { - log.debug("Optimizing text index for " + collection.getCanonicalName() + "..."); - } - - iw.optimize(); - docsAdded = 0; - docsDeleted = 0; - } - iw.close(); iw = null; } catch (IOException e) { throw new DBException(FaultCodes.IDX_CORRUPTED, - "Failed to close writer for index " + name + ", collection " + collection.getCanonicalName(), e); + "Failed to close writer for index " + name + ", collection " + collection.getCanonicalName(), e); } } } @@ -414,15 +349,32 @@ return f.delete(); } - public synchronized void flush() throws DBException { + public void flush() throws DBException { try { assertOpen(); if (iw != null) { iw.flush(); + + int nDocs = iw.docCount(); + /* Fairly arbitrary rules for triggering index optimisation. Need to + * play with these. + */ + synchronized(lock) { + if (docsAdded > nDocs / 10 || docsAdded > 50 || docsDeleted > 10) { + if (log.isDebugEnabled()) { + log.debug("Optimizing text index for " + collection.getCanonicalName() + "..."); + } + + iw.optimize(); + docsAdded = 0; + docsDeleted = 0; + } + } + } } catch (IOException e) { throw new DBException(FaultCodes.IDX_CORRUPTED, - "Could not force unwritten data to disk for index " + name + ", collection " + collection.getCanonicalName(), e); + "Could not force unwritten data to disk for index " + name + ", collection " + collection.getCanonicalName(), e); } } @@ -437,29 +389,33 @@ return new BasicIndexerEventHandler() { Document doc; - public synchronized void onDocumentAdded(Key key) throws DBException { + public void onDocumentAdded(Key key) throws DBException { if (doc != null) { - assertWrite(); + assertOpen(); try { iw.addDocument(doc); - docsAdded++; + synchronized(lock) { + docsAdded++; + } } catch (IOException e) { throw new DBException(FaultCodes.IDX_CORRUPTED, - "Failed to add document to the index " + name + ", collection " + collection.getCanonicalName(), e); + "Failed to add document to the index " + name + ", collection " + collection.getCanonicalName(), e); } } } - public synchronized void onDocumentDeleted(Key key) throws DBException { - assertRead(); + public void onDocumentDeleted(Key key) throws DBException { + assertOpen(); try { - ir.deleteDocuments(new Term(KEYNAME, key.toString())); - docsDeleted++; + iw.deleteDocuments(new Term(KEYNAME, key.toString())); + synchronized(lock) { + docsDeleted++; + } } catch (IOException e) { throw new DBException(FaultCodes.IDX_CORRUPTED, - "Failed to delete document from the index " + name + ", collection " + collection.getCanonicalName(), e); + "Failed to delete document from the index " + name + ", collection " + collection.getCanonicalName(), e); } } @@ -497,11 +453,12 @@ * @return The resulting matches * @throws DBException if IOException prevented indexer from executing the query. */ - public synchronized IndexMatch[] queryMatches(Query query) throws DBException { + public IndexMatch[] queryMatches(Query query) throws DBException { ArrayList matches = new ArrayList(); - openSearch(); + Searcher searcher = getSearcher(); + try { - Hits hits = is.search(query); + Hits hits = searcher.search(query); for (Iterator i = hits.iterator(); i.hasNext(); ) { Hit hit = (Hit) i.next(); Key key = new Key(hit.getDocument().getField(KEYNAME).stringValue()); @@ -509,8 +466,113 @@ } } catch (IOException e) { throw new ProcessingException("Failed to process a query", e); + } finally { + searcher.free(); } return (IndexMatch[]) matches.toArray(EMPTY_MATCHES); } -} + + /** + * getSearcher returns Searcher that uses current version of the index. + * If index has been modified since last time searcher was requested + * this method will create new Searcher instance, otherwise it will + * return Searcher instance it created previously. + * + * @return current Searcher + * @throws DBException + */ + private synchronized Searcher getSearcher() throws DBException { + + if (searcher != null && !searcher.isCurrent()) { + searcher.close(false); + searcher = null; + } + + if (searcher == null) { + searcher = new Searcher(); + } else { + searcher.incRef(); + } + + return searcher; + } + + private class Searcher { + private IndexReader ir; + private IndexSearcher is; + + // number of searches in progress using that searcher + private int ref = 1; + + public Searcher() throws DBException { + try { + ir = IndexReader.open(getFile()); + is = new IndexSearcher(ir); + } catch (IOException e) { + throw new DBException(FaultCodes.IDX_CORRUPTED, + "Failed to open access " + name + ", collection " + collection.getCanonicalName(), e); + } + } + + public boolean isCurrent() throws DBException { + try { + return ir.isCurrent(); + } catch (IOException e) { + throw new DBException(FaultCodes.IDX_CORRUPTED, + "Failed to access index " + name + ", collection " + collection.getCanonicalName(), e); + } + } + + public void incRef() { + ref++; + } + + /** + * This method must be called after executing text query to cleanup + * resources that are not in use anymore. It decrements number of + * searches referencing this searcher and then attempts to close it + * unless it is the most recently opened searcher. If there were no + * searchers opened after this one, the searcher will be kept open + * for future use, even if it is not used at the moment. + * + * @throws DBException if there was IOException + */ + public void free() throws DBException { + synchronized (LuceneIndexer.this) { + ref--; + + if (searcher != this) { + close(false); + } + } + } + + /** + * Closes the searcher if it is not used in any search. + * + * @param force true if searcher has to be closed even if it is used + * @throws DBException if there was IOException + */ + public void close(boolean force) throws DBException { + try { + if (ref == 0 || force) { + is.close(); + ir.close(); + } + } catch (IOException e) { + throw new DBException(FaultCodes.IDX_CORRUPTED, + "Failed to access index " + name + ", collection " + collection.getCanonicalName(), e); + } + } + + public Hits search(Query query) throws DBException { + try { + return is.search(query); + } catch (IOException e) { + throw new DBException(FaultCodes.IDX_CORRUPTED, + "Failed to access index " + name + ", collection " + collection.getCanonicalName(), e); + } + } + } +} \ No newline at end of file