diff --git oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/DelegatingGCMonitor.java oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/DelegatingGCMonitor.java index f100ded..974811c 100644 --- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/DelegatingGCMonitor.java +++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/DelegatingGCMonitor.java @@ -110,5 +110,12 @@ public class DelegatingGCMonitor implements GCMonitor { gcMonitor.cleaned(reclaimedSize, currentSize); } } + + @Override + public void updateStatus(String status) { + for (GCMonitor gcMonitor : gcMonitors) { + gcMonitor.updateStatus(status); + } + } } diff --git oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/GCMonitor.java oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/GCMonitor.java index 6df722a..07ffc50 100644 --- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/GCMonitor.java +++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/GCMonitor.java @@ -70,6 +70,12 @@ public interface GCMonitor { * @param currentSize number of bytes after garbage collection */ void cleaned(long reclaimedSize, long currentSize); + + /** + * The garbage collection entered a new phase e.g. idle, estimation, etc. + * @param status short summary of the GC phase + */ + void updateStatus(String status); class Empty implements GCMonitor { @Override public void info(String message, Object[] arguments) { } @@ -78,5 +84,6 @@ public interface GCMonitor { @Override public void skipped(String reason, Object[] arguments) { } @Override public void compacted() { } @Override public void cleaned(long reclaimedSize, long currentSize) { } + @Override public void updateStatus(String status) { } } } diff --git oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/GCMonitorTracker.java oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/GCMonitorTracker.java index 10f411e..dc883cc 100644 --- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/GCMonitorTracker.java +++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/GCMonitorTracker.java @@ -73,4 +73,10 @@ public class GCMonitorTracker extends AbstractServiceTracker implemen } } + @Override + public void updateStatus(String status) { + for (GCMonitor gcMonitor : getServices()) { + gcMonitor.updateStatus(status); + } + } } diff --git oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/LoggingGCMonitor.java oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/LoggingGCMonitor.java index 6aeab8f..81b2236 100644 --- oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/LoggingGCMonitor.java +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/LoggingGCMonitor.java @@ -67,4 +67,9 @@ public class LoggingGCMonitor implements GCMonitor { @Override public void cleaned(long reclaimedSize, long currentSize) { } + + @Override + public void updateStatus(String status) { + + } } diff --git oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentGCStatus.java oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentGCStatus.java new file mode 100644 index 0000000..d721ce1 --- /dev/null +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentGCStatus.java @@ -0,0 +1,39 @@ +/* + * 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.compaction; + +public enum SegmentGCStatus { + IDLE("idle"), + ESTIMATION("estimation"), + COMPACTION("compaction"), + COMPACTION_RETRY("compaction-retry-"), + COMPACTION_FORCE_COMPACT("compaction-force-compact"), + CLEANUP("cleanup"); + + private String message; + + private SegmentGCStatus(String message) { + this.message = message; + } + + public String message() { + return message; + } +} diff --git oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentRevisionGC.java oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentRevisionGC.java index dc229e7..f18993d 100644 --- oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentRevisionGC.java +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentRevisionGC.java @@ -141,6 +141,12 @@ public interface SegmentRevisionGC { */ @CheckForNull String getLastError(); + + /** + * @return last log message or {@code null} if none. + */ + @Nonnull + String getLastLogMessage(); /** * @return current status. diff --git oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentRevisionGCMBean.java oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentRevisionGCMBean.java index 2029e1a..0a24682 100644 --- oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentRevisionGCMBean.java +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentRevisionGCMBean.java @@ -150,7 +150,12 @@ public class SegmentRevisionGCMBean public String getLastError() { return fileStoreGCMonitor.getLastError(); } - + + @Override + public String getLastLogMessage() { + return fileStoreGCMonitor.getLastLogMessage(); + } + @Nonnull @Override public String getStatus() { diff --git oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java index bfe63c8..1ddb8e9 100644 --- oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java @@ -36,6 +36,7 @@ import static org.apache.jackrabbit.oak.segment.SegmentId.isDataSegmentId; import static org.apache.jackrabbit.oak.segment.SegmentWriterBuilder.segmentWriterBuilder; import static org.apache.jackrabbit.oak.segment.file.TarRevisions.EXPEDITE_OPTION; import static org.apache.jackrabbit.oak.segment.file.TarRevisions.timeout; +import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCStatus.*; import java.io.File; import java.io.IOException; @@ -722,57 +723,63 @@ public class FileStore extends AbstractFileStore { } synchronized void run() throws IOException { - gcListener.info("TarMK GC #{}: started", GC_COUNT.incrementAndGet()); - GCMemoryBarrier gcMemoryBarrier = new GCMemoryBarrier( - sufficientMemory, gcListener, GC_COUNT.get(), gcOptions); - - boolean sufficientEstimatedGain = true; - if (gcOptions.isEstimationDisabled()) { - gcListener.info("TarMK GC #{}: estimation skipped because it was explicitly disabled", GC_COUNT); - } else if (gcOptions.isPaused()) { - gcListener.info("TarMK GC #{}: estimation skipped because compaction is paused", GC_COUNT); - } else { - gcListener.info("TarMK GC #{}: estimation started", GC_COUNT); - Stopwatch watch = Stopwatch.createStarted(); - Supplier cancel = new CancelCompactionSupplier(FileStore.this); - GCEstimation estimate = estimateCompactionGain(cancel); - if (cancel.get()) { - gcListener.info("TarMK GC #{}: estimation interrupted: {}. Skipping compaction.", GC_COUNT, cancel); - gcMemoryBarrier.close(); - return; - } - - sufficientEstimatedGain = estimate.gcNeeded(); - String gcLog = estimate.gcLog(); - if (sufficientEstimatedGain) { - gcListener.info( - "TarMK GC #{}: estimation completed in {} ({} ms). {}", - GC_COUNT, watch, watch.elapsed(MILLISECONDS), gcLog); + try { + gcListener.info("TarMK GC #{}: started", GC_COUNT.incrementAndGet()); + GCMemoryBarrier gcMemoryBarrier = new GCMemoryBarrier( + sufficientMemory, gcListener, GC_COUNT.get(), gcOptions); + + boolean sufficientEstimatedGain = true; + if (gcOptions.isEstimationDisabled()) { + gcListener.info("TarMK GC #{}: estimation skipped because it was explicitly disabled", GC_COUNT); + } else if (gcOptions.isPaused()) { + gcListener.info("TarMK GC #{}: estimation skipped because compaction is paused", GC_COUNT); } else { - gcListener.skipped( - "TarMK GC #{}: estimation completed in {} ({} ms). {}", - GC_COUNT, watch, watch.elapsed(MILLISECONDS), gcLog); + gcListener.info("TarMK GC #{}: estimation started", GC_COUNT); + gcListener.updateStatus(ESTIMATION.message()); + + Stopwatch watch = Stopwatch.createStarted(); + Supplier cancel = new CancelCompactionSupplier(FileStore.this); + GCEstimation estimate = estimateCompactionGain(cancel); + if (cancel.get()) { + gcListener.info("TarMK GC #{}: estimation interrupted: {}. Skipping compaction.", GC_COUNT, cancel); + gcMemoryBarrier.close(); + return; + } + + sufficientEstimatedGain = estimate.gcNeeded(); + String gcLog = estimate.gcLog(); + if (sufficientEstimatedGain) { + gcListener.info( + "TarMK GC #{}: estimation completed in {} ({} ms). {}", + GC_COUNT, watch, watch.elapsed(MILLISECONDS), gcLog); + } else { + gcListener.skipped( + "TarMK GC #{}: estimation completed in {} ({} ms). {}", + GC_COUNT, watch, watch.elapsed(MILLISECONDS), gcLog); + } } - } - - if (sufficientEstimatedGain) { - if (!gcOptions.isPaused()) { - logAndClear(segmentWriter.getNodeWriteTimeStats(), segmentWriter.getNodeCompactTimeStats()); - log(segmentWriter.getNodeCacheOccupancyInfo()); - int gen = compact(); - if (gen > 0) { - fileReaper.add(cleanupOldGenerations(gen)); - } else if (gen < 0) { - gcListener.info("TarMK GC #{}: cleaning up after failed compaction", GC_COUNT); - fileReaper.add(cleanupGeneration(-gen)); + + if (sufficientEstimatedGain) { + if (!gcOptions.isPaused()) { + logAndClear(segmentWriter.getNodeWriteTimeStats(), segmentWriter.getNodeCompactTimeStats()); + log(segmentWriter.getNodeCacheOccupancyInfo()); + int gen = compact(); + if (gen > 0) { + fileReaper.add(cleanupOldGenerations(gen)); + } else if (gen < 0) { + gcListener.info("TarMK GC #{}: cleaning up after failed compaction", GC_COUNT); + fileReaper.add(cleanupGeneration(-gen)); + } + logAndClear(segmentWriter.getNodeWriteTimeStats(), segmentWriter.getNodeCompactTimeStats()); + log(segmentWriter.getNodeCacheOccupancyInfo()); + } else { + gcListener.skipped("TarMK GC #{}: compaction paused", GC_COUNT); } - logAndClear(segmentWriter.getNodeWriteTimeStats(), segmentWriter.getNodeCompactTimeStats()); - log(segmentWriter.getNodeCacheOccupancyInfo()); - } else { - gcListener.skipped("TarMK GC #{}: compaction paused", GC_COUNT); } + gcMemoryBarrier.close(); + } finally { + gcListener.updateStatus(IDLE.message()); } - gcMemoryBarrier.close(); } /** @@ -822,6 +829,7 @@ public class FileStore extends AbstractFileStore { try { Stopwatch watch = Stopwatch.createStarted(); gcListener.info("TarMK GC #{}: compaction started, gc options={}", GC_COUNT, gcOptions); + gcListener.updateStatus(COMPACTION.message()); SegmentNodeState before = getHead(); Supplier cancel = new CancelCompactionSupplier(FileStore.this); @@ -851,6 +859,8 @@ public class FileStore extends AbstractFileStore { gcListener.info("TarMK GC #{}: compaction detected concurrent commits while compacting. " + "Compacting these commits. Cycle {} of {}", GC_COUNT, cycles, gcOptions.getRetryCount()); + gcListener.updateStatus(COMPACTION_RETRY.message() + cycles); + SegmentNodeState head = getHead(); after = compact(head, writer, cancel); if (after == null) { @@ -870,6 +880,8 @@ public class FileStore extends AbstractFileStore { if (forceTimeout > 0) { gcListener.info("TarMK GC #{}: trying to force compact remaining commits for {} seconds", GC_COUNT, forceTimeout); + gcListener.updateStatus(COMPACTION_FORCE_COMPACT.message()); + cycles++; success = forceCompact(writer, or(cancel, timeOut(forceTimeout, SECONDS))); if (!success) { @@ -1022,6 +1034,7 @@ public class FileStore extends AbstractFileStore { fileStoreLock.writeLock().lock(); try { gcListener.info("TarMK GC #{}: cleanup started.", GC_COUNT); + gcListener.updateStatus(CLEANUP.message()); newWriter(); segmentCache.clear(); diff --git oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStoreGCMonitor.java oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStoreGCMonitor.java index d0b9414..b66bea7 100644 --- oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStoreGCMonitor.java +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStoreGCMonitor.java @@ -29,6 +29,7 @@ import java.util.Date; import javax.annotation.Nonnull; +import org.apache.jackrabbit.oak.segment.compaction.SegmentGCStatus; import org.apache.jackrabbit.oak.spi.gc.GCMonitor; import org.apache.jackrabbit.oak.stats.Clock; @@ -43,7 +44,8 @@ public class FileStoreGCMonitor implements GCMonitor { private long lastRepositorySize; private long lastReclaimedSize; private String lastError; - private String status = "NA"; + private String lastLogMessage; + private String status = SegmentGCStatus.IDLE.message(); public FileStoreGCMonitor(@Nonnull Clock clock) { this.clock = checkNotNull(clock); @@ -53,12 +55,12 @@ public class FileStoreGCMonitor implements GCMonitor { @Override public void info(String message, Object... arguments) { - status = arrayFormat(message, arguments).getMessage(); + lastLogMessage = arrayFormat(message, arguments).getMessage(); } @Override public void warn(String message, Object... arguments) { - status = arrayFormat(message, arguments).getMessage(); + lastLogMessage = arrayFormat(message, arguments).getMessage(); } @Override @@ -71,7 +73,7 @@ public class FileStoreGCMonitor implements GCMonitor { @Override public void skipped(String reason, Object... arguments) { - status = arrayFormat(reason, arguments).getMessage(); + lastLogMessage = arrayFormat(reason, arguments).getMessage(); } @Override @@ -85,6 +87,11 @@ public class FileStoreGCMonitor implements GCMonitor { lastReclaimedSize = reclaimed; lastRepositorySize = current; } + + @Override + public void updateStatus(String status) { + this.status = status; + } public String getLastCompaction() { return toString(lastCompaction); @@ -115,6 +122,11 @@ public class FileStoreGCMonitor implements GCMonitor { } @Nonnull + public String getLastLogMessage() { + return lastLogMessage; + } + + @Nonnull public String getStatus() { return status; } diff --git oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/SegmentCompactionIT.java oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/SegmentCompactionIT.java index 78d3c8c..3a0d70f 100644 --- oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/SegmentCompactionIT.java +++ oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/SegmentCompactionIT.java @@ -720,6 +720,11 @@ public class SegmentCompactionIT { cleaned = true; delegate.cleaned(reclaimedSize, currentSize); } + + @Override + public void updateStatus(String status) { + delegate.updateStatus(status); + } public boolean isCleaned() { return cleaned; diff --git oak-segment/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java oak-segment/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java index cf66699..f5c0118 100644 --- oak-segment/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java +++ oak-segment/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java @@ -1609,5 +1609,10 @@ public class FileStore implements SegmentStore { public void cleaned(long reclaimedSize, long currentSize) { delegatee.cleaned(reclaimedSize, currentSize); } + + @Override + public void updateStatus(String status) { + delegatee.updateStatus(status); + } } } diff --git oak-segment/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStoreGCMonitor.java oak-segment/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStoreGCMonitor.java index 63025d9..b52537b 100644 --- oak-segment/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStoreGCMonitor.java +++ oak-segment/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStoreGCMonitor.java @@ -121,6 +121,12 @@ public class FileStoreGCMonitor extends AnnotatedStandardMBean repositorySize.getCounter().set(current); reclaimedSize.getCounter().addAndGet(reclaimed); } + + @Override + @Deprecated + public void updateStatus(String status) { + + } //------------------------------------------------------------< GCMonitorMBean >--- diff --git oak-segment/src/test/java/org/apache/jackrabbit/oak/plugins/segment/SegmentCompactionIT.java oak-segment/src/test/java/org/apache/jackrabbit/oak/plugins/segment/SegmentCompactionIT.java index 99bc135..ecb91d5 100644 --- oak-segment/src/test/java/org/apache/jackrabbit/oak/plugins/segment/SegmentCompactionIT.java +++ oak-segment/src/test/java/org/apache/jackrabbit/oak/plugins/segment/SegmentCompactionIT.java @@ -723,6 +723,11 @@ public class SegmentCompactionIT { cleaned = true; delegate.cleaned(reclaimedSize, currentSize); } + + @Override + public void updateStatus(String status) { + delegate.updateStatus(status); + } public boolean isCleaned() { return cleaned;