You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by da...@apache.org on 2014/11/28 16:49:13 UTC
svn commit: r1642315 - in /jackrabbit/oak/branches/1.0: ./
oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/
oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/
oak-core/src/test/java/org/apache/jack...
Author: davide
Date: Fri Nov 28 15:49:13 2014
New Revision: 1642315
URL: http://svn.apache.org/r1642315
Log:
OAK-2077: Improve the resilence of the OrderedIndex for dangling links
merged into 1.0
Added:
jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/Oak2077QueriesTest.java
- copied, changed from r1642285, jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/Oak2077QueriesTest.java
Modified:
jackrabbit/oak/branches/1.0/ (props changed)
jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java
jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/BasicOrderedPropertyIndexQueryTest.java
jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java
Propchange: jackrabbit/oak/branches/1.0/
------------------------------------------------------------------------------
Merged /jackrabbit/oak/trunk:r1642285
Modified: jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java?rev=1642315&r1=1642314&r2=1642315&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java Fri Nov 28 15:49:13 2014
@@ -17,10 +17,12 @@
package org.apache.jackrabbit.oak.plugins.index.property.strategy;
+import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterators.singletonIterator;
import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.ENTRY_COUNT_PROPERTY_NAME;
import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_CONTENT_NODE_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.property.OrderedIndex.LANES;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
@@ -104,9 +106,15 @@ public class OrderedContentMirrorStoreSt
private static final Random RND = new Random(System.currentTimeMillis());
/**
+ * maximum number of attempt for potential recursive processes like seek()
+ */
+ private static final int MAX_RETRIES = LANES+1;
+
+ /**
* the direction of the index.
*/
private OrderDirection direction = OrderedIndex.DEFAULT_DIRECTION;
+
public OrderedContentMirrorStoreStrategy() {
super();
@@ -146,7 +154,7 @@ public class OrderedContentMirrorStoreSt
// we use the seek for seeking the right spot. The walkedLanes will have all our
// predecessors
- String entry = seek(index, condition, walked);
+ String entry = seek(index, condition, walked, 0, new FixingDanglingLinkCallback(index));
if (LOG.isDebugEnabled()) {
LOG.debug("fetchKeyNode() - entry: {} ", entry);
printWalkedLanes("fetchKeyNode() - ", walked);
@@ -199,7 +207,9 @@ public class OrderedContentMirrorStoreSt
do {
entry = seek(index,
new PredicateEquals(key),
- walkedLanes
+ walkedLanes,
+ 0,
+ new LoggingDanglinLinkCallback()
);
lane0Next = getPropertyNext(index.getChildNode(walkedLanes[0]));
if (LOG.isDebugEnabled()) {
@@ -649,7 +659,9 @@ public class OrderedContentMirrorStoreSt
private NodeState start;
NodeState current;
private NodeState index;
+ private NodeBuilder builder;
String currentName;
+ private DanglingLinkCallback dlc = new LoggingDanglinLinkCallback();
public FullIterator(NodeState index, NodeState start, boolean includeStart,
NodeState current) {
@@ -657,12 +669,19 @@ public class OrderedContentMirrorStoreSt
this.start = start;
this.current = current;
this.index = index;
+ this.builder = new ReadOnlyBuilder(index);
}
@Override
public boolean hasNext() {
+ String next = getPropertyNext(current);
boolean hasNext = (includeStart && start.equals(current))
- || (!includeStart && !Strings.isNullOrEmpty(getPropertyNext(current)));
+ || (!includeStart && !Strings.isNullOrEmpty(next)
+ && ensureAndCleanNode(
+ builder, next,
+ currentName == null ? "" : currentName,
+ 0,
+ dlc));
return hasNext;
}
@@ -792,7 +811,7 @@ public class OrderedContentMirrorStoreSt
* last argument
*/
String seek(@Nonnull NodeBuilder index, @Nonnull Predicate<String> condition) {
- return seek(index, condition, null);
+ return seek(index, condition, null, 0, new LoggingDanglinLinkCallback());
}
/**
@@ -806,11 +825,14 @@ public class OrderedContentMirrorStoreSt
* lane represented by the corresponding position in the array. <b>You have</b> to
* pass in an array already sized as {@link OrderedIndex#LANES} or an
* {@link IllegalArgumentException} will be raised
+ * @param retries the number of retries
* @return the entry or null if not found
*/
String seek(@Nonnull final NodeBuilder index,
@Nonnull final Predicate<String> condition,
- @Nullable final String[] walkedLanes) {
+ @Nullable final String[] walkedLanes,
+ int retries,
+ @Nullable DanglingLinkCallback callback) {
boolean keepWalked = false;
String searchfor = condition.getSearchFor();
if (LOG.isDebugEnabled()) {
@@ -858,6 +880,9 @@ public class OrderedContentMirrorStoreSt
lane++;
} else {
if (condition.apply(nextkey)) {
+ // this branch is used so far only for range queries.
+ // while figuring out how to correctly reproduce the issue is less risky
+ // to leave this untouched.
found = nextkey;
} else {
currentKey = nextkey;
@@ -884,7 +909,18 @@ public class OrderedContentMirrorStoreSt
lane--;
} else {
if (condition.apply(nextkey)) {
- found = nextkey;
+ if (ensureAndCleanNode(index, nextkey, currentKey, lane, callback)) {
+ found = nextkey;
+ } else {
+ if (retries < MAX_RETRIES) {
+ return seek(index, condition, walkedLanes, ++retries, callback);
+ } else {
+ LOG.debug(
+ "Attempted a lookup and fix for {} times. Leaving it be and returning null",
+ retries);
+ return null;
+ }
+ }
} else {
currentKey = nextkey;
currentNode = null;
@@ -902,6 +938,36 @@ public class OrderedContentMirrorStoreSt
}
/**
+ * ensure that the provided {@code next} actually exists as node. Attempt to clean it up
+ * otherwise.
+ *
+ * @param index the {@code :index} node
+ * @param next the {@code :next} retrieved for the provided lane
+ * @param current the current node from which {@code :next} has been retrieved
+ * @param lane the lane on which we're looking into
+ * @return true if the node exists, false otherwise
+ */
+ private static boolean ensureAndCleanNode(@Nonnull final NodeBuilder index,
+ @Nonnull final String next,
+ @Nonnull final String current,
+ final int lane,
+ @Nullable DanglingLinkCallback callback) {
+ checkNotNull(index);
+ checkNotNull(next);
+ checkNotNull(current);
+ checkArgument(lane < LANES && lane >= 0, "The lane must be between 0 and LANES");
+
+ if (index.getChildNode(next).exists()) {
+ return true;
+ } else {
+ if (callback != null) {
+ callback.perform(current, next, lane);
+ }
+ return false;
+ }
+ }
+
+ /**
* predicate for evaluating 'key' equality across index
*/
static class PredicateEquals implements Predicate<String> {
@@ -1103,7 +1169,7 @@ public class OrderedContentMirrorStoreSt
* @param value
* @param lane
*/
- static void setPropertyNext(@Nonnull final NodeBuilder node,
+ public static void setPropertyNext(@Nonnull final NodeBuilder node,
final String value, final int lane) {
if (node != null && value != null && lane >= 0 && lane < OrderedIndex.LANES) {
PropertyState next = node.getProperty(NEXT);
@@ -1151,14 +1217,14 @@ public class OrderedContentMirrorStoreSt
/**
* short-cut for using NodeBuilder. See {@code getNext(NodeState)}
*/
- static String getPropertyNext(@Nonnull final NodeBuilder node) {
+ public static String getPropertyNext(@Nonnull final NodeBuilder node) {
return getPropertyNext(node, 0);
}
/**
* short-cut for using NodeBuilder. See {@code getNext(NodeState)}
*/
- static String getPropertyNext(@Nonnull final NodeBuilder node, final int lane) {
+ public static String getPropertyNext(@Nonnull final NodeBuilder node, final int lane) {
checkNotNull(node);
String next = "";
@@ -1200,7 +1266,7 @@ public class OrderedContentMirrorStoreSt
* @param rnd the Random generator to be used for probability
* @return the lane to be updated.
*/
- int getLane(@Nonnull final Random rnd) {
+ protected int getLane(@Nonnull final Random rnd) {
final int maxLanes = OrderedIndex.LANES - 1;
int lane = 0;
@@ -1210,4 +1276,64 @@ public class OrderedContentMirrorStoreSt
return lane;
}
+
+ /**
+ * implementors of this interface will deal with the dangling link cases along the list
+ * (OAK-2077)
+ */
+ interface DanglingLinkCallback {
+ /**
+ * perform the required operation on the provided {@code current} node for the {@code next}
+ * value on {@code lane}
+ *
+ * @param current the current node with the dangling link
+ * @param next the value pointing to the missing node
+ * @param lane the lane on which the link is on
+ */
+ void perform(String current, String next, int lane);
+ }
+
+ /**
+ * implements a "Read-only" version for managing the dangling links which will simply track down
+ * in logs the presence of it
+ */
+ static class LoggingDanglinLinkCallback implements DanglingLinkCallback {
+ private boolean alreadyLogged;
+
+ @Override
+ public void perform(@Nonnull final String current,
+ @Nonnull final String next,
+ int lane) {
+ checkNotNull(next);
+ checkNotNull(current);
+ checkArgument(lane < LANES && lane >= 0, "The lane must be between 0 and LANES");
+
+ if (!alreadyLogged) {
+ LOG.warn(
+ "Dangling link to '{}' found on lane '{}' for key '{}'. Trying to clean it up. You may consider a reindex",
+ new Object[] { next, lane, current });
+ alreadyLogged = true;
+ }
+ }
+ }
+
+ static class FixingDanglingLinkCallback extends LoggingDanglinLinkCallback {
+ private final NodeBuilder indexContent;
+
+ public FixingDanglingLinkCallback(@Nonnull final NodeBuilder indexContent) {
+ this.indexContent = checkNotNull(indexContent);
+ }
+
+ @Override
+ public void perform(String current, String next, int lane) {
+ super.perform(current, next, lane);
+ // as we're already pointing to nowhere it's safe to truncate here and avoid
+ // future errors. We'll fix all the lanes from slowest to fastest starting from the lane
+ // with the error. This should keep the list a bit more consistent with what is
+ // expected.
+ for (int l = lane; l < LANES; l++) {
+ setPropertyNext(indexContent.getChildNode(current), "", lane);
+ }
+ }
+ }
}
\ No newline at end of file
Modified: jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/BasicOrderedPropertyIndexQueryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/BasicOrderedPropertyIndexQueryTest.java?rev=1642315&r1=1642314&r2=1642315&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/BasicOrderedPropertyIndexQueryTest.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/BasicOrderedPropertyIndexQueryTest.java Fri Nov 28 15:49:13 2014
@@ -20,6 +20,8 @@ import static junit.framework.Assert.ass
import static junit.framework.Assert.assertTrue;
import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
import java.text.DecimalFormat;
import java.text.NumberFormat;
@@ -66,35 +68,61 @@ public abstract class BasicOrderedProper
protected static final String ISO_8601_2000 = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
/**
+ * same as {@link #generateOrderedValues(int, int, OrderDirection)} by passing {@code 0} as
+ * {@code offset}
+ *
+ * @param amount
+ * @param direction
+ * @return
+ */
+ protected static List<String> generateOrderedValues(int amount, OrderDirection direction) {
+ return generateOrderedValues(amount, 0, direction);
+ }
+
+ /**
+ * <p>
* generate a list of values to be used as ordered set. Will return something like
* {@code value000, value001, value002, ...}
+ * </p>
*
- *
- * @param amount
+ * @param amount the values to be generated
+ * @param offset move the current counter by this provided amount.
* @param direction the direction of the sorting
* @return a list of {@code amount} values ordered as specified by {@code direction}
*/
- protected static List<String> generateOrderedValues(int amount, OrderDirection direction) {
+ protected static List<String> generateOrderedValues(int amount, int offset , OrderDirection direction) {
if (amount > 1000) {
throw new RuntimeException("amount cannot be greater than 1000");
}
List<String> values = new ArrayList<String>(amount);
- NumberFormat nf = new DecimalFormat("000");
+
if (OrderDirection.DESC.equals(direction)) {
for (int i = amount; i > 0; i--) {
- values.add(String.format("value%s", String.valueOf(nf.format(i))));
+ values.add(formatNumber(i + offset));
}
} else {
for (int i = 0; i < amount; i++) {
- values.add(String.format("value%s", String.valueOf(nf.format(i))));
+ values.add(formatNumber(i + offset));
}
}
return values;
}
+
+ /**
+ * formats the provided number for being used by the
+ * {@link #generateOrderedValues(int, OrderDirection)}
+ *
+ * @param number
+ * @return something in the format {@code value000}
+ */
+ public static String formatNumber(int number) {
+ NumberFormat nf = new DecimalFormat("0000");
+ return String.format("value%s", String.valueOf(nf.format(number)));
+ }
/**
- * as {@code generateOrderedValues(int, OrderDirection)} by forcing OrderDirection.ASC
+ * as {@link #generateOrderedValues(int, OrderDirection)} by forcing {@link OrderDirection.ASC}
*
* @param amount
* @return
@@ -121,9 +149,13 @@ public abstract class BasicOrderedProper
}
/**
+ * <p>
* convenience method that adds a bunch of nodes in random order and return the order in which
- * they should be presented by the OrderedIndex
- *
+ * they should be presented by the OrderedIndex.
+ * </p>
+ * <p>
+ * The nodes will be created using the {@link #ORDERED_PROPERTY} as property for indexing
+ * </p>
* @param values the values of the property that will be indexed
* @param father the father under which add the nodes
* @param direction the direction of the items to be added.
@@ -154,22 +186,27 @@ public abstract class BasicOrderedProper
/**
* assert the right order of the returned resultset
*
- * @param orderedSequence the right order in which the resultset should be returned
+ * @param expected the right order in which the resultset should be returned
* @param resultset the resultset
*/
- protected void assertRightOrder(@Nonnull final List<ValuePathTuple> orderedSequence,
+ protected void assertRightOrder(@Nonnull final List<ValuePathTuple> expected,
@Nonnull final Iterator<? extends ResultRow> resultset) {
- assertTrue("No results returned", resultset.hasNext());
- int counter = 0;
- while (resultset.hasNext() && counter < orderedSequence.size()) {
- ResultRow row = resultset.next();
- assertEquals(
- String.format("Wrong path at the element '%d'", counter),
- orderedSequence.get(counter).getPath(),
- row.getPath()
- );
- counter++;
- }
+ if (expected.isEmpty()) {
+ assertFalse("An empty resultset is expected but something has been returned.",
+ resultset.hasNext());
+ } else {
+ assertTrue("No results returned", resultset.hasNext());
+ int counter = 0;
+ while (resultset.hasNext() && counter < expected.size()) {
+ ResultRow row = resultset.next();
+ assertEquals(
+ String.format("Wrong path at the element '%d'", counter),
+ expected.get(counter).getPath(),
+ row.getPath()
+ );
+ counter++;
+ }
+ }
}
/**
Copied: jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/Oak2077QueriesTest.java (from r1642285, jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/Oak2077QueriesTest.java)
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/Oak2077QueriesTest.java?p2=jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/Oak2077QueriesTest.java&p1=jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/Oak2077QueriesTest.java&r1=1642285&r2=1642315&rev=1642315&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/Oak2077QueriesTest.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/Oak2077QueriesTest.java Fri Nov 28 15:49:13 2014
@@ -409,7 +409,7 @@ public class Oak2077QueriesTest extends
@Test
public void queryNotNullAscending() throws Exception {
- setTraversalEnabled(false);
+ setTravesalEnabled(false);
final int numberOfNodes = 20;
final OrderDirection direction = ASC;
final String inexistent = formatNumber(numberOfNodes + 1);
@@ -428,12 +428,12 @@ public class Oak2077QueriesTest extends
// pointing to a non-existent node in lane 0 we expect the result to be truncated
assertLogAndQuery(statement, expected);
- setTraversalEnabled(true);
+ setTravesalEnabled(true);
}
@Test
public void queryNotNullDescending() throws Exception {
- setTraversalEnabled(false);
+ setTravesalEnabled(false);
final int numberOfNodes = 20;
final OrderDirection direction = DESC; //changed
final String inexistent = formatNumber(0); //changed
@@ -455,7 +455,7 @@ public class Oak2077QueriesTest extends
// as the full iterable used in `property IS NOT NULL` cases walk the index on lane 0, any
// other lanes doesn't matter.
- setTraversalEnabled(true);
+ setTravesalEnabled(true);
}
// As of OAK-2202 we don't use the skip list for returning a specific key item, so we're not
@@ -467,7 +467,7 @@ public class Oak2077QueriesTest extends
@Test
public void queryGreaterThanAscending() throws Exception {
- setTraversalEnabled(false);
+ setTravesalEnabled(false);
final int numberOfNodes = 20;
final OrderDirection direction = ASC;
final String inexistent = formatNumber(numberOfNodes + 1);
@@ -489,7 +489,7 @@ public class Oak2077QueriesTest extends
// pointing to a non-existent node in lane 0 we expect the result to be truncated
assertLogAndQuery(String.format(statement, whereCondition), expected);
- setTraversalEnabled(true);
+ setTravesalEnabled(true);
}
/*
@@ -498,7 +498,7 @@ public class Oak2077QueriesTest extends
*/
@Test
public void queryGreaterThanAscendingLane1() throws Exception {
- setTraversalEnabled(false);
+ setTravesalEnabled(false);
final int numberOfNodes = 20;
final OrderDirection direction = ASC;
String inexistent = formatNumber(numberOfNodes + 1);
@@ -521,12 +521,12 @@ public class Oak2077QueriesTest extends
Result result = executeQuery(st, SQL2, null);
assertRightOrder(expected, result.getRows().iterator());
- setTraversalEnabled(true);
+ setTravesalEnabled(true);
}
@Test
public void queryGreaterThenDescending() throws Exception {
- setTraversalEnabled(false);
+ setTravesalEnabled(false);
final int numberOfNodes = 20;
final int offset = 5;
final OrderDirection direction = DESC;
@@ -548,12 +548,12 @@ public class Oak2077QueriesTest extends
// pointing to a non-existent node in lane 0 we expect the result to be truncated
assertLogAndQuery(String.format(statement, whereCondition), expected);
- setTraversalEnabled(true);
+ setTravesalEnabled(true);
}
@Test
public void queryGreaterThanEqualAscending() throws Exception {
- setTraversalEnabled(false);
+ setTravesalEnabled(false);
final int numberOfNodes = 20;
final OrderDirection direction = ASC;
final String inexistent = formatNumber(numberOfNodes + 1);
@@ -574,12 +574,12 @@ public class Oak2077QueriesTest extends
// pointing to a non-existent node in lane 0 we expect the result to be truncated
assertLogAndQuery(String.format(statement, whereCondition), expected);
- setTraversalEnabled(true);
+ setTravesalEnabled(true);
}
@Test
public void queryGreaterThanEqualDescending() throws Exception {
- setTraversalEnabled(false);
+ setTravesalEnabled(false);
final int numberOfNodes = 20;
final int offset = 5;
final OrderDirection direction = DESC;
@@ -601,12 +601,12 @@ public class Oak2077QueriesTest extends
// pointing to a non-existent node in lane 0 we expect the result to be truncated
assertLogAndQuery(String.format(statement, whereCondition), expected);
- setTraversalEnabled(true);
+ setTravesalEnabled(true);
}
@Test
public void queryLessThanAscending() throws Exception {
- setTraversalEnabled(false);
+ setTravesalEnabled(false);
final int numberOfNodes = 20;
final OrderDirection direction = ASC;
final String inexistent = formatNumber(numberOfNodes + 1);
@@ -626,12 +626,12 @@ public class Oak2077QueriesTest extends
// pointing to a non-existent node in lane 0 we expect the result to be truncated
assertLogAndQuery(String.format(statement, whereCondition), expected);
- setTraversalEnabled(true);
+ setTravesalEnabled(true);
}
@Test
public void queryLessThanDescending() throws Exception {
- setTraversalEnabled(false);
+ setTravesalEnabled(false);
final int numberOfNodes = 20;
final int offset = 5;
final OrderDirection direction = DESC;
@@ -652,6 +652,6 @@ public class Oak2077QueriesTest extends
// pointing to a non-existent node in lane 0 we expect the result to be truncated
assertLogAndQuery(String.format(statement, whereCondition), expected);
- setTraversalEnabled(true);
+ setTravesalEnabled(true);
}
}
Modified: jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java?rev=1642315&r1=1642314&r2=1642315&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java Fri Nov 28 15:49:13 2014
@@ -17,9 +17,17 @@
package org.apache.jackrabbit.oak.plugins.index.property.strategy;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Sets.newHashSet;
+import static org.apache.jackrabbit.oak.plugins.index.property.OrderedIndex.LANES;
+import static org.apache.jackrabbit.oak.plugins.index.property.OrderedIndex.OrderDirection.DESC;
import static org.apache.jackrabbit.oak.plugins.index.property.strategy.OrderedContentMirrorStoreStrategy.NEXT;
import static org.apache.jackrabbit.oak.plugins.index.property.strategy.OrderedContentMirrorStoreStrategy.START;
+import static org.apache.jackrabbit.oak.plugins.index.property.strategy.OrderedContentMirrorStoreStrategy.getPropertyNext;
+import static org.apache.jackrabbit.oak.plugins.index.property.strategy.OrderedContentMirrorStoreStrategy.setPropertyNext;
+import static org.apache.jackrabbit.oak.plugins.index.property.strategy.OrderedContentMirrorStoreStrategy.FixingDanglingLinkCallback;
+import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
import static org.easymock.EasyMock.createNiceMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
@@ -47,6 +55,7 @@ import org.apache.jackrabbit.oak.plugins
import org.apache.jackrabbit.oak.plugins.index.IndexUtils;
import org.apache.jackrabbit.oak.plugins.index.property.OrderedIndex;
import org.apache.jackrabbit.oak.plugins.index.property.OrderedIndex.OrderDirection;
+import org.apache.jackrabbit.oak.plugins.index.property.strategy.OrderedContentMirrorStoreStrategy.PredicateGreaterThan;
import org.apache.jackrabbit.oak.plugins.index.property.strategy.OrderedContentMirrorStoreStrategy.PredicateLessThan;
import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
import org.apache.jackrabbit.oak.query.ast.Operator;
@@ -56,6 +65,7 @@ import org.apache.jackrabbit.oak.spi.que
import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.ReadOnlyBuilder;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -3123,7 +3133,7 @@ public class OrderedContentMirrorStorage
try {
item = store.seek(builder,
- new OrderedContentMirrorStoreStrategy.PredicateEquals(searchFor), wl);
+ new OrderedContentMirrorStoreStrategy.PredicateEquals(searchFor), wl, 0, null);
fail("With a wrong size for the lane it should have raised an exception");
} catch (IllegalArgumentException e) {
// so far so good. It was expected
@@ -3138,7 +3148,7 @@ public class OrderedContentMirrorStorage
entry = searchFor;
wl = new String[OrderedIndex.LANES];
item = store.seek(builder,
- new OrderedContentMirrorStoreStrategy.PredicateEquals(searchFor), wl);
+ new OrderedContentMirrorStoreStrategy.PredicateEquals(searchFor), wl, 0, null);
assertNotNull(wl);
assertEquals(OrderedIndex.LANES, wl.length);
assertEquals("Wrong lane", lane0, wl[0]);
@@ -3155,7 +3165,7 @@ public class OrderedContentMirrorStorage
entry = searchFor;
wl = new String[OrderedIndex.LANES];
item = store.seek(builder,
- new OrderedContentMirrorStoreStrategy.PredicateEquals(searchFor), wl);
+ new OrderedContentMirrorStoreStrategy.PredicateEquals(searchFor), wl, 0, null);
assertNotNull(wl);
assertEquals(OrderedIndex.LANES, wl.length);
assertEquals("Wrong lane", lane0, wl[0]);
@@ -3172,7 +3182,7 @@ public class OrderedContentMirrorStorage
entry = searchFor;
wl = new String[OrderedIndex.LANES];
item = store.seek(builder,
- new OrderedContentMirrorStoreStrategy.PredicateEquals(searchFor), wl);
+ new OrderedContentMirrorStoreStrategy.PredicateEquals(searchFor), wl, 0, null);
assertNotNull(wl);
assertEquals(OrderedIndex.LANES, wl.length);
assertEquals("Wrong lane", lane0, wl[0]);
@@ -3245,7 +3255,7 @@ public class OrderedContentMirrorStorage
try {
item = store.seek(builder,
- new OrderedContentMirrorStoreStrategy.PredicateEquals(searchFor), wl);
+ new OrderedContentMirrorStoreStrategy.PredicateEquals(searchFor), wl, 0, null);
fail("With a wrong size for the lane it should have raised an exception");
} catch (IllegalArgumentException e) {
// so far so good. It was expected
@@ -3260,7 +3270,7 @@ public class OrderedContentMirrorStorage
entry = searchFor;
wl = new String[OrderedIndex.LANES];
item = store.seek(builder,
- new OrderedContentMirrorStoreStrategy.PredicateEquals(searchFor), wl);
+ new OrderedContentMirrorStoreStrategy.PredicateEquals(searchFor), wl, 0, null);
assertNotNull(wl);
assertEquals(OrderedIndex.LANES, wl.length);
assertEquals("Wrong lane", lane0, wl[0]);
@@ -3277,7 +3287,7 @@ public class OrderedContentMirrorStorage
entry = searchFor;
wl = new String[OrderedIndex.LANES];
item = store.seek(builder,
- new OrderedContentMirrorStoreStrategy.PredicateEquals(searchFor), wl);
+ new OrderedContentMirrorStoreStrategy.PredicateEquals(searchFor), wl, 0, null);
assertNotNull(wl);
assertEquals(OrderedIndex.LANES, wl.length);
assertEquals("Wrong lane", lane0, wl[0]);
@@ -3294,7 +3304,7 @@ public class OrderedContentMirrorStorage
entry = searchFor;
wl = new String[OrderedIndex.LANES];
item = store.seek(builder,
- new OrderedContentMirrorStoreStrategy.PredicateEquals(searchFor), wl);
+ new OrderedContentMirrorStoreStrategy.PredicateEquals(searchFor), wl, 0, null);
assertNotNull(wl);
assertEquals(OrderedIndex.LANES, wl.length);
assertEquals("Wrong lane", lane0, wl[0]);
@@ -3309,7 +3319,7 @@ public class OrderedContentMirrorStorage
*
* @param index
*/
- private static void printSkipList(NodeState index) {
+ public static void printSkipList(NodeState index) {
final String marker = "->o-";
final String filler = "----";
StringBuffer sb = new StringBuffer();
@@ -3659,4 +3669,100 @@ public class OrderedContentMirrorStorage
assertEquals("path/f", resultset.next());
assertFalse("We should have not any results left", resultset.hasNext());
}
+
+ @Test
+ public void oak2077() {
+ NodeBuilder index;
+ MockOrderedContentMirrorStoreStrategy ascending = new MockOrderedContentMirrorStoreStrategy();
+ MockOrderedContentMirrorStoreStrategy descending = new MockOrderedContentMirrorStoreStrategy(DESC);
+ MockOrderedContentMirrorStoreStrategy strategy;
+ OrderedIndex.Predicate<String> condition;
+ String missingEntry, node;
+
+
+ // creating a dangling link on each lane one at time.
+ for (int lane = 0; lane < LANES; lane++) {
+
+ // ---------------------------------------------------< ascending, plain/inserts case >
+ missingEntry = KEYS[5];
+ strategy = ascending;
+ condition = new PredicateGreaterThan(missingEntry, true);
+ index = EMPTY_NODE.builder();
+ node = oak2077CreateStructure(index, lane, strategy, missingEntry);
+
+ assertOak2077(condition, strategy, index, lane, node);
+
+ // ------------------------------------------------- < descending, plain/inserts case >
+ missingEntry = KEYS[0];
+ strategy = descending;
+ index = EMPTY_NODE.builder();
+ condition = new PredicateLessThan(missingEntry, true);
+ node = oak2077CreateStructure(index, lane, strategy, missingEntry);
+
+ assertOak2077(condition, strategy, index, lane, node);
+ }
+ }
+
+ private static void assertOak2077(@Nonnull final OrderedIndex.Predicate<String> condition,
+ @Nonnull final OrderedContentMirrorStoreStrategy strategy,
+ @Nonnull NodeBuilder index,
+ final int lane,
+ @Nonnull final String node) {
+
+ checkNotNull(condition);
+ checkNotNull(strategy);
+ checkNotNull(index);
+ checkArgument(lane >= 0 && lane < LANES);
+ checkNotNull(node);
+
+ NodeState indexState = index.getNodeState();
+ String[] wl = new String[LANES];
+ String entry;
+
+ entry = strategy.seek(index, condition, wl, 0, new FixingDanglingLinkCallback(index));
+ assertNull("the seeked node does not exist and should have been null. lane: " + lane, entry);
+ assertEquals(
+ "As the index is a NodeBuilder we expect the entry to be fixed. lane: " + lane, "",
+ getPropertyNext(index.getChildNode(node), lane));
+
+ index = new ReadOnlyBuilder(indexState);
+ entry = strategy.seek(index, condition);
+ assertNull("the seeked node does not exist and should have been null. lane: " + lane, entry);
+ }
+
+ /**
+ * <p>
+ * utility method to create the structure for the {@link #oak2077()} test.
+ * </p>
+ * <p>
+ * Create an index according to strategy with nodes from {@code 001} to {@code 004}.
+ * </p>
+ *
+ * @param lane
+ * @param strategy
+ * @param missingEntry
+ * @return the node name with the wrong lane for testing on it later on.
+ */
+ private static String oak2077CreateStructure(@Nonnull final NodeBuilder index,
+ final int lane,
+ @Nonnull final MockOrderedContentMirrorStoreStrategy strategy,
+ @Nonnull final String missingEntry) {
+ checkNotNull(index);
+ checkNotNull(strategy);
+ checkArgument(lane >= 0 && lane < LANES);
+ checkNotNull(missingEntry);
+
+ String node;
+
+ strategy.setLane(0);
+ strategy.update(index, "/we/dont/care", EMPTY_KEY_SET, newHashSet(KEYS[1]));
+ strategy.update(index, "/we/dont/care", EMPTY_KEY_SET, newHashSet(KEYS[2]));
+ strategy.setLane(lane);
+ strategy.update(index, "/we/dont/care", EMPTY_KEY_SET, newHashSet(KEYS[3]));
+ strategy.update(index, "/we/dont/care", EMPTY_KEY_SET, newHashSet(KEYS[4]));
+ node = KEYS[3];
+ setPropertyNext(index.getChildNode(node), missingEntry, lane);
+
+ return node;
+ }
}