Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/CachingIndexReader.java
===================================================================
--- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/CachingIndexReader.java (revision 920537)
+++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/CachingIndexReader.java (working copy)
@@ -31,11 +31,13 @@
import org.slf4j.LoggerFactory;
import java.io.IOException;
+import java.util.Arrays;
import java.util.BitSet;
import java.util.Map;
import java.util.HashMap;
import java.util.Collections;
import java.text.NumberFormat;
+import java.util.concurrent.ConcurrentHashMap;
/**
* Implements an IndexReader that maintains caches to resolve
@@ -61,15 +63,21 @@
private final BitSet shareableNodes;
/**
- * Cache of nodes parent relation. If an entry in the array is not null,
- * that means the node with the document number = array-index has the node
- * with DocId as parent.
+ * Cache of nodes parent relation. If an entry in the array is >= 0,
+ * then that means the node with the document number = array-index has the
+ * node with the value at that position as parent.
*/
- private final DocId[] parents;
+ private final int[] inSegmentParents;
/**
- * Initializes the {@link #parents} cache.
+ * Cache of nodes parent relation that point to a foreign index segment.
*/
+ private final Map foreignParentDocIds = new ConcurrentHashMap();
+
+ /**
+ * Initializes the {@link #inSegmentParents} and {@link #foreignParentDocIds}
+ * caches.
+ */
private CacheInitializer cacheInitializer;
/**
@@ -99,7 +107,7 @@
* @param delegatee the base IndexReader.
* @param cache a document number cache, or null if not
* available to this reader.
- * @param initCache if the {@link #parents} cache should be initialized
+ * @param initCache if the parent caches should be initialized
* when this index reader is constructed.
* @throws IOException if an error occurs while reading from the index.
*/
@@ -110,7 +118,8 @@
throws IOException {
super(delegatee);
this.cache = cache;
- this.parents = new DocId[delegatee.maxDoc()];
+ this.inSegmentParents = new int[delegatee.maxDoc()];
+ Arrays.fill(this.inSegmentParents, -1);
this.shareableNodes = new BitSet();
TermDocs tDocs = delegatee.termDocs(
new Term(FieldNames.SHAREABLE_NODE, ""));
@@ -144,7 +153,12 @@
DocId getParent(int n, BitSet deleted) throws IOException {
DocId parent;
boolean existing = false;
- parent = parents[n];
+ int parentDocNum = inSegmentParents[n];
+ if (parentDocNum != -1) {
+ parent = DocId.create(parentDocNum);
+ } else {
+ parent = foreignParentDocIds.get(n);
+ }
if (parent != null) {
existing = true;
@@ -159,6 +173,7 @@
}
if (parent == null) {
+ int plainDocId = -1;
Document doc = document(n, FieldSelectors.UUID_AND_PARENT);
String[] parentUUIDs = doc.getValues(FieldNames.PARENT);
if (parentUUIDs.length == 0 || parentUUIDs[0].length() == 0) {
@@ -174,7 +189,8 @@
try {
while (docs.next()) {
if (!deleted.get(docs.doc())) {
- parent = DocId.create(docs.doc());
+ plainDocId = docs.doc();
+ parent = DocId.create(plainDocId);
break;
}
}
@@ -191,7 +207,20 @@
}
// finally put to cache
- parents[n] = parent;
+ if (plainDocId != -1) {
+ // PlainDocId
+ inSegmentParents[n] = plainDocId;
+ } else {
+ // UUIDDocId
+ foreignParentDocIds.put(n, parent);
+ if (existing) {
+ // there was an existing parent reference in
+ // inSegmentParents, which was invalid and is replaced
+ // with a UUIDDocId (points to a foreign segment).
+ // mark as unknown
+ inSegmentParents[n] = -1;
+ }
+ }
}
return parent;
}
@@ -313,7 +342,8 @@
}
/**
- * Initializes the {@link CachingIndexReader#parents} cache.
+ * Initializes the {@link CachingIndexReader#inSegmentParents} and
+ * {@link CachingIndexReader#foreignParentDocIds} caches.
*/
private class CacheInitializer implements Runnable {
@@ -382,8 +412,8 @@
}
/**
- * Initializes the {@link CachingIndexReader#parents} DocId
- * array.
+ * Initializes the {@link CachingIndexReader#inSegmentParents} and
+ * {@link CachingIndexReader#foreignParentDocIds} caches.
*
* @param reader the underlying index reader.
* @throws IOException if an error occurs while reading from the index.
@@ -432,28 +462,28 @@
for (NodeInfo info : docs.values()) {
NodeInfo parent = docs.get(info.parent);
if (parent != null) {
- parents[info.docId] = DocId.create(parent.docId);
+ inSegmentParents[info.docId] = parent.docId;
} else if (info.parent != null) {
foreignParents++;
- parents[info.docId] = DocId.create(info.parent);
+ foreignParentDocIds.put(info.docId, DocId.create(info.parent));
} else if (shareableNodes.get(info.docId)) {
Document doc = reader.document(info.docId, FieldSelectors.UUID_AND_PARENT);
- parents[info.docId] = DocId.create(doc.getValues(FieldNames.PARENT));
+ foreignParentDocIds.put(info.docId, DocId.create(doc.getValues(FieldNames.PARENT)));
} else {
// no parent -> root node
- parents[info.docId] = DocId.NULL;
+ foreignParentDocIds.put(info.docId, DocId.NULL);
}
}
if (log.isDebugEnabled()) {
NumberFormat nf = NumberFormat.getPercentInstance();
nf.setMaximumFractionDigits(1);
time = System.currentTimeMillis() - time;
- if (parents.length > 0) {
- foreignParents /= parents.length;
+ if (inSegmentParents.length > 0) {
+ foreignParents /= inSegmentParents.length;
}
log.debug("initialized {} DocIds in {} ms, {} foreign parents",
new Object[]{
- parents.length,
+ inSegmentParents.length,
time,
nf.format(foreignParents)
});
Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DocId.java
===================================================================
--- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DocId.java (revision 920537)
+++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DocId.java (working copy)
@@ -32,6 +32,17 @@
static final int[] EMPTY = new int[0];
/**
+ * All DocIds with a value smaller than {@link Short#MAX_VALUE}.
+ */
+ private static final PlainDocId[] LOW_DOC_IDS = new PlainDocId[Short.MAX_VALUE];
+
+ static {
+ for (int i = 0; i < LOW_DOC_IDS.length; i++) {
+ LOW_DOC_IDS[i] = new PlainDocId(i);
+ }
+ }
+
+ /**
* Indicates a null DocId. Will be returned if the root node is asked for
* its parent.
*/
@@ -112,7 +123,12 @@
* @return a DocId based on a document number.
*/
static DocId create(int docNumber) {
- return new PlainDocId(docNumber);
+ if (docNumber < Short.MAX_VALUE) {
+ // use cached values for docNumbers up to 32k
+ return LOW_DOC_IDS[docNumber];
+ } else {
+ return new PlainDocId(docNumber);
+ }
}
/**
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/HashCache.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/HashCache.java (revision 920537)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/HashCache.java (working copy)
@@ -28,20 +28,28 @@
public class HashCache {
/**
- * Size of the cache (must be a power of two). Note that this is the
- * maximum number of objects kept in the cache, but due to hashing it
- * can well be that only a part of the cache array is filled even if
- * many more distinct objects are being accessed.
+ * Array of cached objects, indexed by their hash codes
+ * (module size of the array).
*/
- private static final int SIZE_POWER_OF_2 = 1024;
+ private final Object[] array;
/**
- * Array of cached objects, indexed by their hash codes
- * (module size of the array).
+ * Creates a hash cache with 1024 slots.
*/
- private final Object[] array = new Object[SIZE_POWER_OF_2];
+ public HashCache() {
+ this(10);
+ }
/**
+ * Creates a hash cache with 2^exponent slots.
+ *
+ * @param exponent the exponent.
+ */
+ public HashCache(int exponent) {
+ this.array = new Object[2 << exponent];
+ }
+
+ /**
* If a cached copy of the given object already exists, then returns
* that copy. Otherwise the given object is cached and returned.
*
@@ -49,7 +57,7 @@
* @return the given object or a previously cached copy
*/
public Object get(Object object) {
- int position = object.hashCode() & (SIZE_POWER_OF_2 - 1);
+ int position = object.hashCode() & (array.length - 1);
Object previous = array[position];
if (object.equals(previous)) {
return previous;