Skip to content

Commit 6ff28f1

Browse files
committed
Fixing and adding additional testcase
1 parent f4ef740 commit 6ff28f1

File tree

4 files changed

+207
-25
lines changed

4 files changed

+207
-25
lines changed

hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/AbfsClient.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ public AbfsRestOperation createPath(final String path,
324324
try {
325325
op = getPathStatus(path, false);
326326
} catch (AbfsRestOperationException ex) {
327-
if (e.getStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) {
327+
if (ex.getStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) {
328328
// Is a parallel access case, as file which was found to be
329329
// present went missing by this request.
330330
throw new ConcurrentWriteOperationDetectedException(
@@ -342,7 +342,7 @@ public AbfsRestOperation createPath(final String path,
342342
op = createPathImpl(path, abfsUriQueryBuilder, true, permission,
343343
umask, eTag);
344344
} catch (AbfsRestOperationException ex) {
345-
if (e.getStatusCode() == HttpURLConnection.HTTP_PRECON_FAILED) {
345+
if (ex.getStatusCode() == HttpURLConnection.HTTP_PRECON_FAILED) {
346346
// Is a parallel access case, as file with eTag was just queried
347347
// and precondition failure can happen only when another file with
348348
// different etag got created.
@@ -361,7 +361,8 @@ public AbfsRestOperation createPath(final String path,
361361
return op;
362362
}
363363

364-
private AbfsRestOperation createPathImpl(final String path,
364+
@VisibleForTesting
365+
public AbfsRestOperation createPathImpl(final String path,
365366
AbfsUriQueryBuilder abfsUriQueryBuilder,
366367
final boolean overwrite,
367368
final String permission,

hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAbfsNetworkStatistics.java

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,16 @@ public void testAbfsHttpSendStatistics() throws IOException {
110110
connectionsMade++;
111111
requestsSent++;
112112

113+
113114
try (AbfsOutputStream out = createAbfsOutputStreamWithFlushEnabled(fs,
114115
sendRequestPath)) {
115-
boolean createOverwriteNeedsAdditionalRequest = false;
116-
if (fs.getAbfsStore()
117-
.getAbfsConfiguration()
118-
.isDefaultCreateOverwriteDisabled()) {
119-
// if the default overwrite=true behaviour is disabled by config,
120-
// the re-create here will need 2 requests.
121-
createOverwriteNeedsAdditionalRequest = true;
116+
117+
// Is a file overwrite case
118+
long createRequestCalls = 1;
119+
long createTriggeredGFSForETag = 0;
120+
if (this.getConfiguration().isDefaultCreateOverwriteDisabled()) {
121+
createRequestCalls += 1;
122+
createTriggeredGFSForETag = 1;
122123
}
123124

124125
for (int i = 0; i < LARGE_OPERATIONS; i++) {
@@ -149,22 +150,20 @@ public void testAbfsHttpSendStatistics() throws IOException {
149150
* wrote each time).
150151
*
151152
*/
152-
long expectedConnectionsMade = connectionsMade + 1
153-
+ (createOverwriteNeedsAdditionalRequest ? 1 : 0);
154-
long expectedSendRequests = requestsSent + 1
155-
+ (createOverwriteNeedsAdditionalRequest ? 1 : 0);
156153

154+
connectionsMade += createRequestCalls + createTriggeredGFSForETag;
155+
requestsSent += createRequestCalls;
157156
if (fs.getAbfsStore().isAppendBlobKey(fs.makeQualified(sendRequestPath).toString())) {
158157
// no network calls are made for hflush in case of appendblob
159158
assertAbfsStatistics(CONNECTIONS_MADE,
160-
expectedConnectionsMade + LARGE_OPERATIONS, metricMap);
159+
connectionsMade + LARGE_OPERATIONS, metricMap);
161160
assertAbfsStatistics(SEND_REQUESTS,
162-
expectedSendRequests + LARGE_OPERATIONS, metricMap);
161+
requestsSent + LARGE_OPERATIONS, metricMap);
163162
} else {
164163
assertAbfsStatistics(CONNECTIONS_MADE,
165-
expectedConnectionsMade + LARGE_OPERATIONS * 2, metricMap);
164+
connectionsMade + LARGE_OPERATIONS * 2, metricMap);
166165
assertAbfsStatistics(SEND_REQUESTS,
167-
expectedSendRequests + LARGE_OPERATIONS * 2, metricMap);
166+
requestsSent + LARGE_OPERATIONS * 2, metricMap);
168167
}
169168
assertAbfsStatistics(AbfsStatistic.BYTES_SENT,
170169
bytesSent + LARGE_OPERATIONS * (testNetworkStatsString.getBytes().length),
@@ -250,14 +249,21 @@ public void testAbfsHttpResponseStatistics() throws IOException {
250249
try {
251250

252251
/*
253-
* Creating a file and writing buffer into it. Also recording the
254-
* buffer for future read() call.
252+
* Creating a file and writing buffer into it.
253+
* This is a file recreate, so it will trigger
254+
* 2 extra calls if create overwrite is off by default.
255+
* Also recording the buffer for future read() call.
255256
* This creating outputStream and writing requires 2 *
256257
* (LARGE_OPERATIONS) get requests.
257258
*/
258259
StringBuilder largeBuffer = new StringBuilder();
259260
out = fs.create(getResponsePath);
260261

262+
long createRequestCalls = 1;
263+
if (this.getConfiguration().isDefaultCreateOverwriteDisabled()) {
264+
createRequestCalls += 2;
265+
}
266+
261267
for (int i = 0; i < LARGE_OPERATIONS; i++) {
262268
out.write(testResponseString.getBytes());
263269
out.hflush();
@@ -282,7 +288,8 @@ public void testAbfsHttpResponseStatistics() throws IOException {
282288
*
283289
* get_response : get_responses(Last assertion) + 1
284290
* (OutputStream) + 2 * LARGE_OPERATIONS(Writing and flushing
285-
* LARGE_OPERATIONS times) + 1(open()) + 1(read()).
291+
* LARGE_OPERATIONS times) + 1(open()) + 1(read()) +
292+
* 1 (createOverwriteTriggeredGetForeTag).
286293
*
287294
* bytes_received : bytes_received(Last assertion) + LARGE_OPERATIONS *
288295
* bytes wrote each time (bytes_received is equal to bytes wrote in the
@@ -298,7 +305,8 @@ public void testAbfsHttpResponseStatistics() throws IOException {
298305
getResponses + 3 + LARGE_OPERATIONS, metricMap);
299306
} else {
300307
assertAbfsStatistics(AbfsStatistic.GET_RESPONSES,
301-
getResponses + 3 + 2 * LARGE_OPERATIONS, metricMap);
308+
getResponses + 2 + createRequestCalls + 2 * LARGE_OPERATIONS,
309+
metricMap);
302310
}
303311

304312
} finally {
@@ -333,4 +341,4 @@ public void testAbfsHttpResponseFailure() throws IOException {
333341
IOUtils.cleanupWithLogger(LOG, out);
334342
}
335343
}
336-
}
344+
}

hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAzureBlobFileSystemCreate.java

Lines changed: 165 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,26 @@
3535
import org.apache.hadoop.fs.permission.FsPermission;
3636
import org.apache.hadoop.test.GenericTestUtils;
3737

38+
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AbfsRestOperationException;
39+
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.ConcurrentWriteOperationDetectedException;
40+
import org.apache.hadoop.fs.azurebfs.services.AbfsHttpOperation;
41+
import org.apache.hadoop.fs.azurebfs.services.AbfsRestOperation;
42+
import org.apache.hadoop.fs.azurebfs.services.AbfsUriQueryBuilder;
43+
44+
import static java.net.HttpURLConnection.HTTP_CONFLICT;
45+
import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
46+
import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
47+
import static java.net.HttpURLConnection.HTTP_OK;
48+
import static java.net.HttpURLConnection.HTTP_PRECON_FAILED;
49+
50+
import static org.mockito.ArgumentMatchers.any;
51+
import static org.mockito.ArgumentMatchers.eq;
52+
import static org.mockito.Mockito.doThrow;
53+
import static org.mockito.Mockito.mock;
54+
import static org.mockito.Mockito.when;
55+
3856
import static org.apache.hadoop.fs.contract.ContractTestUtils.assertIsFile;
3957
import static org.apache.hadoop.test.LambdaTestUtils.intercept;
40-
4158
import static org.apache.hadoop.fs.azurebfs.AbfsStatistic.CONNECTIONS_MADE;
4259

4360
/**
@@ -286,4 +303,151 @@ public void testCreateFileOverwrite(boolean defaultDisableCreateOverwrite)
286303
totalConnectionMadeBeforeTest + createRequestCount,
287304
fs.getInstrumentationMap());
288305
}
306+
307+
/**
308+
* Test negative scenarios with Create overwrite=false as default
309+
* With create overwrite=true ending in 3 calls:
310+
* A. Create overwrite=false
311+
* B. GFS
312+
* C. Create overwrite=true
313+
*
314+
* Scn1: A fails with HTTP409, leading to B which fails with HTTP404,
315+
* detect parallel access
316+
* Scn2: A fails with HTTP409, leading to B which fails with HTTP500,
317+
* fail create with HTTP500
318+
* Scn3: A fails with HTTP409, leading to B and then C,
319+
* which fails with HTTP412, detect parallel access
320+
* Scn4: A fails with HTTP409, leading to B and then C,
321+
* which fails with HTTP500, fail create with HTTP500
322+
* Scn5: A fails with HTTP500, fail create with HTTP500
323+
*/
324+
@Test
325+
public void testNegativeScenariosForCreateOverwriteDisabled()
326+
throws Throwable {
327+
328+
final AzureBlobFileSystem currentFs = getFileSystem();
329+
Configuration config = new Configuration(this.getRawConfiguration());
330+
config.set("fs.azure.disable.default.create.overwrite",
331+
Boolean.toString(true));
332+
333+
final AzureBlobFileSystem fs =
334+
(AzureBlobFileSystem) FileSystem.newInstance(currentFs.getUri(),
335+
config);
336+
337+
// Get mock AbfsClient with current config
338+
org.apache.hadoop.fs.azurebfs.services.AbfsClient
339+
mockClient
340+
= org.apache.hadoop.fs.azurebfs.services.TestAbfsClient.getMockAbfsClient(
341+
fs.getAbfsStore().getClient(),
342+
fs.getAbfsStore().getAbfsConfiguration());
343+
344+
AbfsRestOperation successOp = mock(
345+
AbfsRestOperation.class);
346+
AbfsHttpOperation http200Op = mock(
347+
AbfsHttpOperation.class);
348+
when(http200Op.getStatusCode()).thenReturn(HTTP_OK);
349+
when(successOp.getResult()).thenReturn(http200Op);
350+
351+
AbfsRestOperationException conflictResponseEx
352+
= getMockAbfsRestOperationException(HTTP_CONFLICT);
353+
AbfsRestOperationException serverErrorResponseEx
354+
= getMockAbfsRestOperationException(HTTP_INTERNAL_ERROR);
355+
AbfsRestOperationException fileNotFoundResponseEx
356+
= getMockAbfsRestOperationException(HTTP_NOT_FOUND);
357+
AbfsRestOperationException preConditionResponseEx
358+
= getMockAbfsRestOperationException(HTTP_PRECON_FAILED);
359+
360+
doThrow(conflictResponseEx) // Scn1: GFS fails with Http404
361+
.doThrow(conflictResponseEx) // Scn2: GFS fails with Http500
362+
.doThrow(
363+
conflictResponseEx) // Scn3: create overwrite=true fails with Http412
364+
.doThrow(
365+
conflictResponseEx) // Scn4: create overwrite=true fails with Http500
366+
.doThrow(
367+
serverErrorResponseEx) // Scn5: create overwrite=false fails with Http500
368+
.when(mockClient)
369+
.createPathImpl(any(String.class), any(
370+
AbfsUriQueryBuilder.class),
371+
eq(false), any(String.class), any(String.class), eq(null));
372+
373+
doThrow(fileNotFoundResponseEx) // Scn1: GFS fails with Http404
374+
.doThrow(serverErrorResponseEx) // Scn2: GFS fails with Http500
375+
.doReturn(successOp) // Scn3: create overwrite=true fails with Http412
376+
.doReturn(successOp) // Scn4: create overwrite=true fails with Http500
377+
.when(mockClient)
378+
.getPathStatus(any(String.class), eq(false));
379+
380+
doThrow(
381+
preConditionResponseEx) // Scn3: create overwrite=true fails with Http412
382+
.doThrow(
383+
serverErrorResponseEx) // Scn4: create overwrite=true fails with Http500
384+
.when(mockClient)
385+
.createPathImpl(any(String.class), any(
386+
AbfsUriQueryBuilder.class),
387+
eq(true), any(String.class), any(String.class), eq(null));
388+
389+
when(mockClient.createPath(any(String.class), eq(true), eq(true),
390+
any(String.class),
391+
any(String.class), eq(false))).thenCallRealMethod();
392+
393+
// Scn1: GFS fails with Http404
394+
// Sequence of events expected:
395+
// 1. create overwrite=false - fail with conflict
396+
// 2. GFS - fail with File Not found
397+
// Create will fail with ConcurrentWriteOperationDetectedException
398+
intercept(
399+
ConcurrentWriteOperationDetectedException.class,
400+
() ->
401+
mockClient.createPath("someTestPath", true, true, "0644", "0022",
402+
false));
403+
404+
// Scn2: GFS fails with Http500
405+
// Sequence of events expected:
406+
// 1. create overwrite=false - fail with conflict
407+
// 2. GFS - fail with Server error
408+
// Create will fail with 500
409+
intercept(
410+
AbfsRestOperationException.class,
411+
() ->
412+
mockClient.createPath("someTestPath", true, true, "0644", "0022",
413+
false));
414+
415+
// Scn3: create overwrite=true fails with Http412
416+
// Sequence of events expected:
417+
// 1. create overwrite=false - fail with conflict
418+
// 2. GFS - pass
419+
// 3. create overwrite=true - fail with Pre-Condition
420+
// Create will fail with ConcurrentWriteOperationDetectedException
421+
intercept(
422+
ConcurrentWriteOperationDetectedException.class,
423+
() ->
424+
mockClient.createPath("someTestPath", true, true, "0644", "0022",
425+
false));
426+
427+
// Scn4: create overwrite=true fails with Http500
428+
// Sequence of events expected:
429+
// 1. create overwrite=false - fail with conflict
430+
// 2. GFS - pass
431+
// 3. create overwrite=true - fail with Server error
432+
// Create will fail with 500
433+
intercept(
434+
AbfsRestOperationException.class,
435+
() ->
436+
mockClient.createPath("someTestPath", true, true, "0644", "0022",
437+
false));
438+
439+
// Scn5: create overwrite=false fails with Http500
440+
// Sequence of events expected:
441+
// 1. create overwrite=false - fail with server error
442+
// Create will fail with 500
443+
intercept(
444+
AbfsRestOperationException.class,
445+
() ->
446+
mockClient.createPath("someTestPath", true, true, "0644", "0022",
447+
false));
448+
}
449+
450+
private AbfsRestOperationException getMockAbfsRestOperationException(int status) {
451+
return new AbfsRestOperationException(status, "", "", new Exception());
452+
}
289453
}

hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/services/TestAbfsClient.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,10 +309,19 @@ public static AbfsClient getMockAbfsClient(AbfsClient baseAbfsClientInstance,
309309
when(client.getSharedKeyCredentials()).thenCallRealMethod();
310310
when(client.createDefaultHeaders()).thenCallRealMethod();
311311

312+
// override baseurl
313+
Field abfsConfigurationField = AbfsClient.class.getDeclaredField("abfsConfiguration");
314+
abfsConfigurationField.setAccessible(true);
315+
Field modifiersField = Field.class.getDeclaredField("modifiers");
316+
modifiersField.setAccessible(true);
317+
modifiersField.setInt(abfsConfigurationField,
318+
abfsConfigurationField.getModifiers()
319+
& ~java.lang.reflect.Modifier.FINAL);
320+
abfsConfigurationField.set(client, abfsConfig);
321+
312322
// override baseurl
313323
Field baseUrlField = AbfsClient.class.getDeclaredField("baseUrl");
314324
baseUrlField.setAccessible(true);
315-
Field modifiersField = Field.class.getDeclaredField("modifiers");
316325
modifiersField.setAccessible(true);
317326
modifiersField.setInt(baseUrlField, baseUrlField.getModifiers() & ~java.lang.reflect.Modifier.FINAL);
318327
baseUrlField.set(client, baseAbfsClientInstance.getBaseUrl());

0 commit comments

Comments
 (0)