Skip to content

Commit 054afa1

Browse files
authored
HADOOP-18647. x-ms-client-request-id to identify the retry of an API. (#5437)
The x-ms-client-request-id now includes a field to indicate a call is a retry of a previous operation Contributed by Pranav Saxena
1 parent 20d3b9c commit 054afa1

File tree

3 files changed

+114
-4
lines changed

3 files changed

+114
-4
lines changed

hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/utils/TracingContext.java

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,16 @@ public class TracingContext {
6363
//final concatenated ID list set into x-ms-client-request-id header
6464
private String header = EMPTY_STRING;
6565

66+
/**
67+
* If {@link #primaryRequestId} is null, this field shall be set equal
68+
* to the last part of the {@link #clientRequestId}'s UUID
69+
* in {@link #constructHeader(AbfsHttpOperation, String)} only on the
70+
* first API call for an operation. Subsequent retries for that operation
71+
* will not change this field. In case {@link #primaryRequestId} is non-null,
72+
* this field shall not be set.
73+
*/
74+
private String primaryRequestIdForRetry;
75+
6676
private static final Logger LOG = LoggerFactory.getLogger(AbfsClient.class);
6777
public static final int MAX_CLIENT_CORRELATION_ID_LENGTH = 72;
6878
public static final String CLIENT_CORRELATION_ID_PATTERN = "[a-zA-Z0-9-]*";
@@ -156,17 +166,17 @@ public void setListener(Listener listener) {
156166
* X_MS_CLIENT_REQUEST_ID header of the http operation
157167
* @param httpOperation AbfsHttpOperation instance to set header into
158168
* connection
159-
* @param previousFailure List of failures seen before this API trigger on
160-
* same operation from AbfsClient.
169+
* @param previousFailure Failure seen before this API trigger on same operation
170+
* from AbfsClient.
161171
*/
162172
public void constructHeader(AbfsHttpOperation httpOperation, String previousFailure) {
163173
clientRequestId = UUID.randomUUID().toString();
164174
switch (format) {
165175
case ALL_ID_FORMAT: // Optional IDs (e.g. streamId) may be empty
166176
header =
167177
clientCorrelationID + ":" + clientRequestId + ":" + fileSystemID + ":"
168-
+ primaryRequestId + ":" + streamID + ":" + opType + ":"
169-
+ retryCount;
178+
+ getPrimaryRequestIdForHeader(retryCount > 0) + ":" + streamID
179+
+ ":" + opType + ":" + retryCount;
170180
header = addFailureReasons(header, previousFailure);
171181
break;
172182
case TWO_ID_FORMAT:
@@ -179,6 +189,31 @@ public void constructHeader(AbfsHttpOperation httpOperation, String previousFail
179189
listener.callTracingHeaderValidator(header, format);
180190
}
181191
httpOperation.setRequestProperty(HttpHeaderConfigurations.X_MS_CLIENT_REQUEST_ID, header);
192+
/*
193+
* In case the primaryRequestId is an empty-string and if it is the first try to
194+
* API call (previousFailure shall be null), maintain the last part of clientRequestId's
195+
* UUID in primaryRequestIdForRetry. This field shall be used as primaryRequestId part
196+
* of the x-ms-client-request-id header in case of retry of the same API-request.
197+
*/
198+
if (primaryRequestId.isEmpty() && previousFailure == null) {
199+
String[] clientRequestIdParts = clientRequestId.split("-");
200+
primaryRequestIdForRetry = clientRequestIdParts[
201+
clientRequestIdParts.length - 1];
202+
}
203+
}
204+
205+
/**
206+
* Provide value to be used as primaryRequestId part of x-ms-client-request-id header.
207+
* @param isRetry define if it's for a retry case.
208+
* @return {@link #primaryRequestIdForRetry}:If the {@link #primaryRequestId}
209+
* is an empty-string, and it's a retry iteration.
210+
* {@link #primaryRequestId} for other cases.
211+
*/
212+
private String getPrimaryRequestIdForHeader(final Boolean isRetry) {
213+
if (!primaryRequestId.isEmpty() || !isRetry) {
214+
return primaryRequestId;
215+
}
216+
return primaryRequestIdForRetry;
182217
}
183218

184219
private String addFailureReasons(final String header,

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

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,14 @@
3131
import org.junit.AssumptionViolatedException;
3232
import org.junit.Ignore;
3333
import org.junit.Test;
34+
import org.mockito.Mockito;
3435

3536
import org.apache.hadoop.fs.CommonPathCapabilities;
3637
import org.apache.hadoop.fs.Path;
3738
import org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants;
3839
import org.apache.hadoop.fs.azurebfs.constants.FSOperationType;
3940
import org.apache.hadoop.fs.azurebfs.enums.Trilean;
41+
import org.apache.hadoop.fs.azurebfs.services.AbfsHttpOperation;
4042
import org.apache.hadoop.fs.azurebfs.services.AbfsRestOperation;
4143
import org.apache.hadoop.fs.azurebfs.services.AuthType;
4244
import org.apache.hadoop.fs.azurebfs.utils.TracingContext;
@@ -198,4 +200,74 @@ public void testExternalOps() throws Exception {
198200
fs.getAbfsStore().setNamespaceEnabled(Trilean.TRUE);
199201
fs.access(new Path("/"), FsAction.READ);
200202
}
203+
204+
@Test
205+
public void testRetryPrimaryRequestIdWhenInitiallySuppliedEmpty() throws Exception {
206+
final AzureBlobFileSystem fs = getFileSystem();
207+
final String fileSystemId = fs.getFileSystemId();
208+
final String clientCorrelationId = fs.getClientCorrelationId();
209+
final TracingHeaderFormat tracingHeaderFormat = TracingHeaderFormat.ALL_ID_FORMAT;
210+
TracingContext tracingContext = new TracingContext(clientCorrelationId,
211+
fileSystemId, FSOperationType.CREATE_FILESYSTEM, tracingHeaderFormat, new TracingHeaderValidator(
212+
fs.getAbfsStore().getAbfsConfiguration().getClientCorrelationId(),
213+
fs.getFileSystemId(), FSOperationType.CREATE_FILESYSTEM, false,
214+
0));
215+
AbfsHttpOperation abfsHttpOperation = Mockito.mock(AbfsHttpOperation.class);
216+
Mockito.doNothing().when(abfsHttpOperation).setRequestProperty(Mockito.anyString(), Mockito.anyString());
217+
tracingContext.constructHeader(abfsHttpOperation, null);
218+
String header = tracingContext.getHeader();
219+
String clientRequestIdUsed = header.split(":")[1];
220+
String[] clientRequestIdUsedParts = clientRequestIdUsed.split("-");
221+
String assertionPrimaryId = clientRequestIdUsedParts[clientRequestIdUsedParts.length - 1];
222+
223+
tracingContext.setRetryCount(1);
224+
tracingContext.setListener(new TracingHeaderValidator(
225+
fs.getAbfsStore().getAbfsConfiguration().getClientCorrelationId(),
226+
fs.getFileSystemId(), FSOperationType.CREATE_FILESYSTEM, false,
227+
1));
228+
229+
tracingContext.constructHeader(abfsHttpOperation, "RT");
230+
header = tracingContext.getHeader();
231+
String primaryRequestId = header.split(":")[3];
232+
233+
Assertions.assertThat(primaryRequestId)
234+
.describedAs("PrimaryRequestId in a retried request's "
235+
+ "tracingContext should be equal to last part of original "
236+
+ "request's clientRequestId UUID")
237+
.isEqualTo(assertionPrimaryId);
238+
}
239+
240+
@Test
241+
public void testRetryPrimaryRequestIdWhenInitiallySuppliedNonEmpty() throws Exception {
242+
final AzureBlobFileSystem fs = getFileSystem();
243+
final String fileSystemId = fs.getFileSystemId();
244+
final String clientCorrelationId = fs.getClientCorrelationId();
245+
final TracingHeaderFormat tracingHeaderFormat = TracingHeaderFormat.ALL_ID_FORMAT;
246+
TracingContext tracingContext = new TracingContext(clientCorrelationId,
247+
fileSystemId, FSOperationType.CREATE_FILESYSTEM, tracingHeaderFormat, new TracingHeaderValidator(
248+
fs.getAbfsStore().getAbfsConfiguration().getClientCorrelationId(),
249+
fs.getFileSystemId(), FSOperationType.CREATE_FILESYSTEM, false,
250+
0));
251+
tracingContext.setPrimaryRequestID();
252+
AbfsHttpOperation abfsHttpOperation = Mockito.mock(AbfsHttpOperation.class);
253+
Mockito.doNothing().when(abfsHttpOperation).setRequestProperty(Mockito.anyString(), Mockito.anyString());
254+
tracingContext.constructHeader(abfsHttpOperation, null);
255+
String header = tracingContext.getHeader();
256+
String assertionPrimaryId = header.split(":")[3];
257+
258+
tracingContext.setRetryCount(1);
259+
tracingContext.setListener(new TracingHeaderValidator(
260+
fs.getAbfsStore().getAbfsConfiguration().getClientCorrelationId(),
261+
fs.getFileSystemId(), FSOperationType.CREATE_FILESYSTEM, false,
262+
1));
263+
264+
tracingContext.constructHeader(abfsHttpOperation, "RT");
265+
header = tracingContext.getHeader();
266+
String primaryRequestId = header.split(":")[3];
267+
268+
Assertions.assertThat(primaryRequestId)
269+
.describedAs("PrimaryRequestId in a retried request's tracingContext "
270+
+ "should be equal to PrimaryRequestId in the original request.")
271+
.isEqualTo(assertionPrimaryId);
272+
}
201273
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ private void validateBasicFormat(String[] idList) {
130130
}
131131
Assertions.assertThat(idList[5]).describedAs("Operation name incorrect")
132132
.isEqualTo(operation.toString());
133+
if (idList[6].contains("_")) {
134+
idList[6] = idList[6].split("_")[0];
135+
}
133136
int retryCount = Integer.parseInt(idList[6]);
134137
Assertions.assertThat(retryCount)
135138
.describedAs("Retry was required due to issue on server side")

0 commit comments

Comments
 (0)