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