Author: amitj
Date: Mon Jul  1 10:22:03 2019
New Revision: 1862366

URL: http://svn.apache.org/viewvc?rev=1862366&view=rev
Log:
OAK-8446: Enable datastore consistency check after completion of garbage 
collection

- Enables datastore consistency in a low cost way after GC

Modified:
    
jackrabbit/oak/trunk/oak-blob-plugins/src/main/java/org/apache/jackrabbit/oak/plugins/blob/MarkSweepGarbageCollector.java
    
jackrabbit/oak/trunk/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/BlobGCTest.java
    
jackrabbit/oak/trunk/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/SharedDataStoreMarkSweepGarbageCollectorTest.java
    
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/DataStoreCommand.java
    
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/DataStoreOptions.java
    
jackrabbit/oak/trunk/oak-run/src/test/java/org/apache/jackrabbit/oak/run/DataStoreCommandTest.java

Modified: 
jackrabbit/oak/trunk/oak-blob-plugins/src/main/java/org/apache/jackrabbit/oak/plugins/blob/MarkSweepGarbageCollector.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-blob-plugins/src/main/java/org/apache/jackrabbit/oak/plugins/blob/MarkSweepGarbageCollector.java?rev=1862366&r1=1862365&r2=1862366&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-blob-plugins/src/main/java/org/apache/jackrabbit/oak/plugins/blob/MarkSweepGarbageCollector.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-blob-plugins/src/main/java/org/apache/jackrabbit/oak/plugins/blob/MarkSweepGarbageCollector.java
 Mon Jul  1 10:22:03 2019
@@ -23,12 +23,14 @@ import static org.apache.commons.io.File
 import static org.apache.jackrabbit.oak.commons.FileIOUtils.copy;
 import static org.apache.jackrabbit.oak.commons.FileIOUtils.merge;
 import static org.apache.jackrabbit.oak.commons.FileIOUtils.sort;
+import static org.apache.jackrabbit.oak.commons.FileIOUtils.writeStrings;
 import static org.apache.jackrabbit.oak.commons.IOUtils.closeQuietly;
 
 import java.io.BufferedWriter;
 import java.io.ByteArrayInputStream;
 import java.io.Closeable;
 import java.io.File;
+import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.LineNumberReader;
@@ -116,6 +118,9 @@ public class MarkSweepGarbageCollector i
     /** The blob store to be garbage collected. */
     private final GarbageCollectableBlobStore blobStore;
 
+    /** Flag to enable low cost consistency check after DSGC */
+    private boolean checkConsistencyAfterGc;
+
     /** Helper class to mark blob references which **/
     private final BlobReferenceRetriever marker;
 
@@ -167,12 +172,14 @@ public class MarkSweepGarbageCollector i
             String root,
             int batchCount,
             long maxLastModifiedInterval,
