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;