You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by ma...@apache.org on 2019/08/28 19:28:12 UTC

svn commit: r1866044 - in /jackrabbit/oak/trunk: oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/ oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/ oak-blob-cloud/src/ma...

Author: mattryan
Date: Wed Aug 28 19:28:11 2019
New Revision: 1866044

URL: http://svn.apache.org/viewvc?rev=1866044&view=rev
Log:
OAK-8552: Add data store config to skip exists check on download URI generation

This change adds a new optional configuration parameter for AzureDataStore and S3DataStore.  The new parameter, named "presignedHttpDownloadURIVerifyExists", has a default value of true which means the createHttpDownloadURI() method will ensure the requested blob exists in blob storage before creating the signed download URI.  If this parameter is set to false, the exists check will be skipped, resulting in higher signed download URI generation performance but potentially returning a URI to a nonexistent blob in some cases (see OAK-7998).

Modified:
    jackrabbit/oak/trunk/oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureBlobStoreBackend.java
    jackrabbit/oak/trunk/oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureConstants.java
    jackrabbit/oak/trunk/oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureDataRecordAccessProviderTest.java
    jackrabbit/oak/trunk/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/s3/S3Backend.java
    jackrabbit/oak/trunk/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/s3/S3Constants.java
    jackrabbit/oak/trunk/oak-blob-cloud/src/test/java/org/apache/jackrabbit/oak/blob/cloud/s3/S3DataRecordAccessProviderTest.java
    jackrabbit/oak/trunk/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/AbstractDataRecordAccessProviderTest.java
    jackrabbit/oak/trunk/oak-doc/src/site/markdown/features/direct-binary-access.md

Modified: jackrabbit/oak/trunk/oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureBlobStoreBackend.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureBlobStoreBackend.java?rev=1866044&r1=1866043&r2=1866044&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureBlobStoreBackend.java (original)
+++ jackrabbit/oak/trunk/oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureBlobStoreBackend.java Wed Aug 28 19:28:11 2019
@@ -114,6 +114,7 @@ public class AzureBlobStoreBackend exten
     private int httpDownloadURIExpirySeconds = 0; // disabled by default
     private int httpUploadURIExpirySeconds = 0; // disabled by default
     private boolean createBlobContainer = true;
+    private boolean presignedDownloadURIVerifyExists = true;
 
     private Cache<DataIdentifier, URI> httpDownloadURICache;
 
@@ -163,6 +164,8 @@ public class AzureBlobStoreBackend exten
                 if (properties.getProperty(AzureConstants.AZURE_BLOB_REQUEST_TIMEOUT) != null) {
                     requestTimeout = PropertiesUtil.toInteger(properties.getProperty(AzureConstants.AZURE_BLOB_REQUEST_TIMEOUT), RetryPolicy.DEFAULT_CLIENT_RETRY_COUNT);
                 }
+                presignedDownloadURIVerifyExists =
+                        PropertiesUtil.toBoolean(properties.get(AzureConstants.PRESIGNED_HTTP_DOWNLOAD_URI_VERIFY_EXISTS), true);
 
                 CloudBlobContainer azureContainer = getAzureContainer();
 
