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
This commit is contained in:
parent
7c42d0f7da
commit
759ddebb13
|
@ -63,6 +63,16 @@ public class TracingContext {
|
||||||
//final concatenated ID list set into x-ms-client-request-id header
|
//final concatenated ID list set into x-ms-client-request-id header
|
||||||
private String header = EMPTY_STRING;
|
private String header = EMPTY_STRING;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If {@link #primaryRequestId} is null, this field shall be set equal
|
||||||
|
* to the last part of the {@link #clientRequestId}'s UUID
|
||||||
|
* in {@link #constructHeader(AbfsHttpOperation, String)} only on the
|
||||||
|
* first API call for an operation. Subsequent retries for that operation
|
||||||
|
* will not change this field. In case {@link #primaryRequestId} is non-null,
|
||||||
|
* this field shall not be set.
|
||||||
|
*/
|
||||||
|
private String primaryRequestIdForRetry;
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(AbfsClient.class);
|
private static final Logger LOG = LoggerFactory.getLogger(AbfsClient.class);
|
||||||
public static final int MAX_CLIENT_CORRELATION_ID_LENGTH = 72;
|
public static final int MAX_CLIENT_CORRELATION_ID_LENGTH = 72;
|
||||||
public static final String CLIENT_CORRELATION_ID_PATTERN = "[a-zA-Z0-9-]*";
|
public static final String CLIENT_CORRELATION_ID_PATTERN = "[a-zA-Z0-9-]*";
|
||||||
|
@ -152,8 +162,8 @@ public class TracingContext {
|
||||||
* X_MS_CLIENT_REQUEST_ID header of the http operation
|
* X_MS_CLIENT_REQUEST_ID header of the http operation
|
||||||
* @param httpOperation AbfsHttpOperation instance to set header into
|
* @param httpOperation AbfsHttpOperation instance to set header into
|
||||||
* connection
|
* connection
|
||||||
* @param previousFailure List of failures seen before this API trigger on
|
* @param previousFailure Failure seen before this API trigger on same operation
|
||||||
* same operation from AbfsClient.
|
* from AbfsClient.
|
||||||
*/
|
*/
|
||||||
public void constructHeader(AbfsHttpOperation httpOperation, String previousFailure) {
|
public void constructHeader(AbfsHttpOperation httpOperation, String previousFailure) {
|
||||||
clientRequestId = UUID.randomUUID().toString();
|
clientRequestId = UUID.randomUUID().toString();
|
||||||
|
@ -161,8 +171,8 @@ public class TracingContext {
|
||||||
case ALL_ID_FORMAT: // Optional IDs (e.g. streamId) may be empty
|
case ALL_ID_FORMAT: // Optional IDs (e.g. streamId) may be empty
|
||||||
header =
|
header =
|
||||||
clientCorrelationID + ":" + clientRequestId + ":" + fileSystemID + ":"
|
clientCorrelationID + ":" + clientRequestId + ":" + fileSystemID + ":"
|
||||||
+ primaryRequestId + ":" + streamID + ":" + opType + ":"
|
+ getPrimaryRequestIdForHeader(retryCount > 0) + ":" + streamID
|
||||||
+ retryCount;
|
+ ":" + opType + ":" + retryCount;
|
||||||
header = addFailureReasons(header, previousFailure);
|
header = addFailureReasons(header, previousFailure);
|
||||||
break;
|
break;
|
||||||
case TWO_ID_FORMAT:
|
case TWO_ID_FORMAT:
|
||||||
|
@ -175,6 +185,31 @@ public class TracingContext {
|
||||||
listener.callTracingHeaderValidator(header, format);
|
listener.callTracingHeaderValidator(header, format);
|
||||||
}
|
}
|
||||||
httpOperation.setRequestProperty(HttpHeaderConfigurations.X_MS_CLIENT_REQUEST_ID, header);
|
httpOperation.setRequestProperty(HttpHeaderConfigurations.X_MS_CLIENT_REQUEST_ID, header);
|
||||||
|
/*
|
||||||
|
* In case the primaryRequestId is an empty-string and if it is the first try to
|
||||||
|
* API call (previousFailure shall be null), maintain the last part of clientRequestId's
|
||||||
|
* UUID in primaryRequestIdForRetry. This field shall be used as primaryRequestId part
|
||||||
|
* of the x-ms-client-request-id header in case of retry of the same API-request.
|
||||||
|
*/
|
||||||
|
if (primaryRequestId.isEmpty() && previousFailure == null) {
|
||||||
|
String[] clientRequestIdParts = clientRequestId.split("-");
|
||||||
|
primaryRequestIdForRetry = clientRequestIdParts[
|
||||||
|
clientRequestIdParts.length - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide value to be used as primaryRequestId part of x-ms-client-request-id header.
|
||||||
|
* @param isRetry define if it's for a retry case.
|
||||||
|
* @return {@link #primaryRequestIdForRetry}:If the {@link #primaryRequestId}
|
||||||
|
* is an empty-string, and it's a retry iteration.
|
||||||
|
* {@link #primaryRequestId} for other cases.
|
||||||
|
*/
|
||||||
|
private String getPrimaryRequestIdForHeader(final Boolean isRetry) {
|
||||||
|
if (!primaryRequestId.isEmpty() || !isRetry) {
|
||||||
|
return primaryRequestId;
|
||||||
|
}
|
||||||
|
return primaryRequestIdForRetry;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String addFailureReasons(final String header,
|
private String addFailureReasons(final String header,
|
||||||
|
|
|
@ -31,12 +31,14 @@ import org.junit.Assume;
|
||||||
import org.junit.AssumptionViolatedException;
|
import org.junit.AssumptionViolatedException;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
import org.apache.hadoop.fs.CommonPathCapabilities;
|
import org.apache.hadoop.fs.CommonPathCapabilities;
|
||||||
import org.apache.hadoop.fs.Path;
|
import org.apache.hadoop.fs.Path;
|
||||||
import org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants;
|
import org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants;
|
||||||
import org.apache.hadoop.fs.azurebfs.constants.FSOperationType;
|
import org.apache.hadoop.fs.azurebfs.constants.FSOperationType;
|
||||||
import org.apache.hadoop.fs.azurebfs.enums.Trilean;
|
import org.apache.hadoop.fs.azurebfs.enums.Trilean;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.services.AbfsHttpOperation;
|
||||||
import org.apache.hadoop.fs.azurebfs.services.AbfsRestOperation;
|
import org.apache.hadoop.fs.azurebfs.services.AbfsRestOperation;
|
||||||
import org.apache.hadoop.fs.azurebfs.services.AuthType;
|
import org.apache.hadoop.fs.azurebfs.services.AuthType;
|
||||||
import org.apache.hadoop.fs.azurebfs.utils.TracingContext;
|
import org.apache.hadoop.fs.azurebfs.utils.TracingContext;
|
||||||
|
@ -198,4 +200,74 @@ public class TestTracingContext extends AbstractAbfsIntegrationTest {
|
||||||
fs.getAbfsStore().setNamespaceEnabled(Trilean.TRUE);
|
fs.getAbfsStore().setNamespaceEnabled(Trilean.TRUE);
|
||||||
fs.access(new Path("/"), FsAction.READ);
|
fs.access(new Path("/"), FsAction.READ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRetryPrimaryRequestIdWhenInitiallySuppliedEmpty() throws Exception {
|
||||||
|
final AzureBlobFileSystem fs = getFileSystem();
|
||||||
|
final String fileSystemId = fs.getFileSystemId();
|
||||||
|
final String clientCorrelationId = fs.getClientCorrelationId();
|
||||||
|
final TracingHeaderFormat tracingHeaderFormat = TracingHeaderFormat.ALL_ID_FORMAT;
|
||||||
|
TracingContext tracingContext = new TracingContext(clientCorrelationId,
|
||||||
|
fileSystemId, FSOperationType.CREATE_FILESYSTEM, tracingHeaderFormat, new TracingHeaderValidator(
|
||||||
|
fs.getAbfsStore().getAbfsConfiguration().getClientCorrelationId(),
|
||||||
|
fs.getFileSystemId(), FSOperationType.CREATE_FILESYSTEM, false,
|
||||||
|
0));
|
||||||
|
AbfsHttpOperation abfsHttpOperation = Mockito.mock(AbfsHttpOperation.class);
|
||||||
|
Mockito.doNothing().when(abfsHttpOperation).setRequestProperty(Mockito.anyString(), Mockito.anyString());
|
||||||
|
tracingContext.constructHeader(abfsHttpOperation, null);
|
||||||
|
String header = tracingContext.getHeader();
|
||||||
|
String clientRequestIdUsed = header.split(":")[1];
|
||||||
|
String[] clientRequestIdUsedParts = clientRequestIdUsed.split("-");
|
||||||
|
String assertionPrimaryId = clientRequestIdUsedParts[clientRequestIdUsedParts.length - 1];
|
||||||
|
|
||||||
|
tracingContext.setRetryCount(1);
|
||||||
|
tracingContext.setListener(new TracingHeaderValidator(
|
||||||
|
fs.getAbfsStore().getAbfsConfiguration().getClientCorrelationId(),
|
||||||
|
fs.getFileSystemId(), FSOperationType.CREATE_FILESYSTEM, false,
|
||||||
|
1));
|
||||||
|
|
||||||
|
tracingContext.constructHeader(abfsHttpOperation, "RT");
|
||||||
|
header = tracingContext.getHeader();
|
||||||
|
String primaryRequestId = header.split(":")[3];
|
||||||
|
|
||||||
|
Assertions.assertThat(primaryRequestId)
|
||||||
|
.describedAs("PrimaryRequestId in a retried request's "
|
||||||
|
+ "tracingContext should be equal to last part of original "
|
||||||
|
+ "request's clientRequestId UUID")
|
||||||
|
.isEqualTo(assertionPrimaryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRetryPrimaryRequestIdWhenInitiallySuppliedNonEmpty() throws Exception {
|
||||||
|
final AzureBlobFileSystem fs = getFileSystem();
|
||||||
|
final String fileSystemId = fs.getFileSystemId();
|
||||||
|
final String clientCorrelationId = fs.getClientCorrelationId();
|
||||||
|
final TracingHeaderFormat tracingHeaderFormat = TracingHeaderFormat.ALL_ID_FORMAT;
|
||||||
|
TracingContext tracingContext = new TracingContext(clientCorrelationId,
|
||||||
|
fileSystemId, FSOperationType.CREATE_FILESYSTEM, tracingHeaderFormat, new TracingHeaderValidator(
|
||||||
|
fs.getAbfsStore().getAbfsConfiguration().getClientCorrelationId(),
|
||||||
|
fs.getFileSystemId(), FSOperationType.CREATE_FILESYSTEM, false,
|
||||||
|
0));
|
||||||
|
tracingContext.setPrimaryRequestID();
|
||||||
|
AbfsHttpOperation abfsHttpOperation = Mockito.mock(AbfsHttpOperation.class);
|
||||||
|
Mockito.doNothing().when(abfsHttpOperation).setRequestProperty(Mockito.anyString(), Mockito.anyString());
|
||||||
|
tracingContext.constructHeader(abfsHttpOperation, null);
|
||||||
|
String header = tracingContext.getHeader();
|
||||||
|
String assertionPrimaryId = header.split(":")[3];
|
||||||
|
|
||||||
|
tracingContext.setRetryCount(1);
|
||||||
|
tracingContext.setListener(new TracingHeaderValidator(
|
||||||
|
fs.getAbfsStore().getAbfsConfiguration().getClientCorrelationId(),
|
||||||
|
fs.getFileSystemId(), FSOperationType.CREATE_FILESYSTEM, false,
|
||||||
|
1));
|
||||||
|
|
||||||
|
tracingContext.constructHeader(abfsHttpOperation, "RT");
|
||||||
|
header = tracingContext.getHeader();
|
||||||
|
String primaryRequestId = header.split(":")[3];
|
||||||
|
|
||||||
|
Assertions.assertThat(primaryRequestId)
|
||||||
|
.describedAs("PrimaryRequestId in a retried request's tracingContext "
|
||||||
|
+ "should be equal to PrimaryRequestId in the original request.")
|
||||||
|
.isEqualTo(assertionPrimaryId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,6 +130,9 @@ public class TracingHeaderValidator implements Listener {
|
||||||
}
|
}
|
||||||
Assertions.assertThat(idList[5]).describedAs("Operation name incorrect")
|
Assertions.assertThat(idList[5]).describedAs("Operation name incorrect")
|
||||||
.isEqualTo(operation.toString());
|
.isEqualTo(operation.toString());
|
||||||
|
if (idList[6].contains("_")) {
|
||||||
|
idList[6] = idList[6].split("_")[0];
|
||||||
|
}
|
||||||
int retryCount = Integer.parseInt(idList[6]);
|
int retryCount = Integer.parseInt(idList[6]);
|
||||||
Assertions.assertThat(retryCount)
|
Assertions.assertThat(retryCount)
|
||||||
.describedAs("Retry was required due to issue on server side")
|
.describedAs("Retry was required due to issue on server side")
|
||||||
|
|
Loading…
Reference in New Issue