use of com.github.ambry.protocol.UndeleteRequest in project ambry by linkedin.
the class AmbryServerRequestsTest method sendAndVerifyOperationRequest.
/**
* Sends and verifies that an operation specific request works correctly.
* @param requestType the type of the request to send.
* @param ids the partitionIds to send requests for.
* @param expectedErrorCode the {@link ServerErrorCode} expected in the response. For some requests this is the
* response in the constituents rather than the actual response ({@link GetResponse} and
* {@link ReplicaMetadataResponse}).
* @param forceCheckOpReceived if {@code true}, checks the operation received at the {@link Store} even if
* there is an error expected. Always checks op received if {@code expectedErrorCode} is
* {@link ServerErrorCode#No_Error}. Skips the check otherwise.
* @param clientIdStr the clientId string to construct request. if null, generate a random string as clientId.
* @throws InterruptedException
* @throws IOException
* @return a list of {@link Response}(s) associated with given partition ids.
*/
private List<Response> sendAndVerifyOperationRequest(RequestOrResponseType requestType, List<? extends PartitionId> ids, ServerErrorCode expectedErrorCode, Boolean forceCheckOpReceived, String clientIdStr) throws InterruptedException, IOException {
List<Response> responses = new ArrayList<>();
for (PartitionId id : ids) {
int correlationId = TestUtils.RANDOM.nextInt();
String clientId = clientIdStr == null ? TestUtils.getRandomString(10) : clientIdStr;
BlobId originalBlobId = new BlobId(CommonTestUtils.getCurrentBlobIdVersion(), BlobId.BlobIdType.NATIVE, ClusterMap.UNKNOWN_DATACENTER_ID, Utils.getRandomShort(TestUtils.RANDOM), Utils.getRandomShort(TestUtils.RANDOM), id, false, BlobId.BlobDataType.DATACHUNK);
BlobId convertedBlobId = new BlobId(CommonTestUtils.getCurrentBlobIdVersion(), BlobId.BlobIdType.CRAFTED, ClusterMap.UNKNOWN_DATACENTER_ID, originalBlobId.getAccountId(), originalBlobId.getContainerId(), id, false, BlobId.BlobDataType.DATACHUNK);
conversionMap.put(originalBlobId, convertedBlobId);
validKeysInStore.add(convertedBlobId);
RequestOrResponse request;
switch(requestType) {
case PutRequest:
BlobProperties properties = new BlobProperties(0, "serviceId", originalBlobId.getAccountId(), originalBlobId.getAccountId(), false);
request = new PutRequest(correlationId, clientId, originalBlobId, properties, ByteBuffer.allocate(0), Unpooled.wrappedBuffer(ByteBuffer.allocate(0)), 0, BlobType.DataBlob, null);
break;
case DeleteRequest:
request = new DeleteRequest(correlationId, clientId, originalBlobId, SystemTime.getInstance().milliseconds());
break;
case UndeleteRequest:
request = new UndeleteRequest(correlationId, clientId, originalBlobId, SystemTime.getInstance().milliseconds());
break;
case GetRequest:
PartitionRequestInfo pRequestInfo = new PartitionRequestInfo(id, Collections.singletonList(originalBlobId));
request = new GetRequest(correlationId, clientId, MessageFormatFlags.All, Collections.singletonList(pRequestInfo), GetOption.Include_All);
break;
case ReplicaMetadataRequest:
ReplicaMetadataRequestInfo rRequestInfo = new ReplicaMetadataRequestInfo(id, findTokenHelper.getFindTokenFactoryFromReplicaType(ReplicaType.DISK_BACKED).getNewFindToken(), "localhost", "/tmp", ReplicaType.DISK_BACKED, replicationConfig.replicaMetadataRequestVersion);
request = new ReplicaMetadataRequest(correlationId, clientId, Collections.singletonList(rRequestInfo), Long.MAX_VALUE, replicationConfig.replicaMetadataRequestVersion);
break;
case TtlUpdateRequest:
request = new TtlUpdateRequest(correlationId, clientId, originalBlobId, Utils.Infinite_Time, SystemTime.getInstance().milliseconds());
break;
default:
throw new IllegalArgumentException(requestType + " not supported by this function");
}
responses.add(sendAndVerifyOperationRequest(request, expectedErrorCode, forceCheckOpReceived));
}
return responses;
}
use of com.github.ambry.protocol.UndeleteRequest in project ambry by linkedin.
the class AmbryServerRequestsTest method doUndelete.
/**
* Does a UNDELETE and checks for success if {@code expectedErrorCode} is {@link ServerErrorCode#No_Error}. Else,
* checks for failure with the code {@code expectedErrorCode}.
* @param correlationId the correlation ID to use in the request
* @param clientId the client ID to use in the request
* @param blobId the blob ID to use in the request
* @param opTimeMs the operation time (ms) to use in the request
* @param expectedErrorCode the expected {@link ServerErrorCode}
* @throws Exception
*/
private void doUndelete(int correlationId, String clientId, BlobId blobId, long opTimeMs, ServerErrorCode expectedErrorCode) throws Exception {
UndeleteRequest request = new UndeleteRequest(correlationId, clientId, blobId, opTimeMs);
sendAndVerifyOperationRequest(request, expectedErrorCode, true).release();
if (expectedErrorCode == ServerErrorCode.No_Error) {
verifyUndelete(request.getBlobId(), opTimeMs, MockStorageManager.messageWriteSetReceived);
}
}
use of com.github.ambry.protocol.UndeleteRequest in project ambry by linkedin.
the class InMemoryCloudDestinationErrorSimulationTest method testUndeleteBlobErrorSimulation.
/**
* test error simulation for UndeleteRequest
* @throws Exception
*/
@Test
public void testUndeleteBlobErrorSimulation() throws Exception {
BlobId blobId = doPut(partitionId);
// Delete the blob first
{
DeleteRequest request = new DeleteRequest(1234, "clientId", blobId, SystemTime.getInstance().milliseconds());
RequestInfo requestInfo = new RequestInfo(hostname, port, request, replica, null);
ResponseInfo responseInfo = sendAndWaitForResponses(requestInfo);
DeleteResponse response = responseInfo.getError() == null ? (DeleteResponse) RouterUtils.mapToReceivedResponse((Response) responseInfo.getResponse()) : null;
Assert.assertEquals("DeleteRequest should succeed.", response.getError(), ServerErrorCode.No_Error);
}
UndeleteRequest request = new UndeleteRequest(1234, "clientId", blobId, SystemTime.getInstance().milliseconds());
RequestInfo requestInfo = new RequestInfo(hostname, port, request, replica, null);
ResponseInfo responseInfo = sendAndWaitForResponses(requestInfo);
UndeleteResponse response = responseInfo.getError() == null ? (UndeleteResponse) RouterUtils.mapToReceivedResponse((UndeleteResponse) responseInfo.getResponse()) : null;
Assert.assertEquals("UndeleteRequest should succeed.", response.getError(), ServerErrorCode.No_Error);
response.release();
// inject error for cloud colo.
cloudDestination.setServerErrorForAllRequests(StoreErrorCodes.Unknown_Error);
request = new UndeleteRequest(1234, "clientId", blobId, SystemTime.getInstance().milliseconds());
requestInfo = new RequestInfo(hostname, port, request, replica, null);
responseInfo = sendAndWaitForResponses(requestInfo);
response = responseInfo.getError() == null ? (UndeleteResponse) RouterUtils.mapToReceivedResponse((UndeleteResponse) responseInfo.getResponse()) : null;
Assert.assertEquals("UndeleteRequest should return Unknown_Error.", response.getError(), ServerErrorCode.Unknown_Error);
response.release();
}
use of com.github.ambry.protocol.UndeleteRequest in project ambry by linkedin.
the class NonBlockingRouterTestBase method ensureUndeleteInAllServers.
/**
* Ensure that Undelete request for given blob is reaches to all the mock servers in the {@link MockServerLayout}.
* @param blobId The blob id of which Undelete request will be created.
* @param serverLayout The mock server layout.
* @throws IOException
*/
protected void ensureUndeleteInAllServers(String blobId, MockServerLayout serverLayout) throws IOException {
BlobId id = new BlobId(blobId, mockClusterMap);
for (MockServer server : serverLayout.getMockServers()) {
if (!server.getBlobs().get(blobId).isUndeleted()) {
UndeleteRequest undeleteRequest = new UndeleteRequest(NonBlockingRouter.correlationIdGenerator.incrementAndGet(), routerConfig.routerHostname, id, mockTime.milliseconds());
server.send(undeleteRequest).release();
undeleteRequest.release();
}
}
}
use of com.github.ambry.protocol.UndeleteRequest in project ambry by linkedin.
the class UndeleteOperation method handleResponse.
/**
* Handles a response for a Undelete operation. It determines whether the request was successful and updates
* operation tracker. For the same operation, it is possible that different {@link ServerErrorCode} are received from
* different replicas. These error codes are eventually resolved to a single {@link RouterErrorCode}.
* @param responseInfo The {@link ResponseInfo} to be handled.
* @param undeleteResponse The {@link UndeleteResponse} associated with this response.
*/
void handleResponse(ResponseInfo responseInfo, UndeleteResponse undeleteResponse) {
UndeleteRequest undeleteRequest = (UndeleteRequest) responseInfo.getRequestInfo().getRequest();
UndeleteRequestInfo undeleteRequestInfo = undeleteRequestInfos.remove(undeleteRequest.getCorrelationId());
// metric is updated here, as corresponding metrics have been updated when the request was timed out.
if (undeleteRequestInfo == null) {
return;
}
ReplicaId replica = undeleteRequestInfo.replica;
long requestLatencyMs = time.milliseconds() - undeleteRequestInfo.startTimeMs;
routerMetrics.routerRequestLatencyMs.update(requestLatencyMs);
routerMetrics.getDataNodeBasedMetrics(replica.getDataNodeId()).undeleteRequestLatencyMs.update(requestLatencyMs);
// Check the error code from NetworkClient.
if (responseInfo.getError() != null) {
LOGGER.trace("UndeleteRequest with response correlationId {} timed out for replica {} ", undeleteRequest.getCorrelationId(), replica.getDataNodeId());
onErrorResponse(replica, new RouterException("Operation timed out because of " + responseInfo.getError() + " at DataNode " + responseInfo.getDataNode(), RouterErrorCode.OperationTimedOut));
} else {
if (undeleteResponse == null) {
LOGGER.trace("UndeleteRequest with response correlationId {} received UnexpectedInternalError on response deserialization for replica {} ", undeleteRequest.getCorrelationId(), replica.getDataNodeId());
onErrorResponse(replica, new RouterException("Response deserialization received an unexpected error", RouterErrorCode.UnexpectedInternalError));
} else {
// not for its original request. We will immediately fail this operation.
if (undeleteResponse.getCorrelationId() != undeleteRequest.getCorrelationId()) {
LOGGER.error("The correlation id in the DeleteResponse {} is not the same as the correlation id in the associated DeleteRequest: {}", undeleteResponse.getCorrelationId(), undeleteRequest.getCorrelationId());
routerMetrics.unknownReplicaResponseError.inc();
onErrorResponse(replica, new RouterException("Received wrong response that is not for the corresponding request.", RouterErrorCode.UnexpectedInternalError));
} else {
ServerErrorCode serverError = undeleteResponse.getError();
if (serverError == ServerErrorCode.No_Error || (serverError == ServerErrorCode.Blob_Already_Undeleted && // LIFE_VERSION_FROM_FRONTEND is an invalid lifeVersion
undeleteResponse.getLifeVersion() != MessageInfo.LIFE_VERSION_FROM_FRONTEND)) {
if (RouterUtils.isRemoteReplica(routerConfig, replica)) {
LOGGER.trace("Cross colo request successful for remote replica {} in {} ", replica.getDataNodeId(), replica.getDataNodeId().getDatacenterName());
routerMetrics.crossColoSuccessCount.inc();
}
if (lifeVersion == null) {
// This is first successful response.
lifeVersion = undeleteResponse.getLifeVersion();
firstResponseReplicaId = replica;
operationTracker.onResponse(replica, TrackedRequestFinalState.SUCCESS);
} else {
if (lifeVersion != undeleteResponse.getLifeVersion()) {
String message = "LifeVersion of " + blobId + " from Replica " + firstResponseReplicaId + " is different than the lifeVersion from replica " + replica + " " + lifeVersion + " != " + undeleteResponse.getLifeVersion();
LOGGER.error(message);
// this is a successful response and one that completes the operation regardless of whether the
// success target has been reached or not.
operationCompleted = true;
onErrorResponse(replica, new RouterException(message, RouterErrorCode.LifeVersionConflict));
} else {
operationTracker.onResponse(replica, TrackedRequestFinalState.SUCCESS);
}
}
} else if (serverError == ServerErrorCode.Disk_Unavailable) {
LOGGER.trace("Replica {} returned Disk_Unavailable for an undelete request with correlationId : {} ", replica, undeleteRequest.getCorrelationId());
operationTracker.onResponse(replica, TrackedRequestFinalState.DISK_DOWN);
setOperationException(new RouterException("Server returned: " + serverError, RouterErrorCode.AmbryUnavailable));
routerMetrics.routerRequestErrorCount.inc();
routerMetrics.getDataNodeBasedMetrics(replica.getDataNodeId()).undeleteRequestErrorCount.inc();
} else {
LOGGER.trace("Replica {} returned an error {} for an undelete request with response correlationId : {} ", replica, serverError, undeleteRequest.getCorrelationId());
RouterErrorCode routerErrorCode = processServerError(serverError);
if (serverError == ServerErrorCode.Blob_Authorization_Failure) {
// this is a successful response and one that completes the operation regardless of whether the
// success target has been reached or not.
operationCompleted = true;
}
// any server error code that is not equal to ServerErrorCode.No_Error, the onErrorResponse should be invoked
onErrorResponse(replica, new RouterException("Server returned: " + serverError, routerErrorCode));
}
}
}
}
checkAndMaybeComplete();
}
Aggregations