Author: mduerig
Date: Tue Jan 16 14:28:02 2018
New Revision: 1821249
URL: http://svn.apache.org/viewvc?rev=1821249&view=rev
Log:
OAK-7132: SNFE after full compaction
Respect number of retained generations on full compaction
Added:
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/tar/GCGenerationTest.java
Modified:
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/Reclaimers.java
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/tar/GCGeneration.java
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/CompactionAndCleanupIT.java
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/ReclaimersTest.java
Modified:
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java?rev=1821249&r1=1821248&r2=1821249&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java
(original)
+++
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java
Tue Jan 16 14:28:02 2018
@@ -30,6 +30,8 @@ import static org.apache.jackrabbit.oak.
import static
org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
import static
org.apache.jackrabbit.oak.segment.DefaultSegmentWriterBuilder.defaultSegmentWriterBuilder;
import static org.apache.jackrabbit.oak.segment.SegmentId.isDataSegmentId;
+import static
org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.GCType.FULL;
+import static
org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.GCType.TAIL;
import static
org.apache.jackrabbit.oak.segment.compaction.SegmentGCStatus.CLEANUP;
import static
org.apache.jackrabbit.oak.segment.compaction.SegmentGCStatus.COMPACTION;
import static
org.apache.jackrabbit.oak.segment.compaction.SegmentGCStatus.COMPACTION_FORCE_COMPACT;
@@ -37,6 +39,7 @@ import static org.apache.jackrabbit.oak.
import static
org.apache.jackrabbit.oak.segment.compaction.SegmentGCStatus.ESTIMATION;
import static
org.apache.jackrabbit.oak.segment.compaction.SegmentGCStatus.IDLE;
import static
org.apache.jackrabbit.oak.segment.file.PrintableBytes.newPrintableBytes;
+import static
org.apache.jackrabbit.oak.segment.file.Reclaimers.newOldReclaimer;
import static
org.apache.jackrabbit.oak.segment.file.TarRevisions.EXPEDITE_OPTION;
import static org.apache.jackrabbit.oak.segment.file.TarRevisions.timeout;
@@ -76,6 +79,7 @@ import org.apache.jackrabbit.oak.segment
import org.apache.jackrabbit.oak.segment.SegmentWriter;
import org.apache.jackrabbit.oak.segment.WriterCacheManager;
import org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions;
+import org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.GCType;
import org.apache.jackrabbit.oak.segment.file.GCJournal.GCJournalEntry;
import org.apache.jackrabbit.oak.segment.file.ShutDown.ShutDownCloser;
import org.apache.jackrabbit.oak.segment.file.tar.CleanupContext;
@@ -384,11 +388,7 @@ public class FileStore extends AbstractF
*/
public void cleanup() throws IOException {
try (ShutDownCloser ignored = shutDown.keepAlive()) {
- fileReaper.add(garbageCollector.cleanup(CompactionResult.skipped(
- getGcGeneration(),
- garbageCollector.gcOptions,
- revisions.getHead()
- )));
+ fileReaper.add(garbageCollector.cleanup());
}
}
@@ -576,6 +576,15 @@ public class FileStore extends AbstractF
*/
private long lastSuccessfullGC;
+ /**
+ * Last compaction type used to determine which predicate to use during
+ * {@link #cleanup() cleanup}. Defaults to {@link GCType#FULL FULL},
which is
+ * conservative and safe in case it does not match the real type (e.g.
because
+ * of a system restart).
+ */
+ @Nonnull
+ private GCType lastCompactionType = FULL;
+
GarbageCollector(
@Nonnull SegmentGCOptions gcOptions,
@Nonnull GCListener gcListener,
@@ -684,9 +693,12 @@ public class FileStore extends AbstractF
}
@Nonnull
- private CompactionResult compactionSucceeded(@Nonnull GCGeneration
generation, @Nonnull RecordId compactedRootId) {
+ private CompactionResult compactionSucceeded(
+ @Nonnull GCType gcType,
+ @Nonnull GCGeneration generation,
+ @Nonnull RecordId compactedRootId) {
gcListener.compactionSucceeded(generation);
- return CompactionResult.succeeded(generation, gcOptions,
compactedRootId);
+ return CompactionResult.succeeded(gcType, generation, gcOptions,
compactedRootId);
}
@CheckForNull
@@ -708,20 +720,21 @@ public class FileStore extends AbstractF
synchronized CompactionResult compactFull() {
gcListener.info("running full compaction");
- return compact(EMPTY_NODE, getGcGeneration().nextFull());
+ return compact(FULL, EMPTY_NODE, getGcGeneration().nextFull());
}
synchronized CompactionResult compactTail() {
gcListener.info("running tail compaction");
SegmentNodeState base = getBase();
if (base != null) {
- return compact(base, getGcGeneration().nextTail());
+ return compact(TAIL, base, getGcGeneration().nextTail());
}
gcListener.info("no base state available, running full compaction
instead");
- return compact(EMPTY_NODE, getGcGeneration().nextFull());
+ return compact(FULL, EMPTY_NODE, getGcGeneration().nextFull());
}
private CompactionResult compact(
+ @Nonnull GCType gcType,
@Nonnull NodeState base,
@Nonnull GCGeneration newGeneration) {
try {
@@ -817,10 +830,12 @@ public class FileStore extends AbstractF
}
if (success) {
+ // Update type of the last compaction before calling
methods that could throw an exception.
+ lastCompactionType = gcType;
writer.flush();
flush();
gcListener.info("compaction succeeded in {}, after {}
cycles", watch, cycles);
- return compactionSucceeded(newGeneration,
compacted.getRecordId());
+ return compactionSucceeded(gcType, newGeneration,
compacted.getRecordId());
} else {
gcListener.info("compaction failed after {}, and {}
cycles", watch, cycles);
return compactionAborted(newGeneration);
@@ -902,6 +917,22 @@ public class FileStore extends AbstractF
}
/**
+ * Cleanup segments whose generation matches the reclaim predicate
determined by
+ * the {@link #lastCompactionType last successful compaction}.
+ * @return list of files to be removed
+ * @throws IOException
+ */
+ @Nonnull
+ synchronized List<File> cleanup() throws IOException {
+ return cleanup(CompactionResult.skipped(
+ lastCompactionType,
+ getGcGeneration(),
+ garbageCollector.gcOptions,
+ revisions.getHead()
+ ));
+ }
+
+ /**
* Cleanup segments whose generation matches the {@link
CompactionResult#reclaimer()} predicate.
* @return list of files to be removed
* @throws IOException
@@ -911,7 +942,7 @@ public class FileStore extends AbstractF
throws IOException {
PrintableStopwatch watch = PrintableStopwatch.createStarted();
- gcListener.info("cleanup started.");
+ gcListener.info("cleanup started using reclaimer {}",
compactionResult.reclaimer());
gcListener.updateStatus(CLEANUP.message());
segmentCache.clear();
@@ -964,7 +995,7 @@ public class FileStore extends AbstractF
synchronized void collectBlobReferences(Consumer<String> collector)
throws IOException {
segmentWriter.flush();
tarFiles.collectBlobReferences(collector,
- Reclaimers.newOldReclaimer(getGcGeneration(),
gcOptions.getRetainedGenerations()));
+ newOldReclaimer(lastCompactionType, getGcGeneration(),
gcOptions.getRetainedGenerations()));
}
void cancel() {
@@ -1038,9 +1069,9 @@ public class FileStore extends AbstractF
/**
* Instances of this class represent the result from a compaction. Either
- * {@link #succeeded(GCGeneration, SegmentGCOptions, RecordId) succeeded},
+ * {@link #succeeded(GCType, GCGeneration, SegmentGCOptions, RecordId)
succeeded},
* {@link #aborted(GCGeneration, GCGeneration) aborted} or {@link
- * #skipped(GCGeneration, SegmentGCOptions, RecordId)} skipped}.
+ * #skipped(GCType, GCGeneration, SegmentGCOptions, RecordId)} skipped}.
*/
private abstract static class CompactionResult {
@Nonnull
@@ -1052,18 +1083,20 @@ public class FileStore extends AbstractF
/**
* Result of a succeeded compaction.
+ * @param gcType the type of the succeeded compaction
operation
* @param newGeneration the generation successfully created by
compaction
* @param gcOptions the current GC options used by compaction
* @param compactedRootId the record id of the root created by
compaction
*/
static CompactionResult succeeded(
+ @Nonnull GCType gcType,
@Nonnull GCGeneration newGeneration,
@Nonnull final SegmentGCOptions gcOptions,
@Nonnull final RecordId compactedRootId) {
return new CompactionResult(newGeneration) {
@Override
Predicate<GCGeneration> reclaimer() {
- return Reclaimers.newOldReclaimer(newGeneration,
gcOptions.getRetainedGenerations());
+ return newOldReclaimer(gcType, newGeneration,
gcOptions.getRetainedGenerations());
}
@Override
@@ -1101,17 +1134,19 @@ public class FileStore extends AbstractF
/**
* Result serving as a placeholder for a compaction that was skipped.
+ * @param lastGCType type of the most recent gc operation.
{@link GCType#FULL} if none.
* @param currentGeneration the current generation of the store
- * @param gcOptions the current GC options used by compaction
+ * @param gcOptions the current GC options used by compaction
*/
static CompactionResult skipped(
+ @Nonnull GCType lastGCType,
@Nonnull GCGeneration currentGeneration,
@Nonnull final SegmentGCOptions gcOptions,
@Nonnull final RecordId compactedRootId) {
return new CompactionResult(currentGeneration) {
@Override
Predicate<GCGeneration> reclaimer() {
- return Reclaimers.newOldReclaimer(currentGeneration,
gcOptions.getRetainedGenerations());
+ return Reclaimers.newOldReclaimer(lastGCType,
currentGeneration, gcOptions.getRetainedGenerations());
}
@Override
@@ -1134,8 +1169,8 @@ public class FileStore extends AbstractF
abstract Predicate<GCGeneration> reclaimer();
/**
- * @return {@code true} for {@link #succeeded(GCGeneration,
SegmentGCOptions, RecordId) succeeded}
- * and {@link #skipped(GCGeneration, SegmentGCOptions,
RecordId) skipped}, {@code false} otherwise.
+ * @return {@code true} for {@link #succeeded(GCType, GCGeneration,
SegmentGCOptions, RecordId) succeeded}
+ * and {@link #skipped(GCType, GCGeneration,
SegmentGCOptions, RecordId) skipped}, {@code false} otherwise.
*/
abstract boolean isSuccess();
Modified:
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/Reclaimers.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/Reclaimers.java?rev=1821249&r1=1821248&r2=1821249&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/Reclaimers.java
(original)
+++
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/Reclaimers.java
Tue Jan 16 14:28:02 2018
@@ -17,18 +17,93 @@
package org.apache.jackrabbit.oak.segment.file;
+import static com.google.common.base.Preconditions.checkNotNull;
+
import javax.annotation.Nonnull;
import com.google.common.base.Predicate;
+import org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.GCType;
import org.apache.jackrabbit.oak.segment.file.tar.GCGeneration;
+/**
+ * Helper class exposing static factories for reclaimers. A reclaimer
+ * is a predicate used during the cleanup phase of garbage collection
+ * to decide whether a segment of a given generation is reclaimable.
+ */
class Reclaimers {
private Reclaimers() {
// Prevent instantiation.
}
+ /**
+ * Create a reclaimer for segments of old generations. Whether a segment
is considered old and
+ * thus reclaimable depends on the type of the most recent GC operation
and the number of
+ * retained generations.
+ * <p>
+ * In the case of {@link GCType#FULL FULL} a segment is reclaimable if its
+ * {@link GCGeneration#getFullGeneration() full generation} is at least
{@code retainedGenerations}
+ * in the past wrt. {@code referenceGeneration} <em>or</em> if its
+ * {@link GCGeneration#getGeneration() generation} is at least {@code
retainedGenerations} in the
+ * past wrt. {@code referenceGeneration} and it not a {@link
GCGeneration#isCompacted() compacted segment}.
+ * <p>
+ * In the case of {@link GCType#TAIL TAIL} a segment is reclaimable if its
+ * {@link GCGeneration#getGeneration() generation} is at least {@code
retainedGenerations} in the
+ * past wrt. {@code referenceGeneration} <em>and</em> the segment is not
in the same tail as
+ * segments of the {@code referenceGeneration}. A segment is in the same
tail as another segment
+ * if it is a {@link GCGeneration#isCompacted() compacted segment}
<em>and</em> both segments have
+ * the same {@link GCGeneration#fullGeneration full generation}.
+ *
+ * @param lastGCType type of the most recent GC operation. {@link
GCType#FULL} if unknown.
+ * @param referenceGeneration generation used as reference for
determining the age of other segments.
+ * @param retainedGenerations number of generations to retain.
+ */
static Predicate<GCGeneration> newOldReclaimer(
+ @Nonnull GCType lastGCType,
+ @Nonnull final GCGeneration referenceGeneration,
+ int retainedGenerations) {
+
+ switch (checkNotNull(lastGCType)) {
+ case FULL:
+ return newOldFullReclaimer(referenceGeneration,
retainedGenerations);
+ case TAIL:
+ return newOldTailReclaimer(referenceGeneration,
retainedGenerations);
+ default:
+ throw new IllegalArgumentException("Invalid gc type: " +
lastGCType);
+ }
+ }
+
+ private static Predicate<GCGeneration> newOldFullReclaimer(
+ @Nonnull final GCGeneration referenceGeneration,
+ int retainedGenerations) {
+ return new Predicate<GCGeneration>() {
+
+ @Override
+ public boolean apply(GCGeneration generation) {
+ return isOldFull(generation) || (isOld(generation) &&
!generation.isCompacted());
+ }
+
+ private boolean isOld(GCGeneration generation) {
+ return referenceGeneration.compareWith(generation) >=
retainedGenerations;
+ }
+
+ private boolean isOldFull(GCGeneration generation) {
+ return
referenceGeneration.compareFullGenerationWith(generation) >=
retainedGenerations;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "(full generation older than %d.%d, with %d retained
generations)",
+ referenceGeneration.getGeneration(),
+ referenceGeneration.getFullGeneration(),
+ retainedGenerations
+ );
+ }
+ };
+ }
+
+ private static Predicate<GCGeneration> newOldTailReclaimer(
@Nonnull final GCGeneration referenceGeneration,
int retainedGenerations) {
return new Predicate<GCGeneration>() {
@@ -50,7 +125,7 @@ class Reclaimers {
@Override
public String toString() {
return String.format(
- "(generation older than %d.%d, with %d retained
generations)",
+ "(generation older than %d.%d, with %d retained
generations and not in the same compacted tail)",
referenceGeneration.getGeneration(),
referenceGeneration.getFullGeneration(),
retainedGenerations
@@ -60,6 +135,12 @@ class Reclaimers {
};
}
+ /**
+ * Create an exact reclaimer. An exact reclaimer reclaims only segment of
on single generation.
+ * @param referenceGeneration the generation to collect.
+ * @return an new instance of an exact reclaimer for segments with their
generation
+ * matching {@code referenceGeneration}.
+ */
static Predicate<GCGeneration> newExactReclaimer(@Nonnull final
GCGeneration referenceGeneration) {
return new Predicate<GCGeneration>() {
@Override
Modified:
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/tar/GCGeneration.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/tar/GCGeneration.java?rev=1821249&r1=1821248&r2=1821249&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/tar/GCGeneration.java
(original)
+++
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/tar/GCGeneration.java
Tue Jan 16 14:28:02 2018
@@ -108,10 +108,25 @@ public final class GCGeneration {
return new GCGeneration(generation, fullGeneration, false);
}
+ /**
+ * Compare this generation with {@code gcGeneration}
+ * @param gcGeneration the generation this generation is compared against.
+ * @return Number of generations between this generation and {@code
gcGeneration}
+ */
public int compareWith(@Nonnull GCGeneration gcGeneration) {
return generation - checkNotNull(gcGeneration).generation;
}
+ /**
+ * Compare this full generation the full generation of {@code gcGeneration}
+ * @param gcGeneration the generation this generation is compared against.
+ * @return Number of generations between the full generations of this
generation
+ * and {@code gcGeneration}
+ */
+ public int compareFullGenerationWith(@Nonnull GCGeneration gcGeneration) {
+ return fullGeneration - checkNotNull(gcGeneration).fullGeneration;
+ }
+
@Override
public boolean equals(Object other) {
if (this == other) {
Modified:
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/CompactionAndCleanupIT.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/CompactionAndCleanupIT.java?rev=1821249&r1=1821248&r2=1821249&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/CompactionAndCleanupIT.java
(original)
+++
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/CompactionAndCleanupIT.java
Tue Jan 16 14:28:02 2018
@@ -84,7 +84,6 @@ import org.apache.jackrabbit.oak.stats.C
import org.apache.jackrabbit.oak.stats.DefaultStatisticsProvider;
import org.apache.jackrabbit.oak.stats.StatisticsProvider;
import org.junit.BeforeClass;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@@ -1411,7 +1410,6 @@ public class CompactionAndCleanupIT {
}
}
- @Ignore("OAK-7132") // FIXME OAK-7132: SNFE after full compaction
@Test
public void latestFullCompactedStateShouldNotBeDeleted() throws Exception {
SegmentGCOptions gcOptions = defaultGCOptions()
Modified:
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/ReclaimersTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/ReclaimersTest.java?rev=1821249&r1=1821248&r2=1821249&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/ReclaimersTest.java
(original)
+++
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/ReclaimersTest.java
Tue Jan 16 14:28:02 2018
@@ -17,91 +17,178 @@
package org.apache.jackrabbit.oak.segment.file;
+import static com.google.common.collect.Sets.newHashSet;
+import static java.lang.String.join;
+import static
org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.GCType.FULL;
+import static
org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.GCType.TAIL;
import static
org.apache.jackrabbit.oak.segment.file.Reclaimers.newExactReclaimer;
import static
org.apache.jackrabbit.oak.segment.file.Reclaimers.newOldReclaimer;
import static
org.apache.jackrabbit.oak.segment.file.tar.GCGeneration.newGCGeneration;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableMap;
import org.apache.jackrabbit.oak.segment.file.tar.GCGeneration;
import org.junit.Test;
public class ReclaimersTest {
- private static void testOldReclaimer(boolean isCompacted) {
- Predicate<GCGeneration> reclaimer = newOldReclaimer(newGCGeneration(3,
3, isCompacted), 2);
-
- // Don't reclaim young segments
- assertFalse(reclaimer.apply(newGCGeneration(3, 3, false)));
- assertFalse(reclaimer.apply(newGCGeneration(3, 3, true)));
- assertFalse(reclaimer.apply(newGCGeneration(3, 3, false)));
- assertFalse(reclaimer.apply(newGCGeneration(3, 3, true)));
- assertFalse(reclaimer.apply(newGCGeneration(2, 3, false)));
- assertFalse(reclaimer.apply(newGCGeneration(2, 3, true)));
- assertFalse(reclaimer.apply(newGCGeneration(2, 3, false)));
- assertFalse(reclaimer.apply(newGCGeneration(2, 3, true)));
-
- // Reclaim old and uncompacted segments
- assertTrue(reclaimer.apply(newGCGeneration(1, 3, false)));
- assertTrue(reclaimer.apply(newGCGeneration(0, 3, false)));
-
- // Don't reclaim old compacted segments from the same full generation
- assertFalse(reclaimer.apply(newGCGeneration(1, 3, true)));
- assertFalse(reclaimer.apply(newGCGeneration(0, 3, true)));
-
- // Reclaim old compacted segments from prior full generations
- assertTrue(reclaimer.apply(newGCGeneration(1, 2, true)));
- assertTrue(reclaimer.apply(newGCGeneration(1, 2, false)));
- assertTrue(reclaimer.apply(newGCGeneration(0, 2, true)));
- assertTrue(reclaimer.apply(newGCGeneration(0, 2, false)));
- }
-
- private static void testOldReclaimerSequence(boolean isCompacted) {
- Predicate<GCGeneration> reclaimer = newOldReclaimer(newGCGeneration(9,
2, isCompacted), 2);
-
- assertFalse(reclaimer.apply(newGCGeneration(9, 2, false)));
- assertFalse(reclaimer.apply(newGCGeneration(9, 2, true)));
- assertFalse(reclaimer.apply(newGCGeneration(8, 2, false)));
- assertFalse(reclaimer.apply(newGCGeneration(8, 2, true)));
- assertTrue(reclaimer.apply(newGCGeneration(7, 2, false)));
- assertFalse(reclaimer.apply(newGCGeneration(7, 2, true)));
- assertTrue(reclaimer.apply(newGCGeneration(6, 2, false)));
- assertFalse(reclaimer.apply(newGCGeneration(6, 2, true)));
-
- assertTrue(reclaimer.apply(newGCGeneration(5, 1, false)));
- assertTrue(reclaimer.apply(newGCGeneration(5, 1, true)));
- assertTrue(reclaimer.apply(newGCGeneration(4, 1, false)));
- assertTrue(reclaimer.apply(newGCGeneration(4, 1, true)));
- assertTrue(reclaimer.apply(newGCGeneration(3, 1, false)));
- assertTrue(reclaimer.apply(newGCGeneration(3, 1, true)));
-
- assertTrue(reclaimer.apply(newGCGeneration(2, 0, false)));
- assertTrue(reclaimer.apply(newGCGeneration(2, 0, true)));
- assertTrue(reclaimer.apply(newGCGeneration(1, 0, false)));
- assertTrue(reclaimer.apply(newGCGeneration(1, 0, true)));
- assertTrue(reclaimer.apply(newGCGeneration(0, 0, false)));
+ private static final Map<String, GCGeneration> gcHistory =
ImmutableMap.<String, GCGeneration>builder()
+ .put("00w", newGCGeneration(0, 0, false))
+ // First compaction. Always FULL
+ .put("11c", newGCGeneration(1, 1, true))
+ .put("11w", newGCGeneration(1, 1, false))
+
+ // TAIL compaction
+ .put("21c", newGCGeneration(2, 1, true))
+ .put("21w", newGCGeneration(2, 1, false))
+
+ // TAIL compaction
+ .put("31c", newGCGeneration(3, 1, true))
+ .put("31w", newGCGeneration(3, 1, false))
+
+ // FULL compaction
+ .put("42c", newGCGeneration(4, 2, true))
+ .put("42w", newGCGeneration(4, 2, false))
+
+ // TAIL compaction
+ .put("52c", newGCGeneration(5, 2, true))
+ .put("52w", newGCGeneration(5, 2, false))
+
+ // TAIL compaction
+ .put("62c", newGCGeneration(6, 2, true))
+ .put("62w", newGCGeneration(6, 2, false))
+
+ // FULL compaction
+ .put("73c", newGCGeneration(7, 3, true))
+ .put("73w", newGCGeneration(7, 3, false))
+
+ .build();
+
+ private static void assertReclaim(Predicate<GCGeneration> reclaimer,
String... reclaims) {
+ Set<String> toReclaim = newHashSet(reclaims);
+ for (Entry<String, GCGeneration> generation : gcHistory.entrySet()) {
+ if (reclaimer.apply(generation.getValue())) {
+ assertTrue(
+ reclaimer + " should not reclaim " + generation.getKey(),
+ toReclaim.remove(generation.getKey()));
+ }
+ }
+
+ if (!toReclaim.isEmpty()) {
+ fail(reclaimer + " failed to reclaim " + join(",", toReclaim));
+ }
}
@Test
- public void testOldReclaimerCompactedHead() {
- testOldReclaimer(true);
- }
-
- @Test
- public void testOldReclaimerUncompactedHead() {
- testOldReclaimer(false);
- }
-
- @Test
- public void testOldReclaimerSequenceCompactedHead() throws Exception {
- testOldReclaimerSequence(true);
+ public void testOldReclaimer() {
+ // 1 retained generation
+ assertReclaim(newOldReclaimer(TAIL,
+ newGCGeneration(0, 0, false), 1));
+ assertReclaim(newOldReclaimer(FULL,
+ newGCGeneration(1, 1, false), 1),
+ "00w");
+ assertReclaim(newOldReclaimer(TAIL,
+ newGCGeneration(2, 1, false), 1),
+ "00w", "11w");
+ assertReclaim(newOldReclaimer(TAIL,
+ newGCGeneration(3, 1, false), 1),
+ "00w", "11w", "21w");
+ assertReclaim(newOldReclaimer(FULL,
+ newGCGeneration(4, 2, false), 1),
+ "00w", "11w", "11c", "21w", "21c", "31w", "31c");
+ assertReclaim(newOldReclaimer(TAIL,
+ newGCGeneration(5, 2, false), 1),
+ "00w", "11w", "11c", "21w", "21c", "31w", "31c", "42w");
+ assertReclaim(newOldReclaimer(TAIL,
+ newGCGeneration(6, 2, false), 1),
+ "00w", "11w", "11c", "21w", "21c", "31w", "31c", "42w", "52w");
+ assertReclaim(newOldReclaimer(FULL,
+ newGCGeneration(7, 3, false), 1),
+ "00w", "11w", "11c", "21w", "21c", "31w", "31c", "42w", "42c",
"52w", "52c", "62w", "62c");
+
+ // 2 retained generation
+ assertReclaim(newOldReclaimer(TAIL,
+ newGCGeneration(0, 0, false), 2));
+ assertReclaim(newOldReclaimer(FULL,
+ newGCGeneration(1, 1, false), 2));
+ assertReclaim(newOldReclaimer(TAIL,
+ newGCGeneration(2, 1, false), 2),
+ "00w");
+ assertReclaim(newOldReclaimer(TAIL,
+ newGCGeneration(3, 1, false), 2),
+ "00w", "11w");
+ assertReclaim(newOldReclaimer(FULL,
+ newGCGeneration(4, 2, false), 2),
+ "00w", "11w", "21w");
+ assertReclaim(newOldReclaimer(TAIL,
+ newGCGeneration(5, 2, false), 2),
+ "00w", "11w", "11c", "21w", "21c", "31w", "31c");
+ assertReclaim(newOldReclaimer(TAIL,
+ newGCGeneration(6, 2, false), 2),
+ "00w", "11w", "11c", "21w", "21c", "31w", "31c", "42w");
+ assertReclaim(newOldReclaimer(FULL,
+ newGCGeneration(7, 3, false), 2),
+ "00w", "11w", "11c", "21w", "21c", "31w", "31c", "42w", "52w");
}
@Test
- public void testOldReclaimerSequenceUncompactedHead() throws Exception {
- testOldReclaimerSequence(false);
+ public void testOldReclaimerDefaultingToFull() {
+ // 1 retained generation
+ assertReclaim(newOldReclaimer(FULL,
+ newGCGeneration(0, 0, false), 1));
+ assertReclaim(newOldReclaimer(FULL,
+ newGCGeneration(1, 1, false), 1),
+ "00w");
+ assertReclaim(newOldReclaimer(FULL,
+ newGCGeneration(2, 1, false), 1),
+ "00w", "11w");
+ assertReclaim(newOldReclaimer(FULL,
+ newGCGeneration(3, 1, false), 1),
+ "00w", "11w", "21w");
+ assertReclaim(newOldReclaimer(FULL,
+ newGCGeneration(4, 2, false), 1),
+ "00w", "11w", "11c", "21w", "21c", "31w", "31c");
+ assertReclaim(newOldReclaimer(FULL,
+ newGCGeneration(5, 2, false), 1),
+ "00w", "11w", "11c", "21w", "21c", "31w", "31c", "42w");
+ assertReclaim(newOldReclaimer(FULL,
+ newGCGeneration(6, 2, false), 1),
+ "00w", "11w", "11c", "21w", "21c", "31w", "31c", "42w", "52w");
+ assertReclaim(newOldReclaimer(FULL,
+ newGCGeneration(7, 3, false), 1),
+ "00w", "11w", "11c", "21w", "21c", "31w", "31c", "42w", "42c",
"52w", "52c", "62w", "62c");
+
+ // 2 retained generation
+ assertReclaim(newOldReclaimer(FULL,
+ newGCGeneration(0, 0, false), 2));
+ assertReclaim(newOldReclaimer(FULL,
+ newGCGeneration(1, 1, false), 2));
+ assertReclaim(newOldReclaimer(FULL,
+ newGCGeneration(2, 1, false), 2),
+ "00w");
+ assertReclaim(newOldReclaimer(FULL,
+ newGCGeneration(3, 1, false), 2),
+ "00w", "11w");
+ assertReclaim(newOldReclaimer(FULL,
+ newGCGeneration(4, 2, false), 2),
+ "00w", "11w", "21w");
+ assertReclaim(newOldReclaimer(FULL,
+ newGCGeneration(5, 2, false), 2),
+ "00w", "11w", "21w", "31w");
+ assertReclaim(newOldReclaimer(FULL,
+ newGCGeneration(6, 2, false), 2),
+ "00w", "11w", "21w", "31w", "42w");
+ assertReclaim(newOldReclaimer(FULL,
+ newGCGeneration(7, 3, false), 2),
+ "00w", "11w", "11c", "21w", "21c", "31w", "31c", "42w", "52w");
}
@Test
Added:
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/tar/GCGenerationTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/tar/GCGenerationTest.java?rev=1821249&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/tar/GCGenerationTest.java
(added)
+++
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/tar/GCGenerationTest.java
Tue Jan 16 14:28:02 2018
@@ -0,0 +1,41 @@
+/*
+ * 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.file.tar;
+
+import static
org.apache.jackrabbit.oak.segment.file.tar.GCGeneration.newGCGeneration;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class GCGenerationTest {
+
+ @Test
+ public void testCompareWith() {
+ GCGeneration m = newGCGeneration(0, 0, false);
+ GCGeneration n = newGCGeneration(2, 3, false);
+ assertEquals(2, n.compareWith(m));
+ }
+
+ @Test
+ public void testCompareFullGenerationWith() {
+ GCGeneration m = newGCGeneration(0, 0, false);
+ GCGeneration n = newGCGeneration(2, 3, false);
+ assertEquals(3, n.compareFullGenerationWith(m));
+ }
+}
\ No newline at end of file