diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java index bfe63c8..8756f09 100644 --- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java +++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java @@ -1105,7 +1105,7 @@ public class FileStore extends AbstractFileStore { long finalSize = size(); long reclaimedSize = initialSize - afterCleanupSize; stats.reclaimed(reclaimedSize); - gcJournal.persist(reclaimedSize, finalSize); + gcJournal.persist(reclaimedSize, finalSize, getGcGeneration()); gcListener.cleaned(reclaimedSize, finalSize); gcListener.info("TarMK GC #{}: cleanup completed in {} ({} ms). Post cleanup size is {} ({} bytes)" + " and space reclaimed {} ({} bytes).", diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/GCJournal.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/GCJournal.java index b9e1d16..1406673 100644 --- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/GCJournal.java +++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/GCJournal.java @@ -41,8 +41,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Persists the repository size and the reclaimed size following a cleanup operation in the - * {@link #GC_JOURNAL gc journal} file with the format: 'repoSize, reclaimedSize, timestamp'. + * Persists the repository size and the reclaimed size following a cleanup + * operation in the {@link #GC_JOURNAL gc journal} file with the format: + * 'repoSize, reclaimedSize, timestamp, gcGen'. */ public class GCJournal { @@ -59,8 +60,20 @@ public class GCJournal { this.directory = checkNotNull(directory); } - public synchronized void persist(long reclaimedSize, long repoSize) { - latest = new GCJournalEntry(repoSize, reclaimedSize, System.currentTimeMillis()); + /** + * Persists the repository size and the reclaimed size following a cleanup + * operation + */ + public synchronized void persist(long reclaimedSize, long repoSize, + int gcGeneration) { + GCJournalEntry current = read(); + if (current.getGcGeneration() == gcGeneration) { + // failed compaction, only update the journal if the generation + // increases + return; + } + latest = new GCJournalEntry(repoSize, reclaimedSize, + System.currentTimeMillis(), gcGeneration); Path path = new File(directory, GC_JOURNAL).toPath(); try { try (BufferedWriter w = newBufferedWriter(path, UTF_8, WRITE, @@ -73,6 +86,9 @@ public class GCJournal { } } + /** + * Returns the latest entry available + */ public synchronized GCJournalEntry read() { if (latest == null) { List all = readLines(); @@ -86,6 +102,9 @@ public class GCJournal { return latest; } + /** + * Returns all available entries from the journal + */ public synchronized Collection readAll() { List all = new ArrayList(); for (String l : readLines()) { @@ -108,30 +127,38 @@ public class GCJournal { static class GCJournalEntry { - static GCJournalEntry EMPTY = new GCJournalEntry(-1, -1, -1); + static GCJournalEntry EMPTY = new GCJournalEntry(-1, -1, -1, -1); private final long repoSize; private final long reclaimedSize; private final long ts; + private final int gcGeneration; - public GCJournalEntry(long repoSize, long reclaimedSize, long ts) { + public GCJournalEntry(long repoSize, long reclaimedSize, long ts, + int gcGeneration) { this.repoSize = repoSize; this.reclaimedSize = reclaimedSize; this.ts = ts; + this.gcGeneration = gcGeneration; } @Override public String toString() { - return repoSize + "," + reclaimedSize + "," + ts; + return repoSize + "," + reclaimedSize + "," + ts + "," + + gcGeneration; } static GCJournalEntry fromString(String in) { String[] items = in.split(","); - if (items.length == 3) { + if (items.length == 3 || items.length == 4) { long repoSize = safeParse(items[0]); long reclaimedSize = safeParse(items[1]); long ts = safeParse(items[2]); - return new GCJournalEntry(repoSize, reclaimedSize, ts); + int gcGen = -1; + if (items.length == 4) { + gcGen = (int) safeParse(items[3]); + } + return new GCJournalEntry(repoSize, reclaimedSize, ts, gcGen); } return GCJournalEntry.EMPTY; } @@ -157,11 +184,17 @@ public class GCJournal { return ts; } + public int getGcGeneration() { + return gcGeneration; + } + @Override public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + (int) (reclaimedSize ^ (reclaimedSize >>> 32)); + result = prime * result + gcGeneration; + result = prime * result + + (int) (reclaimedSize ^ (reclaimedSize >>> 32)); result = prime * result + (int) (repoSize ^ (repoSize >>> 32)); result = prime * result + (int) (ts ^ (ts >>> 32)); return result; @@ -176,6 +209,8 @@ public class GCJournal { if (getClass() != obj.getClass()) return false; GCJournalEntry other = (GCJournalEntry) obj; + if (gcGeneration != other.gcGeneration) + return false; if (reclaimedSize != other.reclaimedSize) return false; if (repoSize != other.repoSize) @@ -184,5 +219,6 @@ public class GCJournal { return false; return true; } + } } diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/GcJournalTest.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/GcJournalTest.java index cfce239..1f212c3 100644 --- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/GcJournalTest.java +++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/GcJournalTest.java @@ -44,21 +44,27 @@ public class GcJournalTest { File directory = segmentFolder.newFolder(); GCJournal gc = new GCJournal(directory); - gc.persist(0, 100); + gc.persist(0, 100, 1); GCJournalEntry e0 = gc.read(); assertEquals(100, e0.getRepoSize()); assertEquals(0, e0.getReclaimedSize()); - gc.persist(0, 250); + gc.persist(0, 250, 2); GCJournalEntry e1 = gc.read(); assertEquals(250, e1.getRepoSize()); assertEquals(0, e1.getReclaimedSize()); - - gc.persist(50, 200); + + gc.persist(50, 200, 3); GCJournalEntry e2 = gc.read(); assertEquals(200, e2.getRepoSize()); assertEquals(50, e2.getReclaimedSize()); + // same gen + gc.persist(75, 300, 3); + GCJournalEntry e3 = gc.read(); + assertEquals(200, e3.getRepoSize()); + assertEquals(50, e3.getReclaimedSize()); + Collection all = gc.readAll(); assertEquals(all.size(), 3);