Skip to content

Commit fb81998

Browse files
committed
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 829a9bd commit fb81998

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-]*";
@@ -152,17 +162,17 @@ public void setListener(Listener listener) {
152162
* X_MS_CLIENT_REQUEST_ID header of the http operation
153163
* @param httpOperation AbfsHttpOperation instance to set header into
154164
* connection
155-
* @param previousFailure List of failures seen before this API trigger on
156-
* same operation from AbfsClient.
165+
* @param previousFailure Failure seen before this API trigger on same operation
166+
* from AbfsClient.
157167
*/
158168
public void constructHeader(AbfsHttpOperation httpOperation, String previousFailure) {
159169
clientRequestId = UUID.randomUUID().toString();
160170
switch (format) {
161171
case ALL_ID_FORMAT: // Optional IDs (e.g. streamId) may be empty
162172
header =
163173
clientCorrelationID + ":" + clientRequestId + ":" + fileSystemID + ":"
164-
+ primaryRequestId + ":" + streamID + ":" + opType + ":"
165-
+ retryCount;
174+
+ getPrimaryRequestIdForHeader(retryCount > 0) + ":" + streamID
175+
+ ":" + opType + ":" + retryCount;
166176
header = addFailureReasons(header, previousFailure);
167177
break;
168178
case TWO_ID_FORMAT:
@@ -175,6 +185,31 @@ public void constructHeader(AbfsHttpOperation httpOperation, String previousFail
175185
listener.callTracingHeaderValidator(header, format);
176186
}
177187
httpOperation.setRequestProperty(HttpHeaderConfigurations.X_MS_CLIENT_REQUEST_ID, header);
188+
/*
189+
* In case the primaryRequestId is an empty-string and if it is the first try to
190+
* API call (previousFailure shall be null), maintain the last part of clientRequestId's
191+
* UUID in primaryRequestIdForRetry. This field shall be used as primaryRequestId part
192+
* of the x-ms-client-request-id header in case of retry of the same API-request.
193+
*/
194+
if (primaryRequestId.isEmpty() && previousFailure == null) {
195+
String[] clientRequestIdParts = clientRequestId.split("-");
196+
primaryRequestIdForRetry = clientRequestIdParts[
197+
clientRequestIdParts.length - 1];
198+
}
199+
}
200+
201+
/**
202+
* Provide value to be used as primaryRequestId part of x-ms-client-request-id header.
203+
* @param isRetry define if it's for a retry case.
204+
* @return {@link #primaryRequestIdForRetry}:If the {@link #primaryRequestId}
205+
* is an empty-string, and it's a retry iteration.
206+
* {@link #primaryRequestId} for other cases.
207+
*/
208+
private String getPrimaryRequestIdForHeader(final Boolean isRetry) {
209+
if (!primaryRequestId.isEmpty() || !isRetry) {
210+
return primaryRequestId;
211+
}
212+
return primaryRequestIdForRetry;
178213
}
179214

180215
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)