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 mr...@apache.org on 2014/06/16 08:58:35 UTC
svn commit: r1602810 - in /jackrabbit/oak/branches/1.0: ./
oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/
oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/
Author: mreutegg
Date: Mon Jun 16 06:58:35 2014
New Revision: 1602810
URL: http://svn.apache.org/r1602810
Log:
OAK-1788: ConcurrentConflictTest fails occasionally
Merge r1602179 from trunk
Modified:
jackrabbit/oak/branches/1.0/ (props changed)
jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Revision.java
jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ConcurrentConflictTest.java
jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/RevisionTest.java
Propchange: jackrabbit/oak/branches/1.0/
------------------------------------------------------------------------------
Merged /jackrabbit/oak/trunk:r1602179
Modified: jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Revision.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Revision.java?rev=1602810&r1=1602809&r2=1602810&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Revision.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Revision.java Mon Jun 16 06:58:35 2014
@@ -549,6 +549,9 @@ public class Revision {
* <ul>
* <li>
* {@code null} if the revision is older than the earliest range
+ * and the revision timestamp is less than or equal the time
+ * of the last {@link #purge(long)} (see also
+ * {@link #oldestTimestamp}).
* </li>
* <li>
* if the revision is newer than the lower bound of the newest
@@ -565,9 +568,49 @@ public class Revision {
* </li>
* </ul>
*
+ * Below is a graph for a revision comparison example as seen from one
+ * cluster node with some known revision ranges. Revision ranges less
+ * than or equal r2-0-0 have been purged and there are known ranges for
+ * cluster node 1 (this cluster node) and cluster node 2 (some other
+ * cluster node).
+ * <pre>
+ * View from cluster node 1:
+ *
+ * purge r3-0-1 r5-0-2 r7-0-1
+ * Ë
Ë
Ë
Ë
+ * ---+---------+---------+---------+---------+---------
+ * r1-0-0 r2-0-0 r3-0-0 r4-0-0 r5-0-0
+ *
+ * ^
+ * r1-0-1 -> null (1)
+ *
+ * ^
+ * r4-0-2 -> r4-0-0 (2)
+ *
+ * ^
+ * r3-0-1 -> r3-0-0 (3)
+ *
+ * ^
+ * r6-0-2 -> FUTURE (4)
+ *
+ * ^
+ * r9-0-1 -> NEWEST (5)
+ * </pre>
+ * <ol>
+ * <li>older than earliest range and purge time</li>
+ * <li>seen-at of next higher range</li>
+ * <li>seen-at of matching lower bound of range</li>
+ * <li>foreign revision is newer than most recent range</li>
+ * <li>local revision is newer than most recent range</li>
+ * </ol>
+ * This gives the following revision ordering:
+ * <pre>
+ * r1-0-1 < r3-0-1 < r-4-0-2 < r9-0-1 < r6-0-2
+ * </pre>
+ *
* @param r the revision
* @return the seen-at revision or {@code null} if the revision is older
- * than the earliest range.
+ * than the earliest range and purge time.
*/
Revision getRevisionSeen(Revision r) {
List<RevisionRange> list = map.get(r.getClusterId());
@@ -586,8 +629,9 @@ public class Revision {
// search from latest backward
// (binary search could be used, but we expect most queries
// at the end of the list)
+ RevisionRange range = null;
for (int i = list.size() - 1; i >= 0; i--) {
- RevisionRange range = list.get(i);
+ range = list.get(i);
int compare = r.compareRevisionTime(range.revision);
if (compare == 0) {
return range.seenAt;
@@ -597,15 +641,21 @@ public class Revision {
if (r.getClusterId() == currentClusterNodeId) {
// newer than all others, except for FUTURE
return NEWEST;
+ } else {
+ // happens in the future (not visible yet)
+ return FUTURE;
}
- // happens in the future (not visible yet)
- return FUTURE;
} else {
// there is a newer range
return list.get(i + 1).seenAt;
}
}
}
+ if (range != null && r.getTimestamp() > oldestTimestamp) {
+ // revision is older than earliest range and after purge
+ // timestamp. return seen-at revision of earliest range.
+ return range.seenAt;
+ }
return null;
}
Modified: jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ConcurrentConflictTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ConcurrentConflictTest.java?rev=1602810&r1=1602809&r2=1602810&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ConcurrentConflictTest.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ConcurrentConflictTest.java Mon Jun 16 06:58:35 2014
@@ -30,6 +30,7 @@ import org.apache.jackrabbit.mk.api.Micr
import org.apache.jackrabbit.oak.plugins.document.memory.MemoryDocumentStore;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
+import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
@@ -68,6 +69,16 @@ public class ConcurrentConflictTest exte
}
}
+ @After
+ @Override
+ public void disposeDocumentMK() {
+ super.disposeDocumentMK();
+ for (DocumentMK mk : kernels) {
+ mk.dispose();
+ }
+ kernels.clear();
+ }
+
private DocumentMK openDocumentMK() {
return new DocumentMK.Builder().setAsyncDelay(10).setDocumentStore(store).open();
}
@@ -78,12 +89,23 @@ public class ConcurrentConflictTest exte
concurrentUpdates(true);
}
- @Ignore("OAK-1788")
@Test
public void concurrentUpdates() throws Exception {
concurrentUpdates(false);
}
+ @Ignore("Enable to run concurrentUpdates() in a loop")
+ @Test
+ public void concurrentUpdates_Loop() throws Exception {
+ for (int i = 0; i < 1000; i++) {
+ System.out.println("test " + i);
+ concurrentUpdates(false);
+ // prepare for next round
+ disposeDocumentMK();
+ initDocumentMK();
+ }
+ }
+
private void concurrentUpdates(final boolean useBranch) throws Exception {
LOG.info("====== Start test =======");
final AtomicInteger conflicts = new AtomicInteger();
@@ -97,9 +119,9 @@ public class ConcurrentConflictTest exte
@Override
public void run() {
BitSet conflictSet = new BitSet();
- int numTransfers = NUM_TRANSFERS_PER_THREAD;
+ int numTransfers = 0;
try {
- while (numTransfers > 0) {
+ while (numTransfers < NUM_TRANSFERS_PER_THREAD && exceptions.isEmpty()) {
try {
if (!transfer()) {
continue;
@@ -110,7 +132,7 @@ public class ConcurrentConflictTest exte
conflicts.incrementAndGet();
conflictSet.set(numTransfers);
}
- numTransfers--;
+ numTransfers++;
}
} catch (Exception e) {
exceptions.add(e);
@@ -161,6 +183,10 @@ public class ConcurrentConflictTest exte
rev = mk.merge(rev, null);
}
log("Successful transfer @" + oldRev + ": " + jsop.toString() + " (new rev: " + rev + ")");
+ long s = calculateSum(mk, rev);
+ if (s != NUM_NODES * 100) {
+ throw new Exception("Sum mismatch: " + s);
+ }
return true;
}
}));
@@ -177,13 +203,7 @@ public class ConcurrentConflictTest exte
}
DocumentMK mk = openDocumentMK();
String rev = mk.getHeadRevision();
- long sum = 0;
- for (int i = 0; i < NUM_NODES; i++) {
- String json = mk.getNodes("/node-" + i, rev, 0, 0, 1000, null);
- JSONParser parser = new JSONParser();
- JSONObject obj = (JSONObject) parser.parse(json);
- sum += (Long) obj.get("value");
- }
+ long sum = calculateSum(mk, rev);
log("Conflict rate: " + conflicts.get() +
"/" + (NUM_WRITERS * NUM_TRANSFERS_PER_THREAD));
System.out.print(logBuffer);
@@ -191,6 +211,18 @@ public class ConcurrentConflictTest exte
if (!exceptions.isEmpty()) {
throw exceptions.get(0);
}
+ mk.dispose();
+ }
+
+ static long calculateSum(MicroKernel mk, String rev) throws Exception {
+ long sum = 0;
+ for (int i = 0; i < NUM_NODES; i++) {
+ String json = mk.getNodes("/node-" + i, rev, 0, 0, 1000, null);
+ JSONParser parser = new JSONParser();
+ JSONObject obj = (JSONObject) parser.parse(json);
+ sum += (Long) obj.get("value");
+ }
+ return sum;
}
void log(String msg) {
Modified: jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/RevisionTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/RevisionTest.java?rev=1602810&r1=1602809&r2=1602810&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/RevisionTest.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/RevisionTest.java Mon Jun 16 06:58:35 2014
@@ -164,7 +164,8 @@ public class RevisionTest {
"1:\n r120-0-1:r20-0-0\n" +
"2:\n r200-0-2:r10-0-0\n", comp.toString());
- assertEquals(-1, comp.compare(r0c1, r0c2));
+ // r0c2 happens before r0c1 because r2c2 is declared before r2c1
+ assertEquals(1, comp.compare(r0c1, r0c2));
assertEquals(1, comp.compare(r1c1, r1c2));
assertEquals(1, comp.compare(r2c1, r2c2));
@@ -221,17 +222,18 @@ public class RevisionTest {
Revision r2c2 = new Revision(0x40, 0, 2);
comp.add(r1c1, new Revision(0x10, 0, 0));
- comp.add(r2c1, new Revision(0x20, 0, 0));
+ comp.add(r2c1, new Revision(0x30, 0, 0));
// there's no range for c2, and therefore this
// revision must be considered to be in the future
assertTrue(comp.compare(r1c2, r2c1) > 0);
// add a range for r2r2
- comp.add(r2c2, new Revision(0x30, 0, 0));
+ comp.add(r2c2, new Revision(0x40, 0, 0));
+ comp.purge(0x20);
- // now there is a range for c2, but the revision is old,
- // so it must be considered to be in the past
+ // now there is a range for c2, but the revision is old (before purge
+ // time, so it must be considered to be in the past
assertTrue(comp.compare(r1c2, r2c1) < 0);
}
@@ -251,7 +253,7 @@ public class RevisionTest {
Revision c2sync = Revision.fromString("r4-1-2");
comp.add(c2sync, Revision.fromString("r2-1-0"));
Revision c3sync = Revision.fromString("r2-0-3");
- comp.add(c3sync, Revision.fromString("r2-1-0"));
+ comp.add(c3sync, Revision.fromString("r2-1-0"));
assertTrue(comp.compare(r1, r2) < 0);
assertTrue(comp.compare(r2, c2sync) < 0);
@@ -267,6 +269,7 @@ public class RevisionTest {
@Test
public void revisionSeen() {
RevisionComparator comp = new RevisionComparator(1);
+ comp.purge(0);
Revision r0 = new Revision(0x01, 0, 1);
Revision r1 = new Revision(0x10, 0, 1);
@@ -281,8 +284,9 @@ public class RevisionTest {
comp.add(r3, new Revision(0x30, 0, 0));
comp.add(r4, new Revision(0x40, 0, 0));
- // older than first range -> must return null
- assertNull(comp.getRevisionSeen(r0));
+ // older than first range, but after purge timestamp
+ // -> must return seen-at of first range
+ assertEquals(new Revision(0x10, 0, 0), comp.getRevisionSeen(r0));
// exact range start matches
assertEquals(new Revision(0x10, 0, 0), comp.getRevisionSeen(r1));
@@ -323,6 +327,29 @@ public class RevisionTest {
assertNull(comp.getRevisionSeen(r2));
}
+ // OAK-1822
+ @Test
+ public void seenAtBeforeFirstRangeAfterPurge() {
+ RevisionComparator comp = new RevisionComparator(1);
+ comp.purge(0);
+
+ Revision r1 = new Revision(1, 0, 1);
+ Revision r2 = new Revision(2, 0, 1);
+ Revision r3 = new Revision(3, 0, 1);
+
+ Revision r3seen = new Revision(3, 0, 0);
+
+ comp.add(r3, r3seen);
+
+ assertEquals(r3seen, comp.getRevisionSeen(r1));
+ assertEquals(r3seen, comp.getRevisionSeen(r2));
+
+ comp.purge(1);
+
+ assertEquals(null, comp.getRevisionSeen(r1));
+ assertEquals(r3seen, comp.getRevisionSeen(r2));
+ }
+
@Test
public void uniqueRevision2() throws Exception {
List<Thread> threads = new ArrayList<Thread>();