use of com.microsoft.azure.cosmosdb.Document in project ambry by linkedin.
the class CosmosDataAccessorTest method testQueryChangeFeedNormal.
/**
* Test change feed query.
*/
@Test
public void testQueryChangeFeedNormal() throws Exception {
Observable<FeedResponse<Document>> mockResponse = mock(Observable.class);
List<Document> docList = Collections.singletonList(AzureTestUtils.createDocumentFromCloudBlobMetadata(blobMetadata));
mockObservableForChangeFeedQuery(docList, mockResponse);
when(mockumentClient.queryDocumentChangeFeed(anyString(), any(ChangeFeedOptions.class))).thenReturn(mockResponse);
// test with non null requestContinuationToken
List<CloudBlobMetadata> metadataList = doQueryChangeFeed("test");
assertEquals("Expected single entry", 1, metadataList.size());
CloudBlobMetadata outputMetadata = metadataList.get(0);
assertEquals("Returned metadata does not match original", blobMetadata, outputMetadata);
assertEquals(1, azureMetrics.changeFeedQueryCount.getCount());
assertEquals(0, azureMetrics.changeFeedQueryFailureCount.getCount());
// test with a null continuation token
metadataList = doQueryChangeFeed(null);
assertEquals("Expected single entry", 1, metadataList.size());
outputMetadata = metadataList.get(0);
assertEquals("Returned metadata does not match original", blobMetadata, outputMetadata);
assertEquals(2, azureMetrics.changeFeedQueryCount.getCount());
assertEquals(0, azureMetrics.changeFeedQueryFailureCount.getCount());
// test when queryChangeFeed throws exception
when(mockumentClient.queryDocumentChangeFeed(anyString(), any(ChangeFeedOptions.class))).thenThrow(new RuntimeException("mock exception", new DocumentClientException(404)));
try {
doQueryChangeFeed(null);
} catch (DocumentClientException e) {
}
assertEquals(3, azureMetrics.changeFeedQueryCount.getCount());
assertEquals(1, azureMetrics.changeFeedQueryFailureCount.getCount());
}
use of com.microsoft.azure.cosmosdb.Document in project ambry by linkedin.
the class CosmosDataAccessor method getDeadBlobs.
/**
* Get the list of blobs in the specified partition that have been deleted or expired for at least the
* configured retention period.
* @param partitionPath the partition to query.
* @param fieldName the field name to query on. Allowed values are {@link CloudBlobMetadata#FIELD_DELETION_TIME} and
* {@link CloudBlobMetadata#FIELD_EXPIRATION_TIME}.
* @param startTime the start of the query time range.
* @param endTime the end of the query time range.
* @param maxEntries the max number of metadata records to return.
* @return a List of {@link CloudBlobMetadata} referencing the dead blobs found.
* @throws DocumentClientException
*/
List<CloudBlobMetadata> getDeadBlobs(String partitionPath, String fieldName, long startTime, long endTime, int maxEntries) throws DocumentClientException {
String deadBlobsQuery;
if (fieldName.equals(CloudBlobMetadata.FIELD_DELETION_TIME)) {
deadBlobsQuery = DELETED_BLOBS_QUERY;
} else if (fieldName.equals(CloudBlobMetadata.FIELD_EXPIRATION_TIME)) {
deadBlobsQuery = EXPIRED_BLOBS_QUERY;
} else {
throw new IllegalArgumentException("Invalid field: " + fieldName);
}
SqlQuerySpec querySpec = new SqlQuerySpec(deadBlobsQuery, new SqlParameterCollection(new SqlParameter(LIMIT_PARAM, maxEntries), new SqlParameter(START_TIME_PARAM, startTime), new SqlParameter(END_TIME_PARAM, endTime)));
FeedOptions feedOptions = new FeedOptions();
feedOptions.setMaxItemCount(maxEntries);
feedOptions.setResponseContinuationTokenLimitInKb(continuationTokenLimitKb);
feedOptions.setPartitionKey(new PartitionKey(partitionPath));
try {
Iterator<FeedResponse<Document>> iterator = executeCosmosQuery(partitionPath, querySpec, feedOptions, azureMetrics.deadBlobsQueryTime).getIterator();
List<CloudBlobMetadata> deadBlobsList = new ArrayList<>();
double requestCharge = 0.0;
while (iterator.hasNext()) {
FeedResponse<Document> response = iterator.next();
requestCharge += response.getRequestCharge();
response.getResults().iterator().forEachRemaining(doc -> deadBlobsList.add(createMetadataFromDocument(doc)));
}
if (requestCharge >= requestChargeThreshold) {
logger.info("Dead blobs query partition {} endTime {} request charge {} for {} records", partitionPath, new Date(endTime), requestCharge, deadBlobsList.size());
}
return deadBlobsList;
} catch (RuntimeException rex) {
if (rex.getCause() instanceof DocumentClientException) {
logger.warn("Dead blobs query {} partition {} got {}", deadBlobsQuery, partitionPath, ((DocumentClientException) rex.getCause()).getStatusCode());
throw (DocumentClientException) rex.getCause();
}
throw rex;
}
}
use of com.microsoft.azure.cosmosdb.Document in project ambry by linkedin.
the class CosmosDataAccessor method getMetadataOrNull.
/**
* Get the metadata record for a single blob.
* @param blobId the blob to read.
* @return the {@link CloudBlobMetadata} for the blob if it is found, otherwise null.
* @throws DocumentClientException on any other error.
*/
CloudBlobMetadata getMetadataOrNull(BlobId blobId) throws DocumentClientException {
String docLink = getDocumentLink(blobId.getID());
RequestOptions options = getRequestOptions(blobId.getPartition().toPathString());
try {
ResourceResponse<Document> readResponse = executeCosmosAction(() -> asyncDocumentClient.readDocument(docLink, options).toBlocking().single(), azureMetrics.documentReadTime);
return createMetadataFromDocument(readResponse.getResource());
} catch (DocumentClientException dex) {
if (dex.getStatusCode() == HttpConstants.StatusCodes.NOTFOUND) {
return null;
} else {
throw dex;
}
}
}
use of com.microsoft.azure.cosmosdb.Document in project ambry by linkedin.
the class CosmosDataAccessor method getDeprecatedContainers.
/**
* Fetch a {@link Set} of {@link CosmosContainerDeletionEntry} objects from cosmos db that are not marked as deleted.
* @param maxEntries Max number of entries to fetch on one query.
* @return {@link Set} of {@link CosmosContainerDeletionEntry} objects.
* @throws DocumentClientException in case of any error.
*/
public Set<CosmosContainerDeletionEntry> getDeprecatedContainers(int maxEntries) throws DocumentClientException {
SqlQuerySpec querySpec = new SqlQuerySpec(DEPRECATED_CONTAINERS_QUERY, new SqlParameterCollection(new SqlParameter(LIMIT_PARAM, maxEntries)));
Timer timer = new Timer();
Set<CosmosContainerDeletionEntry> containerDeletionEntries = new HashSet<>();
try {
Iterator<FeedResponse<Document>> iterator = executeCosmosQuery(cosmosDeletedContainerCollectionLink, null, querySpec, new FeedOptions(), timer).getIterator();
while (iterator.hasNext()) {
FeedResponse<Document> response = iterator.next();
response.getResults().iterator().forEachRemaining(doc -> containerDeletionEntries.add(CosmosContainerDeletionEntry.fromJson(new JSONObject(doc.toJson()))));
}
} catch (RuntimeException rex) {
if (rex.getCause() instanceof DocumentClientException) {
logger.warn("Get deprecated containers query {} got {}", querySpec.getQueryText(), ((DocumentClientException) rex.getCause()).getStatusCode());
throw (DocumentClientException) rex.getCause();
}
throw rex;
}
return containerDeletionEntries;
}
use of com.microsoft.azure.cosmosdb.Document in project ambry by linkedin.
the class CosmosDataAccessor method updateContainerDeletionEntry.
/**
* Update the container deletion entry document in the CosmosDB collection.
* @param containerId the container id for which document is replaced.
* @param accountId the account id for which document is replaced.
* @param updateFields {@link BiConsumer} object to use as callback to update the required fields.
* @return the {@link ResourceResponse} returned by the operation, if successful.
* Returns {@Null} if the field already has the specified value.
* @throws DocumentClientException if the record was not found or if the operation failed.
*/
ResourceResponse<Document> updateContainerDeletionEntry(short containerId, short accountId, BiConsumer<Document, AtomicBoolean> updateFields) throws DocumentClientException {
// Read the existing record
String id = CosmosContainerDeletionEntry.generateContainerDeletionEntryId(accountId, containerId);
String docLink = getContainerDeletionEntryDocumentLink(id);
RequestOptions options = getRequestOptions(id);
ResourceResponse<Document> readResponse = executeCosmosAction(() -> asyncDocumentClient.readDocument(docLink, options).toBlocking().single(), azureMetrics.continerDeletionEntryReadTime);
Document doc = readResponse.getResource();
AtomicBoolean fieldsChanged = new AtomicBoolean(false);
updateFields.accept(doc, fieldsChanged);
if (!fieldsChanged.get()) {
logger.debug("No change in value for container deletion entry {}", doc.toJson());
return null;
}
// For testing conflict handling
if (updateCallback != null) {
try {
updateCallback.call();
} catch (Exception ex) {
logger.error("Error in update callback", ex);
}
}
// Set condition to ensure we don't clobber a concurrent update
AccessCondition accessCondition = new AccessCondition();
accessCondition.setCondition(doc.getETag());
options.setAccessCondition(accessCondition);
try {
return executeCosmosAction(() -> asyncDocumentClient.replaceDocument(doc, options).toBlocking().single(), azureMetrics.documentUpdateTime);
} catch (DocumentClientException e) {
if (e.getStatusCode() == HttpConstants.StatusCodes.PRECONDITION_FAILED) {
azureMetrics.blobUpdateConflictCount.inc();
}
throw e;
}
}
Aggregations