Index: oak-core/src/main/java/org/apache/jackrabbit/oak/api/jmx/IndexStatsMBean.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/api/jmx/IndexStatsMBean.java (revision 1614880) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/api/jmx/IndexStatsMBean.java (working copy) @@ -47,4 +47,41 @@ */ String getStatus(); + /** + * Pauses the background indexing process. Future changes are not indexed + * until the {@link #resume()} method is called. + * + * The pause call will take effect on the next run cycle and will affect all + * indexes marked as 'async'. + * + * Note: this is experimental and should only be used for + * debugging/diagnosis purposes! + * + */ + void pause(); + + /** + * Resumes the indexing process. All changes from the previous indexed state + * will be indexed. + * + * @see #pause() + */ + void resume(); + + /** + * Returns the value of the 'paused' flag + * + * @return true if the indexing job is paused + */ + boolean isPaused(); + + /** + * Returns the number of updates from the current run cycle. This value is + * kept until the next cycle begins. + * + * @return the number of updates from the current run cycle. This value is + * kept until the next cycle begins. + */ + long getUpdates(); + } Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdate.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdate.java (revision 1614880) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdate.java (working copy) @@ -157,6 +157,9 @@ NodeBuilder builder = root.builder(); builder.child(ASYNC).setProperty(leaseName, lease); mergeWithConcurrencyCheck(builder, checkpoint, beforeLease); + + //reset updates counter + indexStats.setUpdates(this.updates); } boolean isDirty() { @@ -174,6 +177,7 @@ public void indexUpdate() throws CommitFailedException { updates++; if (updates % 100 == 0) { + indexStats.setUpdates(this.updates); long now = System.currentTimeMillis(); if (now + ASYNC_TIMEOUT > lease) { long newLease = now + 2 * ASYNC_TIMEOUT; @@ -189,6 +193,9 @@ @Override public synchronized void run() { + if (indexStats.isPaused()) { + return; + } log.debug("Running background index task {}", name); NodeState root = store.getRoot(); @@ -365,12 +372,15 @@ return indexStats.getStatus() == STATUS_DONE; } - private static final class AsyncIndexStats implements IndexStatsMBean { + static final class AsyncIndexStats implements IndexStatsMBean { private String start = ""; private String done = ""; private String status = STATUS_INIT; + private volatile boolean isPaused; + private volatile long updates; + public void start(String now) { status = STATUS_RUNNING; start = now; @@ -398,10 +408,36 @@ return status; } + public void pause() { + log.debug("Pausing the async indexer"); + this.isPaused = true; + } + + @Override + public void resume() { + log.debug("Resuming the async indexer"); + this.isPaused = false; + } + + @Override + public boolean isPaused() { + return this.isPaused; + } + + void setUpdates(long updates) { + this.updates = updates; + } + + @Override + public long getUpdates() { + return updates; + } + @Override public String toString() { return "AsyncIndexStats [start=" + start + ", done=" + done - + ", status=" + status + "]"; + + ", status=" + status + ", paused=" + isPaused + + ", updates=" + updates + "]"; } } Index: oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdateTest.java =================================================================== --- oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdateTest.java (revision 1614880) +++ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdateTest.java (working copy) @@ -215,6 +215,41 @@ find(lookupChild, "foo", "abc")); } + @Test + public void testAsyncPause() throws Exception { + NodeStore store = new MemoryNodeStore(); + IndexEditorProvider provider = new PropertyIndexEditorProvider(); + + NodeBuilder builder = store.getRoot().builder(); + createIndexDefinition(builder.child(INDEX_DEFINITIONS_NAME), + "rootIndex", true, false, ImmutableSet.of("foo"), null) + .setProperty(ASYNC_PROPERTY_NAME, "async"); + builder.child("testRoot").setProperty("foo", "abc"); + + // merge it back in + store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY); + + AsyncIndexUpdate async = new AsyncIndexUpdate("async", store, provider); + async.getIndexStats().pause(); + async.run(); + assertFalse(store.getRoot().getChildNode(INDEX_DEFINITIONS_NAME) + .getChildNode("rootIndex") + .hasChildNode(INDEX_CONTENT_NODE_NAME)); + + async.getIndexStats().resume(); + async.run(); + NodeState root = store.getRoot(); + + // first check that the index content nodes exist + checkPathExists(root, INDEX_DEFINITIONS_NAME, "rootIndex", + INDEX_CONTENT_NODE_NAME); + assertFalse(root.getChildNode(INDEX_DEFINITIONS_NAME).hasChildNode( + ":conflict")); + + PropertyIndexLookup lookup = new PropertyIndexLookup(root); + assertEquals(ImmutableSet.of("testRoot"), find(lookup, "foo", "abc")); + } + // OAK-1749 @Test public void branchBaseOnCheckpoint() throws Exception {