Index: src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentGCOptions.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentGCOptions.java (revision 1761539) +++ src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentGCOptions.java (working copy) @@ -79,6 +79,8 @@ "oak.segment.compaction.gcSizeDeltaEstimation", SIZE_DELTA_ESTIMATION_DEFAULT); + private volatile boolean stopCompaction; + public SegmentGCOptions(boolean paused, int gainThreshold, int retryCount, int forceTimeout) { this.paused = paused; this.gainThreshold = gainThreshold; @@ -293,4 +295,14 @@ this.gcSizeDeltaEstimation = gcSizeDeltaEstimation; return this; } + + public boolean isStopCompaction() { + return stopCompaction; + } + + public boolean setStopCompaction(boolean stop) { + this.stopCompaction = stop; + return stop; + } + } Index: src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentRevisionGC.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentRevisionGC.java (revision 1761539) +++ src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentRevisionGC.java (working copy) @@ -101,4 +101,8 @@ void setGcSizeDeltaEstimation(long gcSizeDeltaEstimation); + /** + * Raise the flag to signal compaction to stop as soon as possible. + */ + void stopCompaction(); } Index: src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentRevisionGCMBean.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentRevisionGCMBean.java (revision 1761539) +++ src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentRevisionGCMBean.java (working copy) @@ -93,4 +93,9 @@ gcOptions.setGcSizeDeltaEstimation(gcSizeDeltaEstimation); } + @Override + public void stopCompaction() { + gcOptions.setStopCompaction(true); + } + } Index: src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java (revision 1761539) +++ src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java (working copy) @@ -516,10 +516,10 @@ gcListener.info("TarMK GC #{}: estimation skipped because compaction is paused", GC_COUNT); } else { gcListener.info("TarMK GC #{}: estimation started", GC_COUNT); - Supplier shutdown = newShutdownSignal(); - GCEstimation estimate = estimateCompactionGain(shutdown); - if (shutdown.get()) { - gcListener.info("TarMK GC #{}: estimation interrupted. Skipping compaction.", GC_COUNT); + Supplier cancel = newCancelCompactionCondition(); + GCEstimation estimate = estimateCompactionGain(cancel); + if (cancel.get()) { + gcListener.info("TarMK GC #{}: estimation interrupted: {}. Skipping compaction.", GC_COUNT, cancel); } sufficientEstimatedGain = estimate.gcNeeded(); @@ -931,47 +931,11 @@ } /** - * Returns the cancellation policy for the compaction phase. If the disk - * space was considered insufficient at least once during compaction (or if - * the space was never sufficient to begin with), compaction is considered - * canceled. - * Furthermore when the file store is shutting down, compaction is considered - * canceled. - * - * @return a flag indicating if compaction should be canceled. + * Returns the cancellation policy for the compaction phase. + * @return a supplier indicating if compaction should be canceled. */ private Supplier newCancelCompactionCondition() { - return new Supplier() { - - private boolean outOfDiskSpace; - private boolean shutdown; - - @Override - public Boolean get() { - - // The outOfDiskSpace and shutdown flags can only transition from false (their initial - // values), to true. Once true, there should be no way to go back. - if (!sufficientDiskSpace.get()) { - outOfDiskSpace = true; - } - if (FileStore.this.shutdown) { - this.shutdown = true; - } - - return shutdown || outOfDiskSpace; - } - - @Override - public String toString() { - if (outOfDiskSpace) { - return "Not enough disk space available"; - } else if (shutdown) { - return "FileStore shutdown request received"; - } else { - return ""; - } - } - }; + return new CancelCompactionSupplier(this); } /** @@ -1007,19 +971,6 @@ } /** - * Returns a signal indication the file store shutting down. - * @return a shutdown signal - */ - private Supplier newShutdownSignal() { - return new Supplier() { - @Override - public Boolean get() { - return shutdown; - } - }; - } - - /** * Copy every referenced record in data (non-bulk) segments. Bulk segments * are fully kept (they are only removed in cleanup, if there is no * reference to them). @@ -1045,7 +996,7 @@ Supplier cancel = newCancelCompactionCondition(); SegmentNodeState after = compact(bufferWriter, before, cancel); if (after == null) { - gcListener.info("TarMK GC #{}: compaction cancelled.", GC_COUNT); + gcListener.info("TarMK GC #{}: compaction cancelled: {}.", GC_COUNT, cancel); return false; } @@ -1067,7 +1018,7 @@ SegmentNodeState head = getHead(); after = compact(bufferWriter, head, cancel); if (after == null) { - gcListener.info("TarMK GC #{}: compaction cancelled.", GC_COUNT); + gcListener.info("TarMK GC #{}: compaction cancelled: {}.", GC_COUNT, cancel); return false; } @@ -1086,9 +1037,13 @@ cycles++; success = forceCompact(bufferWriter, or(cancel, timeOut(forceTimeout, SECONDS))); if (!success) { - gcListener.warn("TarMK GC #{}: compaction failed to force compact remaining commits. " + - "Most likely compaction didn't get exclusive access to the store or was " + - "prematurely cancelled.", GC_COUNT); + if(cancel.get()) { + gcListener.warn("TarMK GC #{}: compaction failed to force compact remaining commits. " + + "Compaction was cancelled: {}.", GC_COUNT, cancel); + } else { + gcListener.warn("TarMK GC #{}: compaction failed to force compact remaining commits. " + + "Most likely compaction didn't get exclusive access to the store.", GC_COUNT); + } } } } @@ -1684,4 +1639,61 @@ } } + + /** + * Represents the cancellation policy for the compaction phase. If the disk + * space was considered insufficient at least once during compaction (or if + * the space was never sufficient to begin with), compaction is considered + * canceled. Furthermore when the file store is shutting down, compaction is + * considered canceled. + */ + private static class CancelCompactionSupplier implements Supplier { + + private static enum REASON { + UNKNOWN, DISK_SPACE, SHUTDOWN, MANUAL + }; + + private REASON reason = REASON.UNKNOWN; + + private final FileStore store; + + public CancelCompactionSupplier(FileStore store) { + this.store = store; + this.store.gcOptions.setStopCompaction(false); + } + + @Override + public Boolean get() { + // The outOfDiskSpace and shutdown flags can only transition from + // false (their initial + // values), to true. Once true, there should be no way to go back. + if (!store.sufficientDiskSpace.get()) { + reason = REASON.DISK_SPACE; + return true; + } + if (store.shutdown) { + reason = REASON.SHUTDOWN; + return true; + } + if (store.gcOptions.isStopCompaction()) { + reason = REASON.MANUAL; + return true; + } + return false; + } + + @Override + public String toString() { + switch (reason) { + case DISK_SPACE: + return "Not enough disk space available"; + case SHUTDOWN: + return "FileStore shutdown request received"; + case MANUAL: + return "GC stop request received"; + default: + return ""; + } + } + } }