Index: oak-core/src/main/java/org/apache/jackrabbit/oak/Oak.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/Oak.java (revision 1585541) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/Oak.java (working copy) @@ -49,6 +49,7 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.io.Closer; + import org.apache.jackrabbit.oak.api.ContentRepository; import org.apache.jackrabbit.oak.api.ContentSession; import org.apache.jackrabbit.oak.api.Root; @@ -62,6 +63,8 @@ import org.apache.jackrabbit.oak.plugins.index.CompositeIndexEditorProvider; import org.apache.jackrabbit.oak.plugins.index.IndexEditorProvider; import org.apache.jackrabbit.oak.plugins.index.IndexUpdateProvider; +import org.apache.jackrabbit.oak.plugins.index.property.jmx.PropertyIndexAsyncReindex; +import org.apache.jackrabbit.oak.plugins.index.property.jmx.PropertyIndexAsyncReindexMBean; import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore; import org.apache.jackrabbit.oak.query.QueryEngineSettings; import org.apache.jackrabbit.oak.spi.commit.CommitHook; @@ -508,6 +511,12 @@ scheduleWithFixedDelay(whiteboard, task, 5, true); registerMBean(whiteboard, IndexStatsMBean.class, task.getIndexStats(), IndexStatsMBean.TYPE, name); + + PropertyIndexAsyncReindex asyncPI = new PropertyIndexAsyncReindex( + new AsyncIndexUpdate("async-reindex", store, indexEditors, + true), executor); + registerMBean(whiteboard, PropertyIndexAsyncReindexMBean.class, + asyncPI, PropertyIndexAsyncReindexMBean.TYPE, name); } registerMBean(whiteboard, QueryEngineSettingsMBean.class, Index: oak-core/src/main/java/org/apache/jackrabbit/oak/api/jmx/RepositoryManagementMBean.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/api/jmx/RepositoryManagementMBean.java (revision 1585541) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/api/jmx/RepositoryManagementMBean.java (working copy) @@ -175,4 +175,23 @@ @CheckForNull String checkpoint(long lifetime); + /** + * Initiate a reindex operation for the property indexes marked for + * reindexing + * + * @return the status of the operation right after it was initiated + */ + @Nonnull + CompositeData startPropertyIndexAsyncReindex(); + + /** + * Asynchronous Property Index reindexing status + * + * @return the status of the ongoing operation or if none the terminal + * status of the last operation or Status not available if + * none. + */ + @Nonnull + CompositeData getPropertyIndexAsyncReindexStatus(); + } Index: oak-core/src/main/java/org/apache/jackrabbit/oak/management/RepositoryManager.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/management/RepositoryManager.java (revision 1585541) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/management/RepositoryManager.java (working copy) @@ -35,6 +35,7 @@ import org.apache.jackrabbit.oak.api.jmx.RepositoryManagementMBean; import org.apache.jackrabbit.oak.plugins.backup.FileStoreBackupRestoreMBean; import org.apache.jackrabbit.oak.plugins.blob.BlobGCMBean; +import org.apache.jackrabbit.oak.plugins.index.property.jmx.PropertyIndexAsyncReindexMBean; import org.apache.jackrabbit.oak.spi.state.RevisionGCMBean; import org.apache.jackrabbit.oak.spi.whiteboard.Tracker; import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard; @@ -181,4 +182,30 @@ : null; } + @Override + public CompositeData startPropertyIndexAsyncReindex() { + return execute(PropertyIndexAsyncReindexMBean.class, + new Function() { + @Nonnull + @Override + public Status apply(PropertyIndexAsyncReindexMBean reindexer) { + return fromCompositeData(reindexer + .startPropertyIndexAsyncReindex()); + } + }).toCompositeData(); + } + + @Override + public CompositeData getPropertyIndexAsyncReindexStatus() { + return execute(PropertyIndexAsyncReindexMBean.class, + new Function() { + @Nonnull + @Override + public Status apply(PropertyIndexAsyncReindexMBean reindexer) { + return fromCompositeData(reindexer + .getPropertyIndexAsyncReindexStatus()); + } + }).toCompositeData(); + } + } 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 1585541) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdate.java (working copy) @@ -126,7 +126,7 @@ public void indexUpdate() throws CommitFailedException { if (!dirty) { dirty = true; - preAsyncRun(store, name, indexStats); + preAsyncRun(store, name); } } @@ -161,6 +161,7 @@ } AsyncUpdateCallback callback = new AsyncUpdateCallback(); + preAsyncRunStatsStats(indexStats); IndexUpdate indexUpdate = new IndexUpdate(provider, name, after, builder, callback); @@ -170,7 +171,7 @@ if (callback.dirty) { async.setProperty(name, checkpoint); try { - store.merge(builder, newCommitHook(name, state, indexStats), + store.merge(builder, newCommitHook(name, state), CommitInfo.EMPTY); } catch (CommitFailedException e) { if (e != CONCURRENT_UPDATE) { @@ -180,8 +181,10 @@ if (switchOnSync) { reindexedDefinitions.addAll(indexUpdate .getReindexedDefinitions()); + } else { + postAsyncRunStatsStatus(indexStats); } - } else if (switchOnSync && !reindexedDefinitions.isEmpty()) { + } else if (switchOnSync) { log.debug("No changes detected after diff, will try to switch to synchronous updates on " + reindexedDefinitions); async.setProperty(name, checkpoint); @@ -198,9 +201,10 @@ } try { - store.merge(builder, newCommitHook(name, state, indexStats), + store.merge(builder, newCommitHook(name, state), CommitInfo.EMPTY); reindexedDefinitions.clear(); + postAsyncRunStatsStatus(indexStats); } catch (CommitFailedException e) { if (e != CONCURRENT_UPDATE) { exception = e; @@ -223,8 +227,7 @@ } private static CommitHook newCommitHook(final String name, - final PropertyState state, final AsyncIndexStats indexStats) - throws CommitFailedException { + final PropertyState state) throws CommitFailedException { return new CommitHook() { @Override @Nonnull @@ -234,7 +237,7 @@ PropertyState stateAfterRebase = before.getChildNode(ASYNC) .getProperty(name); if (Objects.equal(state, stateAfterRebase)) { - return postAsyncRunStatus(after.builder(), indexStats, name) + return postAsyncRunNodeStatus(after.builder(), name) .getNodeState(); } else { throw CONCURRENT_UPDATE; @@ -243,10 +246,9 @@ }; } - private static void preAsyncRun(NodeStore store, String name, - AsyncIndexStats stats) throws CommitFailedException { + private static void preAsyncRun(NodeStore store, String name) throws CommitFailedException { NodeBuilder builder = store.getRoot().builder(); - preAsyncRunStatus(builder, stats, name); + preAsyncRunNodeStatus(builder, name); store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY); } @@ -278,20 +280,21 @@ return false; } - private static void preAsyncRunStatus(NodeBuilder builder, - AsyncIndexStats stats, String name) { + private static void preAsyncRunNodeStatus(NodeBuilder builder, String name) { String now = now(); - stats.start(now); builder.getChildNode(INDEX_DEFINITIONS_NAME) .setProperty(name + "-status", STATUS_RUNNING) .setProperty(name + "-start", now, Type.DATE) .removeProperty(name + "-done"); } - private static NodeBuilder postAsyncRunStatus(NodeBuilder builder, - AsyncIndexStats stats, String name) { + private static void preAsyncRunStatsStats(AsyncIndexStats stats) { + stats.start(now()); + } + + private static NodeBuilder postAsyncRunNodeStatus(NodeBuilder builder, + String name) { String now = now(); - stats.done(now); builder.getChildNode(INDEX_DEFINITIONS_NAME) .setProperty(name + "-status", STATUS_DONE) .setProperty(name + "-done", now, Type.DATE) @@ -299,6 +302,10 @@ return builder; } + private static void postAsyncRunStatsStatus(AsyncIndexStats stats) { + stats.done(now()); + } + private static String now() { return ISO8601.format(Calendar.getInstance()); } @@ -307,6 +314,10 @@ return indexStats; } + public boolean isFinished() { + return indexStats.getStatus() == STATUS_DONE; + } + private static final class AsyncIndexStats implements IndexStatsMBean { private String start = ""; @@ -339,6 +350,12 @@ public String getStatus() { return status; } + + @Override + public String toString() { + return "AsyncIndexStats [start=" + start + ", done=" + done + + ", status=" + status + "]"; + } } } Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/PropertyIndexAsyncReindex.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/PropertyIndexAsyncReindex.java (revision 0) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/PropertyIndexAsyncReindex.java (revision 0) @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jackrabbit.oak.plugins.index.property.jmx; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.lang.System.nanoTime; +import static org.apache.jackrabbit.oak.management.ManagementOperation.done; + +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; + +import javax.annotation.Nonnull; +import javax.management.openmbean.CompositeData; + +import org.apache.jackrabbit.oak.management.ManagementOperation; +import org.apache.jackrabbit.oak.plugins.index.AsyncIndexUpdate; + +public class PropertyIndexAsyncReindex implements + PropertyIndexAsyncReindexMBean { + + public static final String OP_NAME = "Property index asynchronous reindex"; + + private final AsyncIndexUpdate async; + private final Executor executor; + + private ManagementOperation arOp = done(OP_NAME, 0); + + /** + * @param gc + * Revision garbage collector + * @param executor + * executor for running the garbage collection task + */ + public PropertyIndexAsyncReindex(@Nonnull AsyncIndexUpdate async, + @Nonnull Executor executor) { + this.async = checkNotNull(async); + this.executor = checkNotNull(executor); + } + + @Nonnull + @Override + public CompositeData startPropertyIndexAsyncReindex() { + if (arOp.isDone()) { + arOp = new ManagementOperation(OP_NAME, new Callable() { + @Override + public Long call() throws Exception { + long t0 = nanoTime(); + boolean done = false; + while (!done) { + async.run(); + done = async.isFinished(); + } + return nanoTime() - t0; + } + }); + executor.execute(arOp); + } + return getPropertyIndexAsyncReindexStatus(); + } + + @Nonnull + @Override + public CompositeData getPropertyIndexAsyncReindexStatus() { + return arOp.getStatus().toCompositeData(); + } +} Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/PropertyIndexAsyncReindexMBean.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/PropertyIndexAsyncReindexMBean.java (revision 0) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/PropertyIndexAsyncReindexMBean.java (revision 0) @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jackrabbit.oak.plugins.index.property.jmx; + +import javax.annotation.Nonnull; +import javax.management.openmbean.CompositeData; + +public interface PropertyIndexAsyncReindexMBean { + + String TYPE = "PropertyIndexAsyncReindex"; + + /** + * Initiate a reindex operation for the property indexes marked for + * reindexing + * + * @return the status of the operation right after it was initiated + */ + @Nonnull + CompositeData startPropertyIndexAsyncReindex(); + + /** + * Asynchronous Property Index reindexing status + * + * @return the status of the ongoing operation or if none the terminal + * status of the last operation or Status not available if + * none. + */ + @Nonnull + CompositeData getPropertyIndexAsyncReindexStatus(); + +} Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/package-info.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/package-info.java (revision 0) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/package-info.java (revision 0) @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@Version("0.21.0") +@Export(optional = "provide:=true") +package org.apache.jackrabbit.oak.plugins.index.property.jmx; + +import aQute.bnd.annotation.Version; +import aQute.bnd.annotation.Export; \ No newline at end of file