Index: oak-mk/src/test/java/org/apache/jackrabbit/mk/store/DefaultRevisionStoreTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP <+>/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.apache.jackrabbit.mk.store;\n\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.TimeUnit;\n\nimport org.apache.jackrabbit.mk.blobs.MemoryBlobStore;\nimport org.apache.jackrabbit.mk.core.MicroKernelImpl;\nimport org.apache.jackrabbit.mk.core.Repository;\nimport org.apache.jackrabbit.mk.model.Id;\nimport org.apache.jackrabbit.mk.model.StoredCommit;\nimport org.apache.jackrabbit.mk.persistence.GCPersistence;\nimport org.apache.jackrabbit.mk.persistence.InMemPersistence;\nimport org.apache.jackrabbit.mk.store.DefaultRevisionStore.PutTokenImpl;\nimport org.apache.jackrabbit.mk.store.RevisionStore.PutToken;\nimport org.json.simple.JSONArray;\nimport org.json.simple.parser.JSONParser;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Ignore;\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * Tests verifying the inner workings of DefaultRevisionStore.\n */\npublic class DefaultRevisionStoreTest {\n\n /* avoid synthetic accessor */ DefaultRevisionStore rs;\n private MicroKernelImpl mk;\n \n @Before\n public void setup() throws Exception {\n rs = new DefaultRevisionStore(createPersistence()) {\n @Override\n protected Id markCommits() throws Exception {\n // Keep head commit only\n StoredCommit commit = getHeadCommit();\n markCommit(commit);\n return commit.getId();\n }\n };\n rs.initialize();\n\n mk = new MicroKernelImpl(new Repository(rs, new MemoryBlobStore()));\n }\n \n protected GCPersistence createPersistence() throws Exception {\n return new InMemPersistence();\n }\n\n @After\n public void tearDown() throws Exception {\n if (mk != null) {\n mk.dispose();\n }\n }\n \n /**\n * Verify revision history works with garbage collection.\n * \n * @throws Exception if an error occurs\n */\n @Test\n public void testRevisionHistory() {\n mk.commit(\"/\", \"+\\\"a\\\" : { \\\"c\\\":{}, \\\"d\\\":{} }\", mk.getHeadRevision(), null);\n mk.commit(\"/\", \"+\\\"b\\\" : {}\", mk.getHeadRevision(), null);\n mk.commit(\"/b\", \"+\\\"e\\\" : {}\", mk.getHeadRevision(), null);\n mk.commit(\"/a/c\", \"+\\\"f\\\" : {}\", mk.getHeadRevision(), null);\n \n String headRevision = mk.getHeadRevision();\n String contents = mk.getNodes(\"/\", headRevision, 1, 0, -1, null);\n\n rs.gc();\n \n assertEquals(headRevision, mk.getHeadRevision());\n assertEquals(contents, mk.getNodes(\"/\", headRevision, 1, 0, -1, null));\n \n String history = mk.getRevisionHistory(Long.MIN_VALUE, Integer.MIN_VALUE, null);\n assertEquals(1, parseJSONArray(history).size());\n }\n\n /**\n * Verify branch and merge works with garbage collection.\n * \n * @throws Exception if an error occurs\n */\n @Test\n public void testBranchMerge() throws Exception {\n mk.commit(\"/\", \"+\\\"a\\\" : { \\\"b\\\":{}, \\\"c\\\":{} }\", mk.getHeadRevision(), null);\n String branchRevisionId = mk.branch(mk.getHeadRevision());\n\n mk.commit(\"/a\", \"+\\\"d\\\" : {}\", mk.getHeadRevision(), null);\n branchRevisionId = mk.commit(\"/a\", \"+\\\"e\\\" : {}\", branchRevisionId, null);\n \n rs.gc();\n\n branchRevisionId = mk.commit(\"/a\", \"+\\\"f\\\" : {}\", branchRevisionId, null);\n mk.merge(branchRevisionId, null);\n\n rs.gc();\n\n String history = mk.getRevisionHistory(Long.MIN_VALUE, Integer.MIN_VALUE, null);\n assertEquals(1, parseJSONArray(history).size());\n }\n \n /**\n * Verify garbage collection can run concurrently with commits.\n * \n * @throws Exception if an error occurs\n */\n @Test\n public void testConcurrentGC() throws Exception {\n ScheduledExecutorService gcExecutor = Executors.newScheduledThreadPool(1);\n gcExecutor.scheduleWithFixedDelay(new Runnable() {\n @Override\n public void run() {\n rs.gc();\n }\n }, 100, 20, TimeUnit.MILLISECONDS);\n\n mk.commit(\"/\", \"+\\\"a\\\" : { \\\"b\\\" : { \\\"c\\\" : { \\\"d\\\" : {} } } }\",\n mk.getHeadRevision(), null);\n\n try {\n for (int i = 0; i < 20; i++) {\n mk.commit(\"/a/b/c/d\", \"+\\\"e\\\" : {}\", mk.getHeadRevision(), null);\n Thread.sleep(10);\n mk.commit(\"/a/b/c/d/e\", \"+\\\"f\\\" : {}\", mk.getHeadRevision(), null);\n Thread.sleep(30);\n mk.commit(\"/a/b/c/d\", \"-\\\"e\\\"\", mk.getHeadRevision(), null);\n }\n } finally {\n gcExecutor.shutdown();\n }\n }\n\n /**\n * Verify garbage collection can run concurrently with branch & merge.\n * \n * @throws Exception if an error occurs\n */\n @Test\n @Ignore\n public void testConcurrentMergeGC() throws Exception {\n ScheduledExecutorService gcExecutor = Executors.newScheduledThreadPool(1);\n gcExecutor.scheduleWithFixedDelay(new Runnable() {\n @Override\n public void run() {\n rs.gc();\n }\n }, 100, 20, TimeUnit.MILLISECONDS);\n\n mk.commit(\"/\", \"+\\\"a\\\" : { \\\"b\\\" : { \\\"c\\\" : { \\\"d\\\" : {} } } }\",\n mk.getHeadRevision(), null);\n\n try {\n for (int i = 0; i < 20; i++) {\n String branchId = mk.branch(mk.getHeadRevision());\n if ((i & 1) == 0) {\n /* add some data in even runs */\n branchId = mk.commit(\"/a/b/c/d\", \"+\\\"e\\\" : {}\", branchId, null);\n Thread.sleep(10);\n branchId = mk.commit(\"/a/b/c/d/e\", \"+\\\"f\\\" : {}\", branchId, null);\n } else {\n /* remove added data in odd runs */\n branchId = mk.commit(\"/a/b/c/d\", \"-\\\"e\\\"\", branchId, null);\n }\n Thread.sleep(30);\n mk.merge(branchId, null);\n }\n } finally {\n gcExecutor.shutdown();\n }\n }\n\n @Test\n public void putTokenImpl() throws InterruptedException, ExecutionException {\n final Set tokens = Collections.synchronizedSet(new HashSet());\n Set> results = new HashSet>();\n\n ExecutorService executorService = Executors.newFixedThreadPool(100);\n for (int i = 0; i < 100; i++) {\n results.add(executorService.submit(new Callable() {\n @Override\n public Void call() throws Exception {\n for (int j = 0; j < 10000; j++) {\n assertTrue(tokens.add(new PutTokenImpl()));\n }\n return null;\n }\n }));\n }\n\n for (Future result : results) {\n result.get();\n }\n }\n\n /**\n * Parses the provided string into a {@code JSONArray}.\n *\n * @param json string to be parsed\n * @return a {@code JSONArray}\n * @throws {@code AssertionError} if the string cannot be parsed into a {@code JSONArray}\n */\n private JSONArray parseJSONArray(String json) throws AssertionError {\n JSONParser parser = new JSONParser();\n try {\n Object obj = parser.parse(json);\n assertTrue(obj instanceof JSONArray);\n return (JSONArray) obj;\n } catch (Exception e) {\n throw new AssertionError(\"not a valid JSON array: \" + e.getMessage());\n }\n }\n}\n =================================================================== --- oak-mk/src/test/java/org/apache/jackrabbit/mk/store/DefaultRevisionStoreTest.java (revision 9a0d535548bdafbe7939d902829b72a3fb9b9a07) +++ oak-mk/src/test/java/org/apache/jackrabbit/mk/store/DefaultRevisionStoreTest.java (revision ) @@ -142,17 +142,17 @@ public void run() { rs.gc(); } - }, 100, 20, TimeUnit.MILLISECONDS); + }, 10, 2, TimeUnit.MILLISECONDS); mk.commit("/", "+\"a\" : { \"b\" : { \"c\" : { \"d\" : {} } } }", mk.getHeadRevision(), null); try { - for (int i = 0; i < 20; i++) { + for (int i = 0; i < 2000; i++) { mk.commit("/a/b/c/d", "+\"e\" : {}", mk.getHeadRevision(), null); - Thread.sleep(10); + Thread.sleep(1); mk.commit("/a/b/c/d/e", "+\"f\" : {}", mk.getHeadRevision(), null); - Thread.sleep(30); + Thread.sleep(3); mk.commit("/a/b/c/d", "-\"e\"", mk.getHeadRevision(), null); } } finally {