Exemplo n.º 1
0
        public void ItTriesToLockRecordThatIsNotExpired()
        {
            // Arrange
            var id                  = "123";
            var ownerId             = "foo";
            var ownerType           = "bar";
            var lockDurationSeconds = 5;

            IResourceResponse <Document> record = BuildResponseWithDocument(
                id: id,
                lockOwnerId: ownerId,
                lockOwnerType: ownerType,
                lockExpirationUtcMsecs: Now + testOffsetMs,
                expired: false);

            this.cosmosDbSql.Setup(x => x.ReadAsync(It.IsAny <IDocumentClient>(), this.storageConfig, id))
            .ReturnsAsync(record);

            // Act
            bool result = this.target.TryToLockAsync(id, ownerId, ownerType, lockDurationSeconds)
                          .CompleteOrTimeout().Result;

            // Assert
            Assert.True(result);
        }
Exemplo n.º 2
0
        public void ItTriesToUnlockRecord()
        {
            // Arrange
            var id        = Guid.NewGuid().ToString();
            var ownerId   = Guid.NewGuid().ToString();
            var ownerType = Guid.NewGuid().ToString();
            IResourceResponse <Document> record = BuildResponseWithDocument(
                id: id,
                lockOwnerId: ownerId,
                lockOwnerType: ownerType,
                lockExpirationUtcMsecs: Now + testOffsetMs,
                expired: false);

            this.cosmosDbSql.Setup(x => x.ReadAsync(It.IsAny <IDocumentClient>(), this.storageConfig, id))
            .ReturnsAsync(record);

            // Act
            this.target.TryToUnlockAsync(id, ownerId, ownerType).CompleteOrTimeout();

            // Assert
            this.cosmosDbSql.Verify(
                x => x.UpsertAsync(
                    It.IsAny <IDocumentClient>(),
                    It.IsAny <Config>(),
                    It.IsAny <Resource>()
                    ), Times.Once());
        }
Exemplo n.º 3
0
        /// <summary>
        ///     Выполняет синхронный запрос ресурса
        /// </summary>
        /// <returns></returns>
        public async Task <IResourceResponse> GetResponse(IResourceConfig config = null)
        {
            if (CheckCurrentCanBeReturned())
            {
                return(Response);
            }
            config = config ?? Config ?? ResourceConfig.Default;
            if (ResourceRequestState.Init == State)
            {
                if (null == Response)
                {
                    Response = await InternalGetResponse(config);
                }
            }
            if (ResourceRequestState.Error == State)
            {
                if (null == LastError)
                {
                    LastError = new ResourceException("some error in request");
                }
                throw LastError;
            }

            return(Response);
        }
        private async Task <(bool found, IDataRecord record)> RetrieveAsync(string id, bool throwIfNotFound)
        {
            await this.SetupStorageAsync();

            try
            {
                this.log.Debug("Fetching record...", () => new { this.storageName, id });
                IResourceResponse <Document> response = await this.cosmosDbSql.ReadAsync(this.cosmosDbSqlClient, this.storageConfig, id);

                this.log.Debug("Record fetched", () => new { this.storageName, id });

                Document document = response?.Resource;
                if (document == null)
                {
                    if (throwIfNotFound)
                    {
                        throw new ResourceNotFoundException($"The resource {id} doesn't exist.");
                    }
                    return(false, null);
                }

                var record = this.DocumentToRecord(document);
                if (record.IsExpired())
                {
                    this.log.Debug("The record requested has expired, deleting...", () => new { this.storageName, id });
                    await this.TryToDeleteExpiredRecord(id);

                    this.log.Debug("Expired record deleted", () => new { this.storageName, id });

                    if (throwIfNotFound)
                    {
                        throw new ResourceNotFoundException($"The record '{id}' doesn't exist.");
                    }
                    return(false, null);
                }

                return(true, record);
            }
            catch (CustomException)
            {
                throw;
            }
            catch (DocumentClientException e) when(e.StatusCode == HttpStatusCode.NotFound)
            {
                this.log.Debug("The record requested doesn't exist.", () => new { this.storageName, id });
                if (throwIfNotFound)
                {
                    throw new ResourceNotFoundException($"The record '{id}' doesn't exist.");
                }
                return(false, null);
            }
            catch (Exception e)
            {
                this.log.Error("Unexpected error while reading from Cosmos DB SQL",
                               () => new { this.storageName, id, e });
                throw new ExternalDependencyException(e);
            }
        }
        public static async Task <DocumentCollection> GetDocumentCollectionAsync(
            this IChangeFeedDocumentClient client,
            DocumentCollectionInfo collectionInfo)
        {
            Uri collectionUri = UriFactory.CreateDocumentCollectionUri(collectionInfo.DatabaseName, collectionInfo.CollectionName);
            IResourceResponse <DocumentCollection> response =
                await client.ReadDocumentCollectionAsync(collectionUri, new RequestOptions()).ConfigureAwait(false);

            return(response.Resource);
        }
