Author: amitj
Date: Thu May 16 06:51:55 2019
New Revision: 1859350

URL: http://svn.apache.org/viewvc?rev=1859350&view=rev
Log:
OAK-8314: BlobIds created with direct uploads lack length suffix

Suffixing length to the ids being returned for for directly uploded binaries

Modified:
    
jackrabbit/oak/trunk/oak-blob-plugins/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/DataStoreBlobStore.java
    
jackrabbit/oak/trunk/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/BlobGCTest.java

Modified: 
jackrabbit/oak/trunk/oak-blob-plugins/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/DataStoreBlobStore.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-blob-plugins/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/DataStoreBlobStore.java?rev=1859350&r1=1859349&r2=1859350&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-blob-plugins/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/DataStoreBlobStore.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-blob-plugins/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/DataStoreBlobStore.java
 Thu May 16 06:51:55 2019
@@ -724,7 +724,7 @@ public class DataStoreBlobStore
         if (delegate instanceof DataRecordAccessProvider) {
             try {
                 DataRecord record = ((DataRecordAccessProvider) 
delegate).completeDataRecordUpload(uploadToken);
-                return new BlobStoreBlob(this, 
record.getIdentifier().toString());
+                return new BlobStoreBlob(this, getBlobId(record));
             }
             catch (DataStoreException | DataRecordUploadException e) {
                 log.warn("Unable to complete direct upload for upload token 
{}", uploadToken, e);

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=1859350&r1=1859349&r2=1859350&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
 Thu May 16 06:51:55 2019
@@ -26,19 +26,25 @@ import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.net.URI;
 import java.security.DigestOutputStream;
 import java.security.MessageDigest;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Random;
 import java.util.Set;
+import java.util.UUID;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.atomic.AtomicReference;
 
+import javax.jcr.RepositoryException;
+
 import ch.qos.logback.classic.Level;
 import com.google.common.collect.Iterators;
 import com.google.common.collect.Lists;
@@ -49,15 +55,25 @@ import org.apache.commons.io.IOUtils;
 import org.apache.commons.io.output.NullOutputStream;
 import org.apache.jackrabbit.core.data.DataIdentifier;
 import org.apache.jackrabbit.core.data.DataRecord;
+import org.apache.jackrabbit.core.data.DataStore;
 import org.apache.jackrabbit.core.data.DataStoreException;
+import org.apache.jackrabbit.core.data.MultiDataStoreAware;
 import org.apache.jackrabbit.oak.api.Blob;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.api.blob.BlobAccessProvider;
+import org.apache.jackrabbit.oak.api.blob.BlobUpload;
 import org.apache.jackrabbit.oak.commons.concurrent.ExecutorCloser;
 import org.apache.jackrabbit.oak.commons.junit.LogCustomizer;
+import org.apache.jackrabbit.oak.plugins.blob.datastore.DataStoreBlobStore;
 import org.apache.jackrabbit.oak.plugins.blob.datastore.SharedDataStoreUtils;
+import 
org.apache.jackrabbit.oak.plugins.blob.datastore.directaccess.DataRecordAccessProvider;
+import 
org.apache.jackrabbit.oak.plugins.blob.datastore.directaccess.DataRecordDownloadOptions;
+import 
org.apache.jackrabbit.oak.plugins.blob.datastore.directaccess.DataRecordUpload;
+import 
org.apache.jackrabbit.oak.plugins.blob.datastore.directaccess.DataRecordUploadException;
 import org.apache.jackrabbit.oak.plugins.memory.ArrayBasedBlob;
 import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
-import org.apache.jackrabbit.oak.spi.blob.BlobOptions;
 import org.apache.jackrabbit.oak.spi.blob.BlobStore;
 import org.apache.jackrabbit.oak.spi.blob.GarbageCollectableBlobStore;
 import org.apache.jackrabbit.oak.spi.cluster.ClusterRepositoryInfo;
@@ -128,7 +144,8 @@ public class BlobGCTest {
             }
         };
 
-        TimeLapsedBlobStore blobStore = new TimeLapsedBlobStore();
+        TimeLapsedDataStore dataStore = new TimeLapsedDataStore();
+        DataStoreBlobStore blobStore = new DataStoreBlobStore(dataStore);
         MemoryBlobStoreNodeStore nodeStore = new 
MemoryBlobStoreNodeStore(blobStore);
         cluster = new Cluster(folder.newFolder(), blobStore, nodeStore, 0);
         closer.register(cluster);
@@ -151,6 +168,7 @@ public class BlobGCTest {
         protected final BlobStoreState blobStoreState;
         private final File root;
         String repoId;
+        protected final TimeLapsedDataStore dataStore;
         protected final GarbageCollectableBlobStore blobStore;
         protected final NodeStore nodeStore;
         private MarkSweepGarbageCollector collector;
@@ -163,6 +181,7 @@ public class BlobGCTest {
         public Cluster(File root, GarbageCollectableBlobStore blobStore, 
NodeStore nodeStore, int seed) throws Exception {
             this.root = root;
             this.nodeStore = nodeStore;
+            this.dataStore = (TimeLapsedDataStore) ((DataStoreBlobStore) 
blobStore).getDataStore();
             this.blobStore = blobStore;
             if (SharedDataStoreUtils.isShared(blobStore)) {
                 repoId = ClusterRepositoryInfo.getOrCreateId(nodeStore);
@@ -272,6 +291,30 @@ public class BlobGCTest {
     }
 
     @Test
+    public void gcWithNoDeleteDirectBinary() throws Exception {
+        log.info("Starting gcWithNoDeleteDirectBinary()");
+
+        setupDirectBinary(1, 0);
+        Set<String> existingAfterGC = executeGarbageCollection(cluster, 
cluster.getCollector(0), false);
+        
assertTrue(Sets.symmetricDifference(cluster.blobStoreState.blobsPresent, 
existingAfterGC).isEmpty());
+        assertStats(cluster.statsProvider, 1, 0,
+            cluster.blobStoreState.blobsAdded.size() - 
cluster.blobStoreState.blobsPresent.size(),
+            cluster.blobStoreState.blobsAdded.size() - 
cluster.blobStoreState.blobsPresent.size(), NAME);
+    }
+
+    @Test
+    public void gcWithDeleteDirectBinary() throws Exception {
+        log.info("Starting gcWithNoDeleteDirectBinary()");
+
+        setupDirectBinary(5, 2);
+        Set<String> existingAfterGC = executeGarbageCollection(cluster, 
cluster.getCollector(0), false);
+        
assertTrue(Sets.symmetricDifference(cluster.blobStoreState.blobsPresent, 
existingAfterGC).isEmpty());
+        assertStats(cluster.statsProvider, 1, 0,
+            cluster.blobStoreState.blobsAdded.size() - 
cluster.blobStoreState.blobsPresent.size(),
+            cluster.blobStoreState.blobsAdded.size() - 
cluster.blobStoreState.blobsPresent.size(), NAME);
+    }
+
+    @Test
     public void noGc() throws Exception {
         log.info("Starting noGc()");
 
@@ -453,6 +496,31 @@ public class BlobGCTest {
         return state;
     }
 
+    protected void setupDirectBinary(int numCreate, int numDelete) throws 
CommitFailedException {
+        for (int i = 0; i < numCreate; i++) {
+            BlobUpload blobUpload = ((BlobAccessProvider) 
cluster.blobStore).initiateBlobUpload(100, 1);
+            Blob blob = ((BlobAccessProvider) 
cluster.blobStore).completeBlobUpload(blobUpload.getUploadToken());
+
+            cluster.blobStoreState.blobsAdded.add(blob.getContentIdentity());
+            cluster.blobStoreState.blobsPresent.add(blob.getContentIdentity());
+            NodeBuilder builder = cluster.nodeStore.getRoot().builder();
+            builder.child("dbu" + i).setProperty("x", blob);
+            cluster.nodeStore.merge(builder, EmptyHook.INSTANCE, 
CommitInfo.EMPTY);
+            PropertyState property = 
cluster.nodeStore.getRoot().getChildNode("dbu" + i).getProperty("x");
+            Blob blobReturned = property.getValue(Type.BINARY);
+            ((MemoryBlobStoreNodeStore) 
cluster.nodeStore).getReferencedBlobs().add(blobReturned.getContentIdentity());
+        }
+
+        for (int i = 0; i < Math.min(numCreate, numDelete); i++) {
+            PropertyState property = 
cluster.nodeStore.getRoot().getChildNode("dbu" + i).getProperty("x");
+            String blobId = 
property.getValue(Type.BINARY).getContentIdentity();
+
+            delete("dbu" + i, cluster.nodeStore);
+            ((MemoryBlobStoreNodeStore) 
cluster.nodeStore).getReferencedBlobs().remove(blobId);
+            cluster.blobStoreState.blobsPresent.remove(blobId);
+        }
+    }
+
     protected Set<String> createBlobs(GarbageCollectableBlobStore blobStore, 
int count, int size) throws Exception {
         HashSet<String> blobSet = new HashSet<String>();
         for  (int i = 0; i < count; i++) {
@@ -503,6 +571,10 @@ public class BlobGCTest {
             this.referencedBlobs = referencedBlobs;
         }
 
+        public Set<String> getReferencedBlobs() {
+            return this.referencedBlobs;
+        }
+
         @Override
         public ArrayBasedBlob createBlob(InputStream in) {
             try {
@@ -562,98 +634,59 @@ public class BlobGCTest {
     /**
      * Test in memory DS to store the contents with an increasing time
      */
-    class TimeLapsedBlobStore implements GarbageCollectableBlobStore, 
SharedDataStore {
+    class TimeLapsedDataStore implements DataStore, MultiDataStoreAware, 
SharedDataStore, DataRecordAccessProvider {
+        public static final int MIN_RECORD_LENGTH = 50;
+
         private final long startTime;
         Map<String, DataRecord> store;
         Map<String, DataRecord> metadata;
+        Map<String, String> uploadTokens;
 
-        public TimeLapsedBlobStore() {
-            this(System.currentTimeMillis());
-        }
-
-        public TimeLapsedBlobStore(long startTime) {
+        public TimeLapsedDataStore() {
             this.startTime = clock.getTime();
             store = Maps.newHashMap();
             metadata = Maps.newHashMap();
+            uploadTokens = Maps.newHashMap();
         }
 
-        @Override public Iterator<String> getAllChunkIds(long 
maxLastModifiedTime) throws Exception {
-            return store.keySet().iterator();
-        }
-
-        @Override public boolean deleteChunks(List<String> chunkIds, long 
maxLastModifiedTime) throws Exception {
-            return (chunkIds.size() == countDeleteChunks(chunkIds, 
maxLastModifiedTime));
-        }
-
-        @Override public long countDeleteChunks(List<String> chunkIds, long 
maxLastModifiedTime) throws Exception {
-            int count = 0;
-
-            for(String id : chunkIds) {
-                log.info("maxLastModifiedTime {}", maxLastModifiedTime);
-                log.info("store.get(id).getLastModified() {}", 
store.get(id).getLastModified());
-                if (maxLastModifiedTime <= 0 || 
store.get(id).getLastModified() < maxLastModifiedTime) {
-                    store.remove(id);
-                    count++;
-                }
+        @Override public DataRecord getRecordIfStored(DataIdentifier 
identifier) throws DataStoreException {
+            if (store.containsKey(identifier.toString())) {
+                return getRecord(identifier);
             }
-            return count;
+            return null;
         }
 
-        @Override public Iterator<String> resolveChunks(String blobId) throws 
IOException {
-            return Iterators.singletonIterator(blobId);
+        @Override public DataRecord getRecord(DataIdentifier identifier) 
throws DataStoreException {
+            return store.get(identifier.toString());
         }
 
-        @Override public String writeBlob(InputStream in) throws IOException {
-            return writeBlob(in, new BlobOptions());
+        @Override public DataRecord getRecordFromReference(String reference) 
throws DataStoreException {
+            return getRecord(new DataIdentifier(reference));
         }
 
-        @Override public String writeBlob(InputStream in, BlobOptions options) 
throws IOException {
+        @Override public DataRecord addRecord(InputStream stream) throws 
DataStoreException {
             try {
-                byte[] data = IOUtils.toByteArray(in);
+                byte[] data = IOUtils.toByteArray(stream);
                 String id = getIdForInputStream(new 
ByteArrayInputStream(data));
-                id += "#" + data.length;
                 TestRecord rec = new TestRecord(id, data, clock.getTime());
                 store.put(id, rec);
                 log.info("Blob created {} with timestamp {}", rec.id, 
rec.lastModified);
-                return id;
+                return rec;
             } catch (Exception e) {
-                throw new IOException(e);
-            }
-        }
-
-        private String getIdForInputStream(final InputStream in)
-            throws Exception {
-            MessageDigest digest = MessageDigest.getInstance("SHA-256");
-            OutputStream output = new DigestOutputStream(new 
NullOutputStream(), digest);
-            try {
-                IOUtils.copyLarge(in, output);
-            } finally {
-                IOUtils.closeQuietly(output);
-                IOUtils.closeQuietly(in);
+                throw new DataStoreException(e);
             }
-            return encodeHexString(digest.digest());
-        }
 
-        @Override public long getBlobLength(String blobId) throws IOException {
-            return ((TestRecord) store.get(blobId)).data.length;
         }
 
-        @Override public InputStream getInputStream(String blobId) throws 
IOException {
-            try {
-                return store.get(blobId).getStream();
-            } catch (DataStoreException e) {
-                e.printStackTrace();
-            }
-            return null;
+        @Override public Iterator<DataIdentifier> getAllIdentifiers() throws 
DataStoreException {
+            return  Iterators.transform(store.keySet().iterator(), input -> 
new DataIdentifier(input));
         }
 
-        @Nullable @Override public String getBlobId(@NotNull String reference) 
{
-            return reference;
+        @Override public void deleteRecord(DataIdentifier identifier) throws 
DataStoreException {
+            store.remove(identifier.toString());
         }
 
-        @Nullable @Override public String getReference(@NotNull String blobId) 
{
-            return blobId;
-        }
+        /***************************************** SharedDataStore 
***************************************/
 
         @Override public void addMetadataRecord(InputStream stream, String 
name) throws DataStoreException {
             try {
@@ -729,8 +762,55 @@ public class BlobGCTest {
             return store.get(id.toString());
         }
 
-        @Override public Type getType() {
-            return Type.SHARED;
+        @Override public SharedDataStore.Type getType() {
+            return SharedDataStore.Type.SHARED;
+        }
+
+        /**************************** DataRecordAccessProvider 
*************************/
+
+        @Override public @Nullable URI getDownloadURI(@NotNull DataIdentifier 
identifier,
+            @NotNull DataRecordDownloadOptions downloadOptions) {
+            return null;
+        }
+
+        @Override
+        public @Nullable DataRecordUpload initiateDataRecordUpload(long 
maxUploadSizeInBytes, int maxNumberOfURIs)
+            throws IllegalArgumentException, DataRecordUploadException {
+            String upToken = UUID.randomUUID().toString();
+            Random rand = new Random();
+            InputStream stream = randomStream(rand.nextInt(1000), 100);
+            byte[] data = new byte[0];
+            try {
+                data = IOUtils.toByteArray(stream);
+            } catch (IOException e) {
+                throw new DataRecordUploadException(e);
+            }
+            TestRecord rec = new TestRecord(upToken, data, clock.getTime());
+            store.put(upToken, rec);
+
+            DataRecordUpload uploadRec = new DataRecordUpload() {
+                @Override public @NotNull String getUploadToken() {
+                    return upToken;
+                }
+
+                @Override public long getMinPartSize() {
+                    return maxUploadSizeInBytes;
+                }
+
+                @Override public long getMaxPartSize() {
+                    return maxUploadSizeInBytes;
+                }
+
+                @Override public @NotNull Collection<URI> getUploadURIs() {
+                    return Collections.EMPTY_LIST;
+                }
+            };
+            return uploadRec;
+        }
+
+        @Override public @NotNull DataRecord completeDataRecordUpload(@NotNull 
String uploadToken)
+            throws IllegalArgumentException, DataRecordUploadException, 
DataStoreException {
+            return store.get(uploadToken);
         }
 
         class TestRecord implements DataRecord {
@@ -765,33 +845,38 @@ public class BlobGCTest {
             }
         }
 
-        /** No-op **/
-        @Override public int readBlob(String blobId, long pos, byte[] buff, 
int off, int length) throws IOException {
-            throw new UnsupportedOperationException("readBlob not supported");
-        }
-
-        @Override public void setBlockSize(int x) {
+        private String getIdForInputStream(final InputStream in)
+            throws Exception {
+            MessageDigest digest = MessageDigest.getInstance("SHA-256");
+            OutputStream output = new DigestOutputStream(new 
NullOutputStream(), digest);
+            try {
+                IOUtils.copyLarge(in, output);
+            } finally {
+                IOUtils.closeQuietly(output);
+                IOUtils.closeQuietly(in);
+            }
+            return encodeHexString(digest.digest());
         }
 
-        @Override public String writeBlob(String tempFileName) throws 
IOException {
-            throw new UnsupportedOperationException("getBlockSizeMin not 
supported");
+        /*************************************** No Op ***********************/
+        @Override public void init(String homeDir) throws RepositoryException {
         }
 
-        @Override public int sweep() throws IOException {
-            throw new UnsupportedOperationException("sweep not supported");
+        @Override public void updateModifiedDateOnAccess(long before) {
         }
 
-        @Override public void startMark() throws IOException {
+        @Override public int deleteAllOlderThan(long min) throws 
DataStoreException {
+            return 0;
         }
 
-        @Override public void clearInUse() {
+        @Override public int getMinRecordLength() {
+            return MIN_RECORD_LENGTH;
         }
 
-        @Override public void clearCache() {
+        @Override public void close() throws DataStoreException {
         }
 
-        @Override public long getBlockSizeMin() {
-            throw new UnsupportedOperationException("getBlockSizeMin not 
supported");
+        @Override public void clearInUse() {
         }
     }
 }


Reply via email to