+            boolean checkConsistencyAfterGc,
             @Nullable String repositoryId,
             @Nullable Whiteboard whiteboard,
             @Nullable StatisticsProvider statisticsProvider)
             throws IOException {
         this.executor = executor;
         this.blobStore = blobStore;
+        this.checkConsistencyAfterGc = checkConsistencyAfterGc;
         checkNotNull(blobStore, "BlobStore cannot be null");
         this.marker = marker;
         this.batchCount = batchCount;
@@ -204,7 +211,7 @@ public class MarkSweepGarbageCollector i
             long maxLastModifiedInterval,
             @Nullable String repositoryId)
             throws IOException {
-        this(marker, blobStore, executor, root, batchCount, 
maxLastModifiedInterval, repositoryId, null, null);
+        this(marker, blobStore, executor, root, batchCount, 
maxLastModifiedInterval, false, repositoryId, null, null);
     }
 
     /**
@@ -219,7 +226,7 @@ public class MarkSweepGarbageCollector i
             @Nullable Whiteboard whiteboard,
             @Nullable StatisticsProvider statisticsProvider)
             throws IOException {
-        this(marker, blobStore, executor, TEMP_DIR, DEFAULT_BATCH_COUNT, 
maxLastModifiedInterval, repositoryId, whiteboard, statisticsProvider);
+        this(marker, blobStore, executor, TEMP_DIR, DEFAULT_BATCH_COUNT, 
maxLastModifiedInterval, false, repositoryId, whiteboard, statisticsProvider);
     }
 
     @Override
@@ -506,6 +513,9 @@ public class MarkSweepGarbageCollector i
             closeQuietly(removesWriter);
         }
 
+        if (checkConsistencyAfterGc) {
+            
BlobCollectionType.get(blobStore).checkConsistencyAfterGC(blobStore, fs, 
consistencyStatsCollector, new File(root));
+        }
         BlobCollectionType.get(blobStore).handleRemoves(blobStore, 
fs.getGarbage(), fs.getMarkedRefs());
 
         if(count != deleted) {
@@ -1006,6 +1016,50 @@ public class MarkSweepGarbageCollector i
             FileUtils.forceDelete(removedIds);
         }
 
+        void checkConsistencyAfterGC(GarbageCollectableBlobStore blobStore, 
GarbageCollectorFileState fs,
+            OperationStatsCollector consistencyStatsCollector, File root) 
throws IOException {
+            consistencyStatsCollector.start();
+            Stopwatch sw = Stopwatch.createStarted();
+
+            try {
+                // Remove and spool the remaining ids into a temp file
+                File availAfterGC = new 
File(fs.getAvailableRefs().getParent(), "availAfterGC");
+                FileLineDifferenceIterator iterator = null;
+                try {
+                    iterator = new FileLineDifferenceIterator(fs.getGarbage(), 
fs.getAvailableRefs(), null);
+                    writeStrings(iterator, availAfterGC, false);
+                } finally {
+                    if (iterator != null) {
+                        iterator.close();
+                    }
+                }
+
+                LOG.trace("Starting difference phase of the consistency 
check");
+                FileLineDifferenceIterator iter =
+                    new FileLineDifferenceIterator(availAfterGC, 
fs.getMarkedRefs(), transformer);
+                File consistencyCandidatesAfterGC = new 
File(fs.getGcCandidates().getParent(), "consistencyCandidatesAfterGC");
+                // Write the original candidates
+                int candidates = FileIOUtils.writeStrings(iter, 
consistencyCandidatesAfterGC, true);
+
+                LOG.trace("Ending difference phase of the consistency check");
+                LOG.warn("Consistency check found [{}] missing blobs", 
candidates);
+
+                if (candidates > 0) {
+                    LineIterator lineIterator = new LineIterator(new 
FileReader(consistencyCandidatesAfterGC));
+                    while(lineIterator.hasNext()) {
+                        LOG.warn("Missing Blob [{}]", lineIterator.nextLine());
+                    }
+                    LOG.warn(
+                        "Consistency check failure in the the blob store after 
GC : {}", blobStore);
+                    consistencyStatsCollector.finishFailure();
+                    consistencyStatsCollector.updateNumDeleted(candidates);
+                }
+            } finally {
+                sw.stop();
+                
consistencyStatsCollector.updateDuration(sw.elapsed(TimeUnit.MILLISECONDS), 
TimeUnit.MILLISECONDS);
+            }
+        }
+
         /**
          * Tracker may want to track this file
          *

Modified: 
jackrabbit/oak/trunk/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/BlobGCTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/BlobGCTest.java?rev=1862366&r1=1862365&r2=1862366&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/BlobGCTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/BlobGCTest.java
 Mon Jul  1 10:22:03 2019
@@ -110,6 +110,7 @@ import static org.apache.jackrabbit.oak.
 import static 
org.apache.jackrabbit.oak.plugins.blob.datastore.SharedDataStoreUtils.SharedStoreRecordType.REPOSITORY;
 import static org.apache.jackrabbit.oak.stats.StatsOptions.METRICS_ONLY;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 /**
@@ -204,9 +205,13 @@ public class BlobGCTest {
         }
 
         public MarkSweepGarbageCollector getCollector(long blobGcMaxAgeInSecs) 
throws Exception {
+            return getCollector(blobGcMaxAgeInSecs, false);
+        }
+
+        public MarkSweepGarbageCollector getCollector(long blobGcMaxAgeInSecs, 
boolean checkConsistency) throws Exception {
             collector =
                 new MarkSweepGarbageCollector(referenceRetriever, blobStore, 
executor, root.getAbsolutePath(), 2048,
-                    blobGcMaxAgeInSecs, repoId, wb, statsProvider);
+                    blobGcMaxAgeInSecs, checkConsistency, repoId, wb, 
statsProvider);
             return collector;
         }
 
@@ -291,6 +296,20 @@ public class BlobGCTest {
     }
 
     @Test
+    public void gcWithConsistencyCheck() throws Exception {
+        log.info("Starting gcWithConsistencyCheck()");
+        ((MemoryBlobStoreNodeStore) 
cluster.nodeStore).getReferencedBlobs().add("SPURIOUS");
+
+        MarkSweepGarbageCollector collector = cluster.getCollector(0, true);
+        Set<String> existingAfterGC = executeGarbageCollection(cluster, 
collector, false);
+        
assertFalse(Sets.symmetricDifference(cluster.blobStoreState.blobsPresent, 
existingAfterGC).isEmpty());
+        assertStats(cluster.statsProvider, 1, 0,
+            cluster.blobStoreState.blobsAdded.size() - 
cluster.blobStoreState.blobsPresent.size() + 1,
+            cluster.blobStoreState.blobsAdded.size() - 
cluster.blobStoreState.blobsPresent.size() + 1, NAME);
+        assertStatsBean(collector.getConsistencyOperationStats(), 1, 1, 1);
+    }
+
+    @Test
     public void gcWithNoDeleteDirectBinary() throws Exception {
         log.info("Starting gcWithNoDeleteDirectBinary()");
 

Modified: 
jackrabbit/oak/trunk/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/SharedDataStoreMarkSweepGarbageCollectorTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/SharedDataStoreMarkSweepGarbageCollectorTest.java?rev=1862366&r1=1862365&r2=1862366&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/SharedDataStoreMarkSweepGarbageCollectorTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/SharedDataStoreMarkSweepGarbageCollectorTest.java
 Mon Jul  1 10:22:03 2019
@@ -90,6 +90,7 @@ public class SharedDataStoreMarkSweepGar
       MarkSweepGarbageCollector.TEMP_DIR,
       1,
       0L,
+      false,
       "repo",
       whiteboard,
       new DefaultStatisticsProvider(executor)

Modified: 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/DataStoreCommand.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/DataStoreCommand.java?rev=1862366&r1=1862365&r2=1862366&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/DataStoreCommand.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/DataStoreCommand.java
 Mon Jul  1 10:22:03 2019
@@ -202,8 +202,8 @@ public class DataStoreCommand implements
         MarkSweepGarbageCollector collector =
             new MarkSweepGarbageCollector(retriever, 
(GarbageCollectableBlobStore) fixture.getBlobStore(), service,
                 dataStoreOpts.getOutDir().getAbsolutePath(), 
dataStoreOpts.getBatchCount(),
-                SECONDS.toMillis(dataStoreOpts.getBlobGcMaxAgeInSecs()), 
repositoryId, fixture.getWhiteboard(),
-                getService(fixture.getWhiteboard(), StatisticsProvider.class));
+                SECONDS.toMillis(dataStoreOpts.getBlobGcMaxAgeInSecs()), 
dataStoreOpts.checkConsistencyAfterGC(),
+                repositoryId, fixture.getWhiteboard(), 
getService(fixture.getWhiteboard(), StatisticsProvider.class));
         collector.setTraceOutput(true);
 
         return collector;

Modified: 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/DataStoreOptions.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/DataStoreOptions.java?rev=1862366&r1=1862365&r2=1862366&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/DataStoreOptions.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/DataStoreOptions.java
 Mon Jul  1 10:22:03 2019
@@ -40,6 +40,7 @@ public class DataStoreOptions implements
     private final OptionSpec<File> outputDirOpt;
     private final OptionSpec<Boolean> collectGarbage;
     private final OptionSpec<Void> consistencyCheck;
+    private final OptionSpec<Boolean> checkConsistencyAfterGC;
     private final OptionSpec<Integer> batchCount;
     private OptionSet options;
     private final Set<OptionSpec> actionOpts;
@@ -55,6 +56,10 @@ public class DataStoreOptions implements
                 + "'markOnly' required if only mark phase of garbage 
collection is to be executed")
             .withOptionalArg().ofType(Boolean.class).defaultsTo(Boolean.FALSE);
 
+        checkConsistencyAfterGC = parser.accepts("check-consistency-gc",
+            "Performs a consistency check immediately after DSGC")
+            .withOptionalArg().ofType(Boolean.class).defaultsTo(Boolean.FALSE);
+
         consistencyCheck =
             parser.accepts("check-consistency", "Performs a consistency check 
on the repository/datastore defined");
 
@@ -134,6 +139,10 @@ public class DataStoreOptions implements
         return options.has(consistencyCheck);
     }
 
+    public boolean checkConsistencyAfterGC() {
+        return options.has(checkConsistencyAfterGC) && 
checkConsistencyAfterGC.value(options) ;
+    }
+
     public boolean markOnly() {
         return collectGarbage.value(options);
     }

Modified: 
jackrabbit/oak/trunk/oak-run/src/test/java/org/apache/jackrabbit/oak/run/DataStoreCommandTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/test/java/org/apache/jackrabbit/oak/run/DataStoreCommandTest.java?rev=1862366&r1=1862365&r2=1862366&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-run/src/test/java/org/apache/jackrabbit/oak/run/DataStoreCommandTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-run/src/test/java/org/apache/jackrabbit/oak/run/DataStoreCommandTest.java
 Mon Jul  1 10:22:03 2019
@@ -294,6 +294,19 @@ public class DataStoreCommandTest {
         testConsistency(dump, data, false);
     }
 
+    @Test
+    public void gcWithConsistency() throws Exception {
+        File dump = temporaryFolder.newFolder();
+        Data data = prepareData(storeFixture, blobFixture, 10, 5, 1);
+        storeFixture.close();
+        additionalParams += " --check-consistency-gc true";
+        testGc(dump, data, 0, false);
+
+        assertFileEquals(dump, "avail-", Sets.difference(data.added, 
data.missingDataStore));
+
+        // Verbose would have paths as well as ids changed but normally only 
DocumentNS would have paths suffixed
+        assertFileEquals(dump, "consistencyCandidatesAfterGC", 
data.missingDataStore);
+    }
 
     @Test
     public void gc() throws Exception {
@@ -430,7 +443,6 @@ public class DataStoreCommandTest {
                 data.missingDataStore);
     }
 
-
     private void testGc(File dump, Data data, long maxAge, boolean markOnly) 
throws Exception {
         List<String> argsList = Lists
             .newArrayList("--collect-garbage", String.valueOf(markOnly), 
"--max-age", String.valueOf(maxAge),
@@ -438,7 +450,7 @@ public class DataStoreCommandTest {
                 storeFixture.getConnectionString(), "--out-dir", 
dump.getAbsolutePath(), "--work-dir",
                 temporaryFolder.newFolder().getAbsolutePath());
         if (!Strings.isNullOrEmpty(additionalParams)) {
-            argsList.add(additionalParams);
+            argsList.addAll(Splitter.on(" ").splitToList(additionalParams));
         }
 
         DataStoreCommand cmd = new DataStoreCommand();


Reply via email to