diff --git oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStore.java oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStore.java index 53eaac3..3c998f0 100644 --- oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStore.java +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStore.java @@ -61,6 +61,7 @@ import org.apache.jackrabbit.oak.spi.state.ConflictAnnotatingRebaseDiff; import org.apache.jackrabbit.oak.spi.state.NodeBuilder; import org.apache.jackrabbit.oak.spi.state.NodeState; import org.apache.jackrabbit.oak.spi.state.NodeStore; +import org.apache.jackrabbit.oak.stats.StatisticsProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -99,7 +100,11 @@ public class SegmentNodeStore implements NodeStore, Observable { private boolean isCreated; private boolean dispatchChanges = true; + + @Nonnull + private StatisticsProvider statsProvider = StatisticsProvider.NOOP; + private SegmentNodeStoreBuilder( @Nonnull Revisions revisions, @Nonnull SegmentReader reader, @@ -116,7 +121,18 @@ public class SegmentNodeStore implements NodeStore, Observable { this.dispatchChanges = dispatchChanges; return this; } - + + /** + * {@link StatisticsProvider} for collecting statistics related to SegmentStore + * @param statisticsProvider + * @return this instance + */ + @Nonnull + public SegmentNodeStoreBuilder withStatisticsProvider(@Nonnull StatisticsProvider statisticsProvider) { + this.statsProvider = checkNotNull(statisticsProvider); + return this; + } + @Nonnull public SegmentNodeStore build() { checkState(!isCreated); @@ -129,6 +145,11 @@ public class SegmentNodeStore implements NodeStore, Observable { private static String getString(@CheckForNull BlobStore blobStore) { return "blobStore=" + (blobStore == null ? "inline" : blobStore); } + + @Nonnull + StatisticsProvider getStatsProvider() { + return statsProvider; + } @Override public String toString() { @@ -165,7 +186,7 @@ public class SegmentNodeStore implements NodeStore, Observable { @CheckForNull private final BlobStore blobStore; - + private final ChangeDispatcher changeDispatcher; /** @@ -194,6 +215,8 @@ public class SegmentNodeStore implements NodeStore, Observable { */ private static final boolean COMMIT_FAIR_LOCK = Boolean .parseBoolean(System.getProperty("oak.segmentNodeStore.commitFairLock", "true")); + + private final SegmentNodeStoreStats stats; private SegmentNodeStore(SegmentNodeStoreBuilder builder) { if (COMMIT_FAIR_LOCK) { @@ -210,6 +233,8 @@ public class SegmentNodeStore implements NodeStore, Observable { } else { this.changeDispatcher = null; } + + this.stats = new SegmentNodeStoreStats(builder.getStatsProvider()); } void setMaximumBackoff(long max) { @@ -285,12 +310,35 @@ public class SegmentNodeStore implements NodeStore, Observable { checkArgument(snb.isRootBuilder()); checkNotNull(commitHook); + boolean queued = false; + try { + long queuedTime = -1; + + if (commitSemaphore.availablePermits() < 1) { + queuedTime = System.nanoTime(); + stats.onCommitQueued(); + queued = true; + } + commitSemaphore.acquire(); try { + if (queued) { + long dequeuedTime = System.nanoTime(); + stats.dequeuedAfter(dequeuedTime - queuedTime); + stats.onCommitDequeued(); + } + + long beforeCommitTime = System.nanoTime(); + Commit commit = new Commit(snb, commitHook, info); NodeState merged = commit.execute(); snb.reset(merged); + + long afterCommitTime = System.nanoTime(); + stats.committedAfter(afterCommitTime - beforeCommitTime); + stats.onCommit(); + return merged; } finally { commitSemaphore.release(); @@ -509,6 +557,10 @@ public class SegmentNodeStore implements NodeStore, Observable { NodeState getCheckpoints() { return head.get().getChildNode(CHECKPOINTS); } + + public SegmentNodeStoreStats getStats() { + return stats; + } private class Commit { diff --git oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreMonitor.java oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreMonitor.java new file mode 100644 index 0000000..22377b4 --- /dev/null +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreMonitor.java @@ -0,0 +1,80 @@ +/* + * 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.segment; + +/** + * SegmentNodeStoreMonitor is notified for commit related operations performed by SegmentNodeStore. + */ +public interface SegmentNodeStoreMonitor { + + SegmentNodeStoreMonitor DEFAULT = new SegmentNodeStoreMonitor() { + @Override + public void onCommit() { + + } + + @Override + public void onCommitQueued() { + + } + + public void onCommitDequeued() { + + } + + @Override + public void committedAfter(long time) { + + } + + @Override + public void dequeuedAfter(long time) { + + } + }; + + /** + * Notifies the monitor when a new commit was persisted right away + */ + void onCommit(); + + /** + * Notifies the monitor when a new commit couldn't be persisted, but was + * queued for later retry + */ + void onCommitQueued(); + + /** + * Notifies the monitor when a queued commit was dequeued for processing. + */ + void onCommitDequeued(); + + /** + * Notifies the monitor time spent (excluding queuing time) for a commit. + * @param time the time spent + */ + void committedAfter(long time); + + /** + * Notifies the monitor time spent in the queue for a commit, before being processed. + * @param time the time spent + */ + void dequeuedAfter(long time); +} diff --git oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java index 804e1fe..0ad4cb8 100644 --- oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java @@ -536,7 +536,8 @@ public class SegmentNodeStoreService extends ProxyNodeStore final long blobGcMaxAgeInSecs = toLong(property(PROP_BLOB_GC_MAX_AGE), DEFAULT_BLOB_GC_MAX_AGE); SegmentNodeStore.SegmentNodeStoreBuilder segmentNodeStoreBuilder = - SegmentNodeStoreBuilders.builder(store); + SegmentNodeStoreBuilders.builder(store) + .withStatisticsProvider(statisticsProvider); if (toBoolean(property(STANDBY), false)) { segmentNodeStoreBuilder.dispatchChanges(false); } @@ -623,6 +624,16 @@ public class SegmentNodeStoreService extends ProxyNodeStore FileStoreBackupRestoreMBean.TYPE, "Segment node store backup/restore" )); + // Expose statistics about the SegmentNodeStore + + registrations.add(registerMBean( + whiteboard, + SegmentNodeStoreStatsMBean.class, + segmentNodeStore.getStats(), + SegmentNodeStoreStatsMBean.TYPE, + "SegmentNodeStore statistics" + )); + log.info("SegmentNodeStore initialized"); // Register a factory service to expose the FileStore diff --git oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreStats.java oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreStats.java new file mode 100644 index 0000000..a0f61b8 --- /dev/null +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreStats.java @@ -0,0 +1,109 @@ +/* + * 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.segment; + +import static org.apache.jackrabbit.stats.TimeSeriesStatsUtil.asCompositeData; + +import java.util.concurrent.TimeUnit; + +import javax.management.openmbean.CompositeData; + +import org.apache.jackrabbit.api.stats.TimeSeries; +import org.apache.jackrabbit.oak.stats.CounterStats; +import org.apache.jackrabbit.oak.stats.MeterStats; +import org.apache.jackrabbit.oak.stats.StatisticsProvider; +import org.apache.jackrabbit.oak.stats.StatsOptions; +import org.apache.jackrabbit.oak.stats.TimerStats; + +public class SegmentNodeStoreStats implements SegmentNodeStoreStatsMBean, SegmentNodeStoreMonitor { + public static final String COMMITS_COUNT = "COMMITS_COUNT"; + public static final String COMMIT_QUEUE_SIZE = "COMMIT_QUEUE_SIZE"; + public static final String COMMIT_TIME = "COMMIT_TIME"; + public static final String QUEUEING_TIME = "QUEUEING_TIME"; + + private final StatisticsProvider statisticsProvider; + private final MeterStats commitsCount; + private final CounterStats commitQueueSize; + private final TimerStats commitTime; + private final TimerStats queueingTime; + + public SegmentNodeStoreStats(StatisticsProvider statisticsProvider) { + this.statisticsProvider = statisticsProvider; + this.commitsCount = statisticsProvider.getMeter(COMMITS_COUNT, StatsOptions.DEFAULT); + this.commitQueueSize = statisticsProvider.getCounterStats(COMMIT_QUEUE_SIZE, StatsOptions.DEFAULT); + this.commitTime = statisticsProvider.getTimer(COMMIT_TIME, StatsOptions.DEFAULT); + this.queueingTime = statisticsProvider.getTimer(QUEUEING_TIME, StatsOptions.DEFAULT); + } + + //~--------------------------------< SegmentStoreMonitor > + + @Override + public void onCommit() { + commitsCount.mark(); + } + + @Override + public void onCommitQueued() { + commitQueueSize.inc(); + } + + public void onCommitDequeued() { + commitQueueSize.dec(); + } + + + @Override + public void committedAfter(long time) { + commitTime.update(time, TimeUnit.NANOSECONDS); + } + + @Override + public void dequeuedAfter(long time) { + queueingTime.update(time, TimeUnit.NANOSECONDS); + } + + //~--------------------------------< SegmentStoreStatsMBean > + + @Override + public CompositeData getCommitsCount() { + return asCompositeData(getTimeSeries(COMMITS_COUNT), COMMITS_COUNT); + } + + + @Override + public CompositeData getQueuingCommitsCount() { + return asCompositeData(getTimeSeries(COMMIT_QUEUE_SIZE), COMMIT_QUEUE_SIZE); + } + + @Override + public CompositeData getCommitTimes() { + return asCompositeData(getTimeSeries(COMMIT_TIME), COMMIT_TIME); + } + + @Override + public CompositeData getQueuingTimes() { + return asCompositeData(getTimeSeries(QUEUEING_TIME), QUEUEING_TIME); + } + + private TimeSeries getTimeSeries(String name) { + return statisticsProvider.getStats().getTimeSeries(name, true); + } + +} diff --git oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreStatsMBean.java oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreStatsMBean.java new file mode 100644 index 0000000..6b49076 --- /dev/null +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreStatsMBean.java @@ -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.segment; + +import javax.management.openmbean.CompositeData; + +public interface SegmentNodeStoreStatsMBean { + String TYPE = "SegmentStoreStats"; + + /** + * @return time series of the number of commits + */ + CompositeData getCommitsCount(); + + /** + * @return time series of the number of commits queuing + */ + CompositeData getQueuingCommitsCount(); + + /** + * @return time series of the commit times + */ + CompositeData getCommitTimes(); + + /** + * @return time series of the queuing times + */ + CompositeData getQueuingTimes(); +}