Skip to content
This repository was archived by the owner on Jul 19, 2024. It is now read-only.

Commit 03d71b8

Browse files
committed
Skip Etag Locking Request Option Added
Can now specify the BlobRequestOption skipEtagLocking, which tells blob downloads to ignore changes in etags as they progress.
1 parent 0375367 commit 03d71b8

File tree

5 files changed

+89
-8
lines changed

5 files changed

+89
-8
lines changed

microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/BlobTestHelper.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,4 +466,17 @@ public static void disableSoftDelete() throws StorageException, URISyntaxExcepti
466466
bClient.uploadServiceProperties(serviceProperties);
467467
Thread.sleep(30000);
468468
}
469+
470+
public static HashMap<String, String> generateSampleMetadata(final int entries) {
471+
if (entries < 0) {
472+
throw new IllegalArgumentException();
473+
}
474+
475+
HashMap<String, String> result = new HashMap<>(entries);
476+
for (int i = 0; i < entries; i++) {
477+
result.put("metadataKey_" + i, "metadataValue_" + i);
478+
}
479+
480+
return result;
481+
}
469482
}

microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlockBlobTests.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2699,4 +2699,36 @@ public void testGetAccountInfo() throws StorageException, URISyntaxException, In
26992699
assertNotNull(accountInformation.getAccountKind());
27002700
assertNotNull(accountInformation.getSkuName());
27012701
}
2702+
2703+
@Test
2704+
public void testSkipEtagCheck() throws StorageException, IOException, URISyntaxException {
2705+
final int blobSize = 2 * Constants.DEFAULT_MINIMUM_READ_SIZE_IN_BYTES; // so BlobInputStream doesn't read entire blob at once.
2706+
2707+
// setup
2708+
CloudBlockBlob blob = (CloudBlockBlob)BlobTestHelper.uploadNewBlob(
2709+
this.container, BlobType.BLOCK_BLOB, "testSkipEtagCheck", blobSize, null);
2710+
2711+
BlobRequestOptions options = new BlobRequestOptions();
2712+
options.setSkipEtagLocking(true); // Only request option to skip for these downloads. The rest is automatic.
2713+
BlobInputStream stream = blob.openInputStream(null, options, null);
2714+
2715+
// test
2716+
byte[] buffer = new byte[Constants.DEFAULT_MINIMUM_READ_SIZE_IN_BYTES];
2717+
2718+
assertEquals(Constants.DEFAULT_MINIMUM_READ_SIZE_IN_BYTES, stream.read(buffer)); // read 1st half of blob
2719+
blob.downloadAttributes();
2720+
String etag1 = blob.getProperties().getEtag();
2721+
2722+
blob.setMetadata(BlobTestHelper.generateSampleMetadata(1));
2723+
blob.uploadMetadata(); // change etag
2724+
2725+
assertEquals(Constants.DEFAULT_MINIMUM_READ_SIZE_IN_BYTES, stream.read(buffer)); // read 2nd half of blob
2726+
blob.downloadAttributes();
2727+
String etag2 = blob.getProperties().getEtag();
2728+
2729+
assertNotEquals(etag1, etag2); // assert etags were actually different if we get here without throwing
2730+
2731+
// cleanup
2732+
stream.close();
2733+
}
27022734
}

microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobInputStream.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,11 @@ protected BlobInputStream(long blobRangeOffset, Long blobRangeLength, final Clou
222222
}
223223
}
224224

225-
this.accessCondition = AccessCondition.generateIfMatchCondition(this.parentBlobRef.getProperties().getEtag());
225+
this.accessCondition = new AccessCondition();
226226
this.accessCondition.setLeaseID(previousLeaseId);
227+
if (!options.getSkipEtagLocking()) {
228+
this.accessCondition.setIfMatch(this.parentBlobRef.getProperties().getEtag());
229+
}
227230

228231
this.streamLength = blobRangeLength == null
229232
? this.parentBlobRef.getProperties().getLength() - this.blobRangeOffset

microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobRequestOptions.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@ public final class BlobRequestOptions extends RequestOptions {
7171
* Default is true.
7272
*/
7373
private boolean validateEncryptionPolicy = true;
74+
75+
/**
76+
* WARNING: etag locking is automatically used in blob downloads to ensure the blob does not change mid-download.
77+
* Skip this validation at your own risk.
78+
* Default is false.
79+
*/
80+
private boolean skipEtagLocking = false;
7481

7582
/**
7683
* Creates an instance of the <code>BlobRequestOptions</code> class.
@@ -97,6 +104,7 @@ public BlobRequestOptions(final BlobRequestOptions other) {
97104
this.setSingleBlobPutThresholdInBytes(other.getSingleBlobPutThresholdInBytes());
98105
this.setEncryptionPolicy(other.getEncryptionPolicy());
99106
this.setValidateEncryptionPolicy(other.getValidateEncryptionPolicy());
107+
this.setSkipEtagLocking(other.getSkipEtagLocking());
100108
}
101109
}
102110

@@ -300,6 +308,18 @@ protected boolean getValidateEncryptionPolicy() {
300308
return this.validateEncryptionPolicy;
301309
}
302310

311+
/**
312+
* WARNING: etag locking is automatically used in blob downloads to ensure the blob does not change mid-download.
313+
* Skip this validation at your own risk.
314+
*
315+
* Gets whether etag locking and validation on blob downloads should be skipped.
316+
*
317+
* @return <code>true</code> if skipping is enabled; otherwise, <code>false</code>.
318+
*/
319+
public boolean getSkipEtagLocking() {
320+
return this.skipEtagLocking;
321+
}
322+
303323
/**
304324
* Sets whether a conditional failure should be absorbed on a retry attempt for the request. This option
305325
* is only used by {@link CloudAppendBlob} in upload and openWrite methods. By default, it is set to
@@ -427,7 +447,20 @@ public void setEncryptionPolicy(BlobEncryptionPolicy encryptionPolicy) {
427447
protected void setValidateEncryptionPolicy(boolean validateEncryptionPolicy) {
428448
this.validateEncryptionPolicy = validateEncryptionPolicy;
429449
}
430-
450+
451+
/**
452+
* WARNING: etag locking is automatically used in blob downloads to ensure the blob does not change mid-download.
453+
* Skip this validation at your own risk.
454+
*
455+
* Sets whether etag locking and validation on blob downloads should be skipped.
456+
*
457+
* @param skipEtagLocking
458+
* Use <code>true</code> to skip etag locking and validation; otherwise, <code>false</code>.
459+
*/
460+
public void setSkipEtagLocking(boolean skipEtagLocking) {
461+
this.skipEtagLocking = skipEtagLocking;
462+
}
463+
431464
/**
432465
* Assert that if validation is on, an encryption policy is not specified.
433466
*/

microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlob.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,7 +1243,7 @@ public final void download(final OutputStream outStream, final AccessCondition a
12431243
options = BlobRequestOptions.populateAndApplyDefaults(options, this.properties.getBlobType(), this.blobServiceClient);
12441244

12451245
ExecutionEngine.executeWithRetry(this.blobServiceClient, this, this.downloadToStreamImpl(
1246-
null /* blobOffset */, null /* length */, outStream, accessCondition, options, opContext), options
1246+
null /* blobOffset */, null /* length */, outStream, accessCondition, options), options
12471247
.getRetryPolicyFactory(), opContext);
12481248
}
12491249

@@ -1307,7 +1307,7 @@ public final void downloadRange(final long offset, final Long length, final Outp
13071307
}
13081308

13091309
ExecutionEngine.executeWithRetry(this.blobServiceClient, this,
1310-
this.downloadToStreamImpl(offset, length, outStream, accessCondition, options, opContext),
1310+
this.downloadToStreamImpl(offset, length, outStream, accessCondition, options),
13111311
options.getRetryPolicyFactory(), opContext);
13121312
}
13131313

@@ -1415,7 +1415,7 @@ public Void preProcessResponse(CloudBlob blob, CloudBlobClient client, Operation
14151415
@DoesServiceRequest
14161416
private final StorageRequest<CloudBlobClient, CloudBlob, Integer> downloadToStreamImpl(Long offset,
14171417
Long length, final OutputStream userStream, final AccessCondition accessCondition,
1418-
final BlobRequestOptions options, OperationContext opContext) {
1418+
final BlobRequestOptions options) {
14191419
options.assertPolicyIfRequired();
14201420

14211421
final Long userSpecifiedLength = length;
@@ -1588,7 +1588,7 @@ public Integer postProcessResponse(HttpURLConnection connection, CloudBlob blob,
15881588

15891589
@Override
15901590
public void recoveryAction(OperationContext context) throws IOException {
1591-
if (this.getETagLockCondition() == null && (!Utility.isNullOrEmpty(this.getLockedETag()))) {
1591+
if (!options.getSkipEtagLocking() && this.getETagLockCondition() == null && (!Utility.isNullOrEmpty(this.getLockedETag()))) {
15921592
AccessCondition etagLockCondition = new AccessCondition();
15931593
etagLockCondition.setIfMatch(this.getLockedETag());
15941594
if (accessCondition != null) {
@@ -1653,7 +1653,7 @@ protected final int downloadRangeInternal(final long blobOffset, final Long leng
16531653

16541654
WrappedByteArrayOutputStream outputStream = new WrappedByteArrayOutputStream(buffer, bufferOffset);
16551655
ExecutionEngine.executeWithRetry(this.blobServiceClient, this,
1656-
this.downloadToStreamImpl(blobOffset, length, outputStream, accessCondition, options, opContext),
1656+
this.downloadToStreamImpl(blobOffset, length, outputStream, accessCondition, options),
16571657
options.getRetryPolicyFactory(), opContext);
16581658
return outputStream.getPosition();
16591659
}
@@ -1791,7 +1791,7 @@ public final int downloadToByteArray(final byte[] buffer, final int bufferOffset
17911791

17921792
WrappedByteArrayOutputStream outputStream = new WrappedByteArrayOutputStream(buffer, bufferOffset);
17931793
ExecutionEngine.executeWithRetry(this.blobServiceClient, this,
1794-
this.downloadToStreamImpl(null, null, outputStream, accessCondition, options, opContext),
1794+
this.downloadToStreamImpl(null, null, outputStream, accessCondition, options),
17951795
options.getRetryPolicyFactory(), opContext);
17961796
return outputStream.getPosition();
17971797
}

0 commit comments

Comments
 (0)