@@ -767,23 +770,24 @@ public class AzureBlobStoreBackend exten
         
         if (httpDownloadURIExpirySeconds > 0) {
 
-            // Check if this identifier exists.  If not, we want to return null
-            // even if the identifier is in the download URI cache.
-            try {
-                if (! exists(identifier)) {
-                    LOG.warn("Cannot create download URI for nonexistent blob {}; returning null", getKeyName(identifier));
-                    return null;
-                }
-            }
-            catch (DataStoreException e) {
-                LOG.warn("Cannot create download URI for blob {} (caught DataStoreException); returning null", getKeyName(identifier), e);
-                return null;
-            }
-
             if (null != httpDownloadURICache) {
                 uri = httpDownloadURICache.getIfPresent(identifier);
             }
             if (null == uri) {
+                if (presignedDownloadURIVerifyExists) {
+                    // Check if this identifier exists.  If not, we want to return null
+                    // even if the identifier is in the download URI cache.
+                    try {
+                        if (!exists(identifier)) {
+                            LOG.warn("Cannot create download URI for nonexistent blob {}; returning null", getKeyName(identifier));
+                            return null;
+                        }
+                    } catch (DataStoreException e) {
+                        LOG.warn("Cannot create download URI for blob {} (caught DataStoreException); returning null", getKeyName(identifier), e);
+                        return null;
+                    }
+                }
+
                 String key = getKeyName(identifier);
                 SharedAccessBlobHeaders headers = new SharedAccessBlobHeaders();
                 headers.setCacheControl(String.format("private, max-age=%d, immutable", httpDownloadURIExpirySeconds));

Modified: jackrabbit/oak/trunk/oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureConstants.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureConstants.java?rev=1866044&r1=1866043&r2=1866044&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureConstants.java (original)
+++ jackrabbit/oak/trunk/oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureConstants.java Wed Aug 28 19:28:11 2019
@@ -95,5 +95,17 @@ public final class AzureConstants {
      */
     public static final String PRESIGNED_HTTP_DOWNLOAD_URI_CACHE_MAX_SIZE = "presignedHttpDownloadURICacheMaxSize";
 
+    /**
+     * Boolean flag to allow disabling of verification check on download URI
+     * generation.  Default is true (the existence check is performed).
+     *
+     * Some installations may prefer to disable async uploads, in which case it
+     * is possible to disable the existence check and thus greatly speed up the
+     * generation of presigned download URIs.  See OAK-7998 which describes why
+     * the existence check was added to understand how async uploading relates
+     * to this feature.
+     */
+    public static final String PRESIGNED_HTTP_DOWNLOAD_URI_VERIFY_EXISTS = "presignedHttpDownloadURIVerifyExists";
+
     private AzureConstants() { }
 }

Modified: jackrabbit/oak/trunk/oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureDataRecordAccessProviderTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureDataRecordAccessProviderTest.java?rev=1866044&r1=1866043&r2=1866044&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureDataRecordAccessProviderTest.java (original)
+++ jackrabbit/oak/trunk/oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureDataRecordAccessProviderTest.java Wed Aug 28 19:28:11 2019
@@ -18,6 +18,9 @@
  */
 package org.apache.jackrabbit.oak.blob.cloud.azure.blobstorage;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -38,14 +41,12 @@ import org.apache.jackrabbit.oak.plugins
 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.spi.blob.BlobOptions;
+import org.jetbrains.annotations.NotNull;
 import org.junit.BeforeClass;
 import org.junit.ClassRule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assume.assumeTrue;
-
 public class AzureDataRecordAccessProviderTest extends AbstractDataRecordAccessProviderTest {
     @ClassRule
     public static TemporaryFolder homeDir = new TemporaryFolder(new File("target"));
@@ -55,20 +56,40 @@ public class AzureDataRecordAccessProvid
     @BeforeClass
     public static void setupDataStore() throws Exception {
         assumeTrue(AzureDataStoreUtils.isAzureConfigured());
-        Properties props = AzureDataStoreUtils.getAzureConfig();
-        props.setProperty("cacheSize", "0");
         dataStore = (AzureDataStore) AzureDataStoreUtils
-            .getAzureDataStore(props, homeDir.newFolder().getAbsolutePath());
+            .getAzureDataStore(getProperties(), homeDir.newFolder().getAbsolutePath());
         dataStore.setDirectDownloadURIExpirySeconds(expirySeconds);
         dataStore.setDirectUploadURIExpirySeconds(expirySeconds);
     }
 
+    private static Properties getProperties() {
+        Properties props = AzureDataStoreUtils.getAzureConfig();
+        props.setProperty("cacheSize", "0");
+        return props;
+    }
+
+    private static AzureDataStore createDataStore(@NotNull Properties properties) throws Exception {
+        AzureDataStore ds = (AzureDataStore) AzureDataStoreUtils
+                .getAzureDataStore(properties, homeDir.newFolder().getAbsolutePath());
+        ds.setDirectDownloadURIExpirySeconds(expirySeconds);
+        ds.setDirectUploadURIExpirySeconds(expirySeconds);
+        return ds;
+    }
+
     @Override
     protected ConfigurableDataRecordAccessProvider getDataStore() {
         return dataStore;
     }
 
     @Override
+    protected ConfigurableDataRecordAccessProvider getDataStore(@NotNull Properties overrideProperties) throws Exception {
+        Properties mergedProperties = new Properties();
+        mergedProperties.putAll(getProperties());
+        mergedProperties.putAll(overrideProperties);
+        return createDataStore(mergedProperties);
+    }
+
+    @Override
     protected DataRecord doGetRecord(DataStore ds, DataIdentifier identifier) throws DataStoreException {
         return ds.getRecord(identifier);
     }

Modified: jackrabbit/oak/trunk/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/s3/S3Backend.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/s3/S3Backend.java?rev=1866044&r1=1866043&r2=1866044&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/s3/S3Backend.java (original)
+++ jackrabbit/oak/trunk/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/s3/S3Backend.java Wed Aug 28 19:28:11 2019
@@ -86,6 +86,7 @@ import org.apache.jackrabbit.core.data.D
 import org.apache.jackrabbit.core.data.DataRecord;
 import org.apache.jackrabbit.core.data.DataStoreException;
 import org.apache.jackrabbit.core.data.util.NamedThreadFactory;
+import org.apache.jackrabbit.oak.commons.PropertiesUtil;
 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;
@@ -147,6 +148,8 @@ public class S3Backend extends AbstractS
     private int httpUploadURIExpirySeconds = 0;
     private int httpDownloadURIExpirySeconds = 0;
 
+    private boolean presignedDownloadURIVerifyExists = true;
+
     public void init() throws DataStoreException {
         ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
 
@@ -238,6 +241,9 @@ public class S3Backend extends AbstractS
             String enablePresignedAccelerationStr = properties.getProperty(S3Constants.PRESIGNED_URI_ENABLE_ACCELERATION);
             setBinaryTransferAccelerationEnabled(enablePresignedAccelerationStr != null && "true".equals(enablePresignedAccelerationStr));
 
+            presignedDownloadURIVerifyExists =
+                    PropertiesUtil.toBoolean(properties.get(S3Constants.PRESIGNED_HTTP_DOWNLOAD_URI_VERIFY_EXISTS), true);
+
             LOG.debug("S3 Backend initialized in [{}] ms",
                 +(System.currentTimeMillis() - startTime.getTime()));
         } catch (Exception e) {
@@ -744,23 +750,24 @@ public class S3Backend extends AbstractS
         if (null == identifier) throw new NullPointerException("identifier");
         if (null == downloadOptions) throw new NullPointerException("downloadOptions");
 
-        try {
-            if (! exists(identifier)) {
-                LOG.warn("Cannot create download URI for nonexistent blob {}; returning null", getKeyName(identifier));
-                return null;
-            }
-        }
-        catch (DataStoreException e) {
-            LOG.warn("Cannot create download URI for blob {} (caught DataStoreException); returning null", getKeyName(identifier), e);
-            return null;
-        }
-
         URI uri = null;
         // if cache is enabled, check the cache
         if (httpDownloadURICache != null) {
             uri = httpDownloadURICache.getIfPresent(identifier);
         }
-        if (uri == null) {
+        if (null == uri) {
+            if (presignedDownloadURIVerifyExists) {
+                try {
+                    if (!exists(identifier)) {
+                        LOG.warn("Cannot create download URI for nonexistent blob {}; returning null", getKeyName(identifier));
+                        return null;
+                    }
+                } catch (DataStoreException e) {
+                    LOG.warn("Cannot create download URI for blob {} (caught DataStoreException); returning null", getKeyName(identifier), e);
+                    return null;
+                }
+            }
+
             Map<String, String> requestParams = Maps.newHashMap();
             requestParams.put("response-cache-control",
                     String.format("private, max-age=%d, immutable",

Modified: jackrabbit/oak/trunk/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/s3/S3Constants.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/s3/S3Constants.java?rev=1866044&r1=1866043&r2=1866044&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/s3/S3Constants.java (original)
+++ jackrabbit/oak/trunk/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/s3/S3Constants.java Wed Aug 28 19:28:11 2019
@@ -126,6 +126,18 @@ public final class S3Constants {
     public static final String PRESIGNED_URI_ENABLE_ACCELERATION = "presignedURIEnableTransferAcceleration";
 
     /**
+     * Boolean flag to allow disabling of verification check on download URI
+     * generation.  Default is true (the existence check is performed).
+     *
+     * Some installations may prefer to disable async uploads, in which case it
+     * is possible to disable the existence check and thus greatly speed up the
+     * generation of presigned download URIs.  See OAK-7998 which describes why
+     * the existence check was added to understand how async uploading relates
+     * to this feature.
+     */
+    public static final String PRESIGNED_HTTP_DOWNLOAD_URI_VERIFY_EXISTS = "presignedHttpDownloadURIVerifyExists";
+
+    /**
      * private constructor so that class cannot initialized from outside.
      */
     private S3Constants() {

Modified: jackrabbit/oak/trunk/oak-blob-cloud/src/test/java/org/apache/jackrabbit/oak/blob/cloud/s3/S3DataRecordAccessProviderTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-blob-cloud/src/test/java/org/apache/jackrabbit/oak/blob/cloud/s3/S3DataRecordAccessProviderTest.java?rev=1866044&r1=1866043&r2=1866044&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-blob-cloud/src/test/java/org/apache/jackrabbit/oak/blob/cloud/s3/S3DataRecordAccessProviderTest.java (original)
+++ jackrabbit/oak/trunk/oak-blob-cloud/src/test/java/org/apache/jackrabbit/oak/blob/cloud/s3/S3DataRecordAccessProviderTest.java Wed Aug 28 19:28:11 2019
@@ -18,11 +18,20 @@
  */
 package org.apache.jackrabbit.oak.blob.cloud.s3;
 
+import static org.apache.jackrabbit.oak.blob.cloud.s3.S3DataStoreUtils.getFixtures;
+import static org.apache.jackrabbit.oak.blob.cloud.s3.S3DataStoreUtils.getS3Config;
+import static org.apache.jackrabbit.oak.blob.cloud.s3.S3DataStoreUtils.getS3DataStore;
+import static org.apache.jackrabbit.oak.blob.cloud.s3.S3DataStoreUtils.isS3Configured;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URI;
 import java.util.Map;
+import java.util.Properties;
 
 import javax.net.ssl.HttpsURLConnection;
 
@@ -35,19 +44,12 @@ import org.apache.jackrabbit.oak.plugins
 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.spi.blob.BlobOptions;
+import org.jetbrains.annotations.NotNull;
 import org.junit.BeforeClass;
 import org.junit.ClassRule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 
-import static org.apache.jackrabbit.oak.blob.cloud.s3.S3DataStoreUtils.getFixtures;
-import static org.apache.jackrabbit.oak.blob.cloud.s3.S3DataStoreUtils.getS3Config;
-import static org.apache.jackrabbit.oak.blob.cloud.s3.S3DataStoreUtils.getS3DataStore;
-import static org.apache.jackrabbit.oak.blob.cloud.s3.S3DataStoreUtils.isS3Configured;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
-
 public class S3DataRecordAccessProviderTest extends AbstractDataRecordAccessProviderTest {
     @ClassRule
     public static TemporaryFolder homeDir = new TemporaryFolder(new File("target"));
@@ -57,11 +59,17 @@ public class S3DataRecordAccessProviderT
     @BeforeClass
     public static void setupDataStore() throws Exception {
         assumeTrue(isS3Configured());
-        dataStore = (S3DataStore) getS3DataStore(getFixtures().get(0), getS3Config(),
-            homeDir.newFolder().getAbsolutePath());
+        dataStore = createDataStore(getS3Config());
+    }
 
-        dataStore.setDirectDownloadURIExpirySeconds(expirySeconds);
-        dataStore.setDirectUploadURIExpirySeconds(expirySeconds);
+    private static S3DataStore createDataStore(@NotNull final Properties properties) throws Exception {
+        S3DataStore ds = (S3DataStore) getS3DataStore(getFixtures().get(0), properties,
+                homeDir.newFolder().getAbsolutePath());
+
+        ds.setDirectDownloadURIExpirySeconds(expirySeconds);
+        ds.setDirectUploadURIExpirySeconds(expirySeconds);
+
+        return ds;
     }
 
     @Override
@@ -70,6 +78,14 @@ public class S3DataRecordAccessProviderT
     }
 
     @Override
+    protected ConfigurableDataRecordAccessProvider getDataStore(@NotNull Properties overrideProperties) throws Exception {
+        Properties mergedProperties = new Properties();
+        mergedProperties.putAll(getS3Config());
+        mergedProperties.putAll(overrideProperties);
+        return createDataStore(mergedProperties);
+    }
+
+    @Override
     protected DataRecord doGetRecord(DataStore ds, DataIdentifier identifier) throws DataStoreException {
         return ds.getRecord(identifier);
     }

Modified: jackrabbit/oak/trunk/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/AbstractDataRecordAccessProviderTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/AbstractDataRecordAccessProviderTest.java?rev=1866044&r1=1866043&r2=1866044&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/AbstractDataRecordAccessProviderTest.java (original)
+++ jackrabbit/oak/trunk/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/AbstractDataRecordAccessProviderTest.java Wed Aug 28 19:28:11 2019
@@ -36,6 +36,7 @@ import java.net.URLDecoder;
 import java.nio.charset.Charset;
 import java.util.Arrays;
 import java.util.Map;
+import java.util.Properties;
 
 import javax.net.ssl.HttpsURLConnection;
 
@@ -49,6 +50,7 @@ import org.apache.jackrabbit.core.data.D
 import org.apache.jackrabbit.core.data.DataStoreException;
 import org.apache.jackrabbit.oak.api.blob.BlobDownloadOptions;
 import org.apache.jackrabbit.util.Base64;
+import org.jetbrains.annotations.NotNull;
 import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -57,6 +59,7 @@ public abstract class AbstractDataRecord
     protected static final Logger LOG = LoggerFactory.getLogger(AbstractDataRecordAccessProviderTest.class);
 
     protected abstract ConfigurableDataRecordAccessProvider getDataStore();
+    protected abstract ConfigurableDataRecordAccessProvider getDataStore(@NotNull Properties overrideProperties) throws Exception;
     protected abstract long getProviderMinPartSize();
     protected abstract long getProviderMaxPartSize();
     protected abstract long getProviderMaxSinglePutSize();
@@ -101,7 +104,23 @@ public abstract class AbstractDataRecord
     }
 
     @Test
-    public void testGetDownloadURIRequiresValidIdentifier() {
+    public void testGetDownloadURIWithExistsDisabled() throws Exception {
+        DataIdentifier id = new DataIdentifier("identifier");
+        Properties overrideProperties = new Properties();
+        overrideProperties.put("presignedHttpDownloadURIVerifyExists", "false");
+        ConfigurableDataRecordAccessProvider ds = getDataStore(overrideProperties);
+        assertNotNull(ds.getDownloadURI(id, DataRecordDownloadOptions.DEFAULT));
+    }
+
+    @Test
+    public void testGetDownloadURIRequiresValidIdentifierByDefault() {
+        DataIdentifier id = new DataIdentifier("identifier");
+        ConfigurableDataRecordAccessProvider ds = getDataStore();
+        assertNull(ds.getDownloadURI(id, DataRecordDownloadOptions.DEFAULT));
+    }
+
+    @Test
+    public void testGetDownloadURIRequiresNonNullIdentifier() {
         try {
             getDataStore().getDownloadURI(null, DataRecordDownloadOptions.DEFAULT);
             fail();

Modified: jackrabbit/oak/trunk/oak-doc/src/site/markdown/features/direct-binary-access.md
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/markdown/features/direct-binary-access.md?rev=1866044&r1=1866043&r2=1866044&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-doc/src/site/markdown/features/direct-binary-access.md (original)
+++ jackrabbit/oak/trunk/oak-doc/src/site/markdown/features/direct-binary-access.md Wed Aug 28 19:28:11 2019
@@ -51,6 +51,7 @@ The feature has to be explicitly enabled
 | `presignedHttpUploadURIExpirySeconds`   | Integer  | 0 (disabled)     | Time limit for upload URLs, in seconds. Choose a value long enough for clients to upload larger binaries and possibly retry, but not unlimited to ensure access control. Setting to 0 disables the direct upload feature. |
 | `presignedHttpDownloadURIExpirySeconds` | Integer  | 0 (disabled)     | Time limit for download URLs, in seconds. Choose a value long enough for clients to download larger binaries and possibly retry, but not unlimited to ensure access control. Setting to 0 disables the direct download feature. |
 | `presignedHttpDownloadURICacheMaxSize`  | Integer  | 0 (disabled)     | **Experimental.** Cache size for reusing download URLs. Expired URLs will be cached for half their expiry time, hence if this feature is enabled, clients might get URLs that expire after half of `presignedHttpDownloadURIExpirySeconds`. Setting to 0 disables the cache. |
+| `presignedHttpDownloadURIVerifyExists` | Boolean | true | Flag to determine whether to check that a binary exists in blob storage before issuing a presigned download URI.  The default is to verify existence first, providing assurance that the signed URI references an existing binary.  If this flag is set to false, the existence check is skipped.  This makes generating the signed URI anywhere from 100 to 1000 times faster since no network API calls are made, but means it is possible in some cases to return a signed URI that doesn't reference an existing blob.  See [OAK-7998](https://issues.apache.org/jira/browse/OAK-7998) and [OAK-8552](https://issues.apache.org/jira/browse/OAK-8552) for more details. |
 | S3:&nbsp;`presignedURIEnableTransferAcceleration` <br/>Azure:&nbsp;n/a                                     | Boolean  | false (disabled) | **Experimental.** Enables [S3 Transfer Acceleration](https://docs.aws.amazon.com/AmazonS3/latest/dev/transfer-acceleration.html) for both upload and download URLs. Transfer acceleration must be enabled on the S3 bucket before Oak starts. |
 
 ## API Javadoc