Exemplo n.º 6
0
 /// <summary>
 ///
 /// </summary>
 /// <param name="requestIdentifier"></param>
 /// <param name="response"></param>
 /// <returns></returns>
 public static ResponseMetadata FromOffset(string requestIdentifier, IResourceResponse <Document> response)
 {
     return(new ResponseMetadata
     {
         RequestIdentifier = requestIdentifier,
         CurrentResourceQuotaUsage = response.CurrentResourceQuotaUsage,
         MaxResourceQuota = response.MaxResourceQuota,
         RequestCharge = response.RequestCharge
     });
 }
        // Note: requestOptions are only used for read and not for update.
        public async Task <ILease> UpdateLeaseAsync(
            ILease cachedLease,
            Uri documentUri,
            RequestOptions requestOptions,
            Func <ILease, ILease> updateLease,
            bool retryOnConflict)
        {
            ILease lease = cachedLease;

            for (int retryCount = retryOnConflict ? RetryCountOnConflict : 0; retryCount >= 0; retryCount--)
            {
                lease = updateLease(lease);
                if (lease == null)
                {
                    return(null);
                }

                lease.Timestamp = DateTime.UtcNow;
                Document leaseDocument = await this.TryReplaceLeaseAsync(lease, documentUri).ConfigureAwait(false);

                if (leaseDocument != null)
                {
                    return(DocumentServiceLease.FromDocument(leaseDocument));
                }

                Logger.InfoFormat("Partition {0} lease update conflict. Reading the current version of lease.", lease.PartitionId);
                Document document;
                try
                {
                    IResourceResponse <Document> response = await this.client.ReadDocumentAsync(
                        documentUri, requestOptions).ConfigureAwait(false);

                    document = response.Resource;
                }
                catch (DocumentClientException ex) when(ex.StatusCode == HttpStatusCode.NotFound)
                {
                    Logger.InfoFormat("Partition {0} lease no longer exists", lease.PartitionId);
                    throw new LeaseLostException(lease);
                }

                DocumentServiceLease serverLease = DocumentServiceLease.FromDocument(document);
                Logger.InfoFormat(
                    "Partition {0} update failed because the lease with token '{1}' was updated by host '{2}' with token '{3}'. Will retry, {4} retry(s) left.",
                    lease.PartitionId,
                    lease.ConcurrencyToken,
                    serverLease.Owner,
                    serverLease.ConcurrencyToken,
                    retryCount);

                lease = serverLease;
            }

            throw new LeaseLostException(lease);
        }
        public static async Task <Document> TryCreateDocumentAsync(this IChangeFeedDocumentClient client, string collectionLink, object document)
        {
            try
            {
                IResourceResponse <Document> response = await client.CreateDocumentAsync(collectionLink, document).ConfigureAwait(false);

                return(response.Resource);
            }
            catch (DocumentClientException ex) when(ex.StatusCode == HttpStatusCode.Conflict)
            {
                return(null);    // Ignore -- document already exists.
            }
        }
Exemplo n.º 9
0
        public ValueServiceModel(IResourceResponse <Document> response)
        {
            if (response == null)
            {
                return;
            }

            var resource = response.Resource;

            this.CollectionId = resource.GetPropertyValue <string>("CollectionId");
            this.Key          = resource.GetPropertyValue <string>("Key");
            this.Data         = resource.GetPropertyValue <string>("Data");
            this.ETag         = resource.ETag;
            this.Timestamp    = resource.Timestamp;
        }
        public static async Task <Document> TryDeleteDocumentAsync(
            this IChangeFeedDocumentClient client,
            Uri documentUri,
            RequestOptions requestOptions = null)
        {
            try
            {
                IResourceResponse <Document> response = await client.DeleteDocumentAsync(documentUri, requestOptions).ConfigureAwait(false);

                return(response.Resource);
            }
            catch (DocumentClientException ex) when(ex.StatusCode == HttpStatusCode.NotFound)
            {
                return(null);    // Ignore -- document not found.
            }
        }
        public static async Task <Document> TryGetDocumentAsync(this IChangeFeedDocumentClient client, Uri documentUri)
        {
            try
            {
                IResourceResponse <Document> response = await client.ReadDocumentAsync(documentUri).ConfigureAwait(false);

                return(response.Resource);
            }
            catch (DocumentClientException ex)
            {
                if (ex.StatusCode != HttpStatusCode.NotFound)
                {
                    throw;
                }
            }

            return(null);
        }
