/// <summary> /// Builds a new instance of the <see cref="IChangeFeedProcessor"/> with the specified configuration. /// </summary> /// <returns>An instance of <see cref="IChangeFeedProcessor"/>.</returns> public async Task <IChangeFeedProcessor> BuildAsync() { if (this.HostName == null) { throw new InvalidOperationException("Host name was not specified"); } if (this.feedCollectionLocation == null) { throw new InvalidOperationException(nameof(this.feedCollectionLocation) + " was not specified"); } if (this.leaseCollectionLocation == null && this.LeaseStoreManager == null) { throw new InvalidOperationException($"Either {nameof(this.leaseCollectionLocation)} or {nameof(this.LeaseStoreManager)} must be specified"); } if (this.observerFactory == null) { throw new InvalidOperationException("Observer was not specified"); } await this.InitializeCollectionPropertiesForBuildAsync().ConfigureAwait(false); ILeaseStoreManager leaseStoreManager = await this.GetLeaseStoreManagerAsync(this.leaseCollectionLocation, true).ConfigureAwait(false); IPartitionManager partitionManager = this.BuildPartitionManager(leaseStoreManager); return(new ChangeFeedProcessor(partitionManager)); }
/// <summary> /// Sets the <see cref="ILeaseStoreManager"/> to be used to manage leases. /// </summary> /// <param name="leaseStoreManager">The instance of <see cref="ILeaseStoreManager"/> to use.</param> /// <returns>The instance of <see cref="ChangeFeedProcessorBuilder"/> to use.</returns> public ChangeFeedProcessorBuilder WithLeaseStoreManager(ILeaseStoreManager leaseStoreManager) { if (leaseStoreManager == null) { throw new ArgumentNullException(nameof(leaseStoreManager)); } this.LeaseStoreManager = leaseStoreManager; return(this); }
private IPartitionManager BuildPartitionManager(ILeaseStoreManager leaseStoreManager) { string feedCollectionSelfLink = this.feedCollectionLocation.GetCollectionSelfLink(); var factory = new CheckpointerObserverFactory(this.observerFactory, this.changeFeedProcessorOptions.CheckpointFrequency); var synchronizer = new PartitionSynchronizer( this.feedDocumentClient, feedCollectionSelfLink, leaseStoreManager, leaseStoreManager, this.changeFeedProcessorOptions.DegreeOfParallelism, this.changeFeedProcessorOptions.QueryPartitionsMaxBatchSize); var bootstrapper = new Bootstrapper(synchronizer, leaseStoreManager, this.lockTime, this.sleepTime); var partitionSuperviserFactory = new PartitionSupervisorFactory( factory, leaseStoreManager, this.partitionProcessorFactory ?? new PartitionProcessorFactory(this.feedDocumentClient, this.changeFeedProcessorOptions, leaseStoreManager, feedCollectionSelfLink), this.changeFeedProcessorOptions); if (this.loadBalancingStrategy == null) { this.loadBalancingStrategy = new EqualPartitionsBalancingStrategy( this.HostName, this.changeFeedProcessorOptions.MinPartitionCount, this.changeFeedProcessorOptions.MaxPartitionCount, this.changeFeedProcessorOptions.LeaseExpirationInterval); } IPartitionController partitionController = new PartitionController(leaseStoreManager, leaseStoreManager, partitionSuperviserFactory, synchronizer); if (this.healthMonitor == null) { this.healthMonitor = new TraceHealthMonitor(); } partitionController = new HealthMonitoringPartitionControllerDecorator(partitionController, this.healthMonitor); var partitionLoadBalancer = new PartitionLoadBalancer( partitionController, leaseStoreManager, this.loadBalancingStrategy, this.changeFeedProcessorOptions.LeaseAcquireInterval); return(new PartitionManager(bootstrapper, partitionController, partitionLoadBalancer)); }
private async Task <ILeaseStoreManager> GetLeaseStoreManagerAsync( DocumentCollectionInfo collectionInfo, bool isPartitionKeyByIdRequiredIfPartitioned) { if (this.LeaseStoreManager == null) { var leaseDocumentClient = this.leaseDocumentClient ?? this.leaseCollectionLocation.CreateDocumentClient(); var collection = await leaseDocumentClient.GetDocumentCollectionAsync(collectionInfo).ConfigureAwait(false); bool isPartitioned = collection.PartitionKey != null && collection.PartitionKey.Paths != null && collection.PartitionKey.Paths.Count > 0; if (isPartitioned && isPartitionKeyByIdRequiredIfPartitioned && (collection.PartitionKey.Paths.Count != 1 || collection.PartitionKey.Paths[0] != "/id")) { throw new ArgumentException("The lease collection, if partitioned, must have partition key equal to id."); } var requestOptionsFactory = isPartitioned ? (IRequestOptionsFactory) new PartitionedByIdCollectionRequestOptionsFactory() : (IRequestOptionsFactory) new SinglePartitionRequestOptionsFactory(); string leasePrefix = this.GetLeasePrefix(); var leaseStoreManagerBuilder = new DocumentServiceLeaseStoreManagerBuilder() .WithLeasePrefix(leasePrefix) .WithLeaseCollection(this.leaseCollectionLocation) .WithLeaseCollectionLink(collection.SelfLink) .WithRequestOptionsFactory(requestOptionsFactory) .WithHostName(this.HostName); leaseStoreManagerBuilder = leaseStoreManagerBuilder.WithLeaseDocumentClient(leaseDocumentClient); this.LeaseStoreManager = await leaseStoreManagerBuilder.BuildAsync().ConfigureAwait(false); } return(this.LeaseStoreManager); }
public ChangeFeedProcessorTests() { var leaseQueryMock = new Mock <IDocumentQuery <Document> >(); var leaseDocumentClient = Mock.Of <IChangeFeedDocumentClient>(); Mock.Get(leaseDocumentClient) .Setup(c => c.CreateDocumentQuery <Document>(collectionLink, It.Is <SqlQuerySpec>(spec => spec.QueryText == "SELECT * FROM c WHERE STARTSWITH(c.id, @PartitionLeasePrefix)" && spec.Parameters.Count == 1 && spec.Parameters[0].Name == "@PartitionLeasePrefix" && (string)spec.Parameters[0].Value == storeNamePrefix + ".." ), null)) .Returns(leaseQueryMock.As <IQueryable <Document> >().Object); leaseQueryMock .Setup(q => q.HasMoreResults) .Returns(false); Mock.Get(leaseDocumentClient) .Setup(ex => ex.ReadDatabaseAsync(It.IsAny <Uri>(), It.IsAny <RequestOptions>())) .ReturnsAsync(new ResourceResponse <Database>(database)); Mock.Get(leaseDocumentClient) .Setup(ex => ex.ReadDocumentCollectionAsync(It.IsAny <Uri>(), It.IsAny <RequestOptions>())) .ReturnsAsync(new ResourceResponse <DocumentCollection>(collection)); var documents = new List <Document> { }; var feedResponse = Mock.Of <IFeedResponse <Document> >(); Mock.Get(feedResponse) .Setup(response => response.Count) .Returns(documents.Count); Mock.Get(feedResponse) .Setup(response => response.GetEnumerator()) .Returns(documents.GetEnumerator()); var documentQuery = Mock.Of <IChangeFeedDocumentQuery <Document> >(); Mock.Get(documentQuery) .Setup(query => query.HasMoreResults) .Returns(false); Mock.Get(documentQuery) .Setup(query => query.ExecuteNextAsync <Document>(It.IsAny <CancellationToken>())) .ReturnsAsync(() => feedResponse) .Callback(cancellationTokenSource.Cancel); var documentClient = Mock.Of <IChangeFeedDocumentClient>(); Mock.Get(documentClient) .Setup(ex => ex.CreateDocumentChangeFeedQuery(It.IsAny <string>(), It.IsAny <ChangeFeedOptions>())) .Callback((string s, ChangeFeedOptions o) => this.createDocumentChangeFeedQueryCallback(s, o)) .Returns(documentQuery); Mock.Get(documentClient) .Setup(ex => ex.ReadDatabaseAsync(It.IsAny <Uri>(), It.IsAny <RequestOptions>())) .ReturnsAsync(new ResourceResponse <Database>(database)); Mock.Get(documentClient) .Setup(ex => ex.ReadDocumentCollectionAsync(It.IsAny <Uri>(), It.IsAny <RequestOptions>())) .ReturnsAsync(new ResourceResponse <DocumentCollection>(collection)); this.lease = Mock.Of <ILease>(); Mock.Get(this.lease) .Setup(l => l.PartitionId) .Returns("partitionId"); var leaseStore = Mock.Of <ILeaseStore>(); this.leaseStoreManager = Mock.Of <ILeaseStoreManager>(); Mock.Get(this.leaseStoreManager) .Setup(store => store.IsInitializedAsync()) .ReturnsAsync(true); Mock.Get(this.leaseStoreManager) .Setup(manager => manager.AcquireAsync(lease)) .ReturnsAsync(lease); Mock.Get(this.leaseStoreManager) .Setup(manager => manager.ReleaseAsync(lease)) .Returns(Task.CompletedTask); this.builder .WithHostName("someHost") .WithFeedDocumentClient(documentClient) .WithFeedCollection(collectionInfo) .WithProcessorOptions(new ChangeFeedProcessorOptions()) .WithLeaseStoreManager(leaseStoreManager) .WithLeaseCollection(collectionInfo) .WithLeaseDocumentClient(leaseDocumentClient); this.observer = Mock.Of <IChangeFeedObserver>(); Mock.Get(observer) .Setup(feedObserver => feedObserver .ProcessChangesAsync(It.IsAny <ChangeFeedObserverContext>(), It.IsAny <IReadOnlyList <Document> >(), It.IsAny <CancellationToken>())) .Returns(Task.CompletedTask) .Callback(cancellationTokenSource.Cancel); Mock.Get(observer) .Setup(observer => observer.OpenAsync(It.IsAny <ChangeFeedObserverContext>())) .Returns(Task.CompletedTask); this.observerFactory = Mock.Of <IChangeFeedObserverFactory>(); Mock.Get(observerFactory) .Setup(observer => observer.CreateObserver()) .Returns(observer); }