Exemplo n.º 12
0
        public void ItDeletesExpiredRecordsOnExistsCheck()
        {
            // Arrange
            const string ID_NEW = "new";
            const string ID_OLD = "old";
            IResourceResponse <Document> response1 = BuildResponseWithDocument(id: ID_NEW, expired: false);
            IResourceResponse <Document> response2 = BuildResponseWithDocument(id: ID_OLD, expired: true);

            this.cosmosDbSql.Setup(x => x.ReadAsync(It.IsAny <IDocumentClient>(), this.storageConfig, ID_NEW))
            .ReturnsAsync(response1);
            this.cosmosDbSql.Setup(x => x.ReadAsync(It.IsAny <IDocumentClient>(), this.storageConfig, ID_OLD))
            .ReturnsAsync(response2);

            // Act
            this.target.ExistsAsync(ID_NEW).CompleteOrTimeout();
            this.target.ExistsAsync(ID_OLD).CompleteOrTimeout();

            // Assert
            this.cosmosDbSql.Verify(x => x.DeleteAsync(this.cosmosDbSqlClient.Object, this.storageConfig, ID_NEW), Times.Never);
            this.cosmosDbSql.Verify(x => x.DeleteAsync(this.cosmosDbSqlClient.Object, this.storageConfig, ID_OLD), Times.Once);
        }
Exemplo n.º 13
0
        public void ItReturnsFalseWhenUnlockingRecordThatIsLockedByOther()
        {
            // Arrange
            var id        = Guid.NewGuid().ToString();
            var ownerId   = Guid.NewGuid().ToString();
            var ownerType = Guid.NewGuid().ToString();
            IResourceResponse <Document> record = BuildResponseWithDocument(
                id: id,
                lockOwnerId: ownerId,
                lockOwnerType: ownerType,
                lockExpirationUtcMsecs: Now + testOffsetMs,
                expired: false);

            this.cosmosDbSql.Setup(x => x.ReadAsync(It.IsAny <IDocumentClient>(), this.storageConfig, id))
            .ReturnsAsync(record);

            // Act
            bool result = this.target.TryToUnlockAsync(id, "wrongOwner", ownerType).CompleteOrTimeout().Result;

            // Assert
            Assert.False(result);
        }
Exemplo n.º 14
0
        private async Task <Document> TryReplaceLeaseAsync(ILease lease, Uri leaseUri)
        {
            try
            {
                IResourceResponse <Document> response = await this.client.ReplaceDocumentAsync(leaseUri, lease, this.CreateIfMatchOptions(lease)).ConfigureAwait(false);

                return(response.Resource);
            }
            catch (DocumentClientException ex) when(ex.StatusCode == HttpStatusCode.PreconditionFailed)
            {
                return(null);
            }
            catch (DocumentClientException ex)
            {
                Logger.WarnFormat("Lease operation exception, status code: ", ex.StatusCode);
                if (ex.StatusCode == HttpStatusCode.Conflict ||
                    ex.StatusCode == HttpStatusCode.NotFound)
                {
                    throw new LeaseLostException(lease, ex, ex.StatusCode == HttpStatusCode.NotFound);
                }

                throw;
            }
        }
Exemplo n.º 15
0
        public void ItCanCheckIfARecordExists()
        {
            // Arrange
            const string ID_NEW     = "new";
            const string ID_OLD     = "old";
            const string ID_MISSING = "xyz";
            IResourceResponse <Document> response1 = BuildResponseWithDocument(id: ID_NEW, expired: false);
            IResourceResponse <Document> response2 = BuildResponseWithDocument(id: ID_OLD, expired: true);

            this.cosmosDbSql.Setup(x => x.ReadAsync(It.IsAny <IDocumentClient>(), this.storageConfig, ID_NEW))
            .ReturnsAsync(response1);
            this.cosmosDbSql.Setup(x => x.ReadAsync(It.IsAny <IDocumentClient>(), this.storageConfig, ID_OLD))
            .ReturnsAsync(response2);

            // Act
            var result1 = this.target.ExistsAsync(ID_NEW).CompleteOrTimeout().Result;
            var result2 = this.target.ExistsAsync(ID_OLD).CompleteOrTimeout().Result;
            var result3 = this.target.ExistsAsync(ID_MISSING).CompleteOrTimeout().Result;

            // Assert
            Assert.True(result1);
            Assert.False(result2);
            Assert.False(result3);
        }
Exemplo n.º 16
0
        private T ParseResponseDocument(IResourceResponse <AzureDocument> response)
        {
            var document = JsonConvert.DeserializeObject <T>(response.Resource.ToString());

            return(document);
        }
Exemplo n.º 17
0
 /// <summary>
 /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
 /// </summary>
 /// <filterpriority>2</filterpriority>
 public void Dispose()
 {
     Config   = null;
     Response = null;
 }