public WorkerData(Task task, IChangeFeedObserver observer, ChangeFeedObserverContext context, CancellationTokenSource cancellation)
 {
     this.Task         = task;
     this.Observer     = observer;
     this.Context      = context;
     this.Cancellation = cancellation;
 }
        public FeedProcessing.IChangeFeedObserver CreateObserver()
        {
#pragma warning disable CS0618 // Type or member is obsolete
            IChangeFeedObserver observer = this.factory.CreateObserver();
#pragma warning restore CS0618 // Type or member is obsolete
            return(new ChangeFeedObserverAdapter(observer));
        }
Exemple #3
0
        public IPartitionProcessor Create(ILease lease, IChangeFeedObserver observer)
        {
            if (observer == null)
            {
                throw new ArgumentNullException(nameof(observer));
            }
            if (lease == null)
            {
                throw new ArgumentNullException(nameof(lease));
            }

            var settings = new ProcessorSettings
            {
                CollectionSelfLink = this.collectionSelfLink,
                StartContinuation  = !string.IsNullOrEmpty(lease.ContinuationToken) ?
                                     lease.ContinuationToken :
                                     this.changeFeedProcessorOptions.StartContinuation,
                PartitionKeyRangeId = lease.PartitionId,
                FeedPollDelay       = this.changeFeedProcessorOptions.FeedPollDelay,
                MaxItemCount        = this.changeFeedProcessorOptions.MaxItemCount,
                StartFromBeginning  = this.changeFeedProcessorOptions.StartFromBeginning,
                StartTime           = this.changeFeedProcessorOptions.StartTime,
                SessionToken        = this.changeFeedProcessorOptions.SessionToken,
            };

            var checkpointer = new PartitionCheckpointer(this.leaseCheckpointer, lease);

            return(new PartitionProcessor(observer, this.documentClient, settings, checkpointer));
        }
        public PartitionControllerTests()
        {
            lease = Mock.Of <ILease>();
            Mock.Get(lease)
            .Setup(l => l.PartitionId)
            .Returns("partitionId");

            partitionProcessor         = MockPartitionProcessor();
            leaseRenewer               = MockRenewer();
            observer                   = MockObserver();
            partitionSupervisorFactory = Mock.Of <IPartitionSupervisorFactory>(f => f.Create(lease) == new PartitionSupervisor(lease, observer, partitionProcessor, leaseRenewer));

            leaseManager = Mock.Of <ILeaseManager>();
            Mock.Get(leaseManager).Reset(); // Reset implicit/by default setup of properties.
            Mock.Get(leaseManager)
            .Setup(manager => manager.AcquireAsync(lease))
            .ReturnsAsync(lease);
            Mock.Get(leaseManager)
            .Setup(manager => manager.ReleaseAsync(lease))
            .Returns(Task.CompletedTask);
            var leaseContainer = Mock.Of <ILeaseContainer>();

            synchronizer = Mock.Of <IPartitionSynchronizer>();
            sut          = new PartitionController(leaseContainer, leaseManager, partitionSupervisorFactory, synchronizer);
        }
Exemple #5
0
        public AutoCheckPointTests()
        {
            changeFeedObserver    = Mock.Of <IChangeFeedObserver>();
            partitionCheckpointer = Mock.Of <IPartitionCheckpointer>();
            Mock.Get(partitionCheckpointer)
            .Setup(checkPointer => checkPointer.CheckpointPartitionAsync(It.IsAny <string>()))
            .Returns(Task.CompletedTask);

            checkpointFrequency = new CheckpointFrequency();
            sut = new AutoCheckpointer(checkpointFrequency, changeFeedObserver);

            documents    = Mock.Of <IReadOnlyList <Document> >();
            feedResponse = Mock.Of <IFeedResponse <Document> >();
            Mock.Get(feedResponse)
            .Setup(response => response.Count)
            .Returns(documents.Count);
            Mock.Get(feedResponse)
            .Setup(response => response.ResponseContinuation)
            .Returns("token");
            Mock.Get(feedResponse)
            .Setup(response => response.GetEnumerator())
            .Returns(documents.GetEnumerator());

            observerContext = Mock.Of <IChangeFeedObserverContext>();
            Mock.Get(observerContext)
            .Setup(context => context.CheckpointAsync())
            .Returns(partitionCheckpointer.CheckpointPartitionAsync("token"));
        }
 public PartitionSupervisor(ILease lease, IChangeFeedObserver observer, IPartitionProcessor processor, ILeaseRenewer renewer)
 {
     this.lease     = lease;
     this.observer  = observer;
     this.processor = processor;
     this.renewer   = renewer;
 }
 public PartitionProcessor(IChangeFeedObserver observer, IChangeFeedDocumentQuery <Document> query, ChangeFeedOptions options, ProcessorSettings settings, IPartitionCheckpointer checkpointer)
 {
     this.observer     = observer;
     this.settings     = settings;
     this.checkpointer = checkpointer;
     this.options      = options;
     this.query        = query;
 }
        public PartitionProcessorFactoryTests()
        {
            this.collectionSelfLink = this.fixture.Create <string>();
            this.docClient          = Mock.Of <IChangeFeedDocumentClient>();
            Mock.Get(this.docClient)
            .Setup(ex => ex.CreateDocumentChangeFeedQuery(this.collectionSelfLink, It.IsAny <ChangeFeedOptions>()))
            .Returns(Mock.Of <IChangeFeedDocumentQuery <Document> >());

            this.observer = Mock.Of <IChangeFeedObserver>();
        }
Exemple #9
0
        /// <summary>
        /// Creates a new instance of <see cref="Documents.ChangeFeedProcessor.IChangeFeedObserver"/>.
        /// </summary>
        /// <returns>Created instance of <see cref="Documents.ChangeFeedProcessor.IChangeFeedObserver"/>.</returns>
        public IChangeFeedObserver CreateObserver()
        {
            IChangeFeedObserver observer = this.observerFactory.CreateObserver();

            if (this.checkpointFrequency.ExplicitCheckpoint)
            {
                return(observer);
            }

            return(new AutoCheckpointer(this.checkpointFrequency, observer));
        }
Exemple #10
0
        public ObserverExceptionWrappingChangeFeedObserverDecoratorTests()
        {
            this.observer = Mock.Of <IChangeFeedObserver>();
            this.changeFeedObserverContext = Mock.Of <IChangeFeedObserverContext>();
            this.observerWrapper           = new FeedProcessing.ObserverExceptionWrappingChangeFeedObserverDecorator(observer);

            var document = new Document();

            documents = new List <Document> {
                document
            };
        }
        public IPartitionSupervisor Create(ILease lease)
        {
            if (lease == null)
            {
                throw new ArgumentNullException(nameof(lease));
            }

            IChangeFeedObserver changeFeedObserver = this.observerFactory.CreateObserver();
            var processor = this.partitionProcessorFactory.Create(lease, this.leaseCheckpointer, changeFeedObserver);
            var renewer   = new LeaseRenewer(lease, this.leaseManager, this.changeFeedProcessorOptions.LeaseRenewInterval);

            return(new PartitionSupervisor(lease, changeFeedObserver, processor, renewer));
        }
        public PartitionSupervisorTests()
        {
            lease = Mock.Of <ILease>();
            Mock.Get(lease)
            .Setup(l => l.PartitionId)
            .Returns("partitionId");

            leaseRenewer       = Mock.Of <ILeaseRenewer>();
            partitionProcessor = Mock.Of <IPartitionProcessor>();
            observer           = Mock.Of <IChangeFeedObserver>();

            sut = new PartitionSupervisor(lease, observer, partitionProcessor, leaseRenewer);
        }
            public WorkerData(Task task, IChangeFeedObserver observer, ChangeFeedObserverContext context, CancellationTokenSource cancellation, DocumentServiceLease lease)
            {
                Debug.Assert(task != null);
                Debug.Assert(observer != null);
                Debug.Assert(context != null);
                Debug.Assert(cancellation != null);

                this.Task         = task;
                this.Observer     = observer;
                this.Context      = context;
                this.Cancellation = cancellation;
                this.Lease        = lease;
            }
Exemple #14
0
        public AutoCheckpointer(CheckpointFrequency checkpointFrequency, IChangeFeedObserver observer)
        {
            if (checkpointFrequency == null)
            {
                throw new ArgumentNullException(nameof(checkpointFrequency));
            }
            if (observer == null)
            {
                throw new ArgumentNullException(nameof(observer));
            }

            this.checkpointFrequency = checkpointFrequency;
            this.observer            = observer;
        }
Exemple #15
0
        public PartitionExceptionsTests()
        {
            processorSettings = new ProcessorSettings
            {
                CollectionSelfLink  = "selfLink",
                FeedPollDelay       = TimeSpan.FromMilliseconds(16),
                MaxItemCount        = 5,
                PartitionKeyRangeId = "keyRangeId",
                RequestContinuation = "initialToken"
            };

            var document = new Document();

            documents = new List <Document> {
                document
            };

            feedResponse = Mock.Of <IFeedResponse <Document> >();
            Mock.Get(feedResponse)
            .Setup(response => response.Count)
            .Returns(documents.Count);
            Mock.Get(feedResponse)
            .Setup(response => response.ResponseContinuation)
            .Returns("token");
            Mock.Get(feedResponse)
            .Setup(response => response.GetEnumerator())
            .Returns(documents.GetEnumerator());

            documentQuery = Mock.Of <IChangeFeedDocumentQuery <Document> >();
            Mock.Get(documentQuery)
            .Setup(query => query.HasMoreResults)
            .Returns(false);

            docClient = Mock.Of <IChangeFeedDocumentClient>();
            Mock.Get(docClient)
            .Setup(ex => ex.CreateDocumentChangeFeedQuery(processorSettings.CollectionSelfLink, It.IsAny <ChangeFeedOptions>()))
            .Returns(documentQuery);

            observer = Mock.Of <IChangeFeedObserver>();
            Mock.Get(observer)
            .Setup(feedObserver => feedObserver
                   .ProcessChangesAsync(It.IsAny <ChangeFeedObserverContext>(), It.IsAny <IReadOnlyList <Document> >(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(false))
            .Callback(cancellationTokenSource.Cancel);

            var checkPointer = new Mock <IPartitionCheckpointer>();

            partitionProcessor = new PartitionProcessor(observer, docClient, processorSettings, checkPointer.Object);
        }
Exemple #16
0
        public PartitionProcessorTests()
        {
            processorSettings = new ProcessorSettings
            {
                CollectionSelfLink  = "selfLink",
                FeedPollDelay       = TimeSpan.FromMilliseconds(16),
                MaxItemCount        = 5,
                PartitionKeyRangeId = "keyRangeId",
                StartContinuation   = "initialToken"
            };

            var document = new Document();

            documents = new List <Document> {
                document
            };

            feedResponse = Mock.Of <IFeedResponse <Document> >();
            Mock.Get(feedResponse)
            .Setup(response => response.Count)
            .Returns(documents.Count);
            Mock.Get(feedResponse)
            .Setup(response => response.ResponseContinuation)
            .Returns("token");
            Mock.Get(feedResponse)
            .Setup(response => response.GetEnumerator())
            .Returns(documents.GetEnumerator());

            documentQuery = Mock.Of <IChangeFeedDocumentQuery <Document> >();
            Mock.Get(documentQuery)
            .Setup(query => query.HasMoreResults)
            .Returns(false);

            Mock.Get(documentQuery)
            .Setup(query => query.ExecuteNextAsync <Document>(It.Is <CancellationToken>(token => token == cancellationTokenSource.Token)))
            .ReturnsAsync(() => feedResponse)
            .Callback(() => cancellationTokenSource.Cancel());

            observer = Mock.Of <IChangeFeedObserver>();
            var checkPointer = new Mock <IPartitionCheckpointer>();

            sut = new PartitionProcessor(
                new ObserverExceptionWrappingChangeFeedObserverDecorator(observer),
                documentQuery,
                new ChangeFeedOptions(),
                processorSettings,
                checkPointer.Object);
        }
Exemple #17
0
        public PartitionProcessor(IChangeFeedObserver observer, IChangeFeedDocumentClient documentClient, ProcessorSettings settings, IPartitionCheckpointer checkpointer)
        {
            this.observer     = observer;
            this.settings     = settings;
            this.checkpointer = checkpointer;
            this.options      = new ChangeFeedOptions
            {
                MaxItemCount        = settings.MaxItemCount,
                PartitionKeyRangeId = settings.PartitionKeyRangeId,
                SessionToken        = settings.SessionToken,
                StartFromBeginning  = settings.StartFromBeginning,
                RequestContinuation = settings.RequestContinuation,
                StartTime           = settings.StartTime,
            };

            this.query = documentClient.CreateDocumentChangeFeedQuery(settings.CollectionSelfLink, this.options);
        }
        public PartitionControllerTests()
        {
            lease = Mock.Of <ILease>();
            Mock.Get(lease)
            .Setup(l => l.PartitionId)
            .Returns("partitionId");

            partitionProcessor         = MockPartitionProcessor();
            leaseRenewer               = MockRenewer();
            observer                   = MockObserver();
            partitionSupervisorFactory = Mock.Of <IPartitionSupervisorFactory>(f => f.Create(lease) == new PartitionSupervisor(lease, observer, partitionProcessor, leaseRenewer));

            leaseManager = Mock.Of <ILeaseManager>();
            Mock.Get(leaseManager)
            .Setup(manager => manager.AcquireAsync(lease))
            .ReturnsAsync(lease);

            Mock.Get(leaseManager)
            .Setup(manager => manager.ReleaseAsync(lease))
            .Returns(Task.FromResult(false));

            synchronizer = Mock.Of <IPartitionSynchronizer>();
            sut          = new PartitionController(leaseManager, partitionSupervisorFactory, synchronizer);
        }
Exemple #19
0
 public TestObserver(IChangeFeedObserver parent)
 {
     Debug.Assert(parent != null);
     this.parent = parent;
 }
Exemple #20
0
        public PartitionChainingTests()
        {
            processorSettings = new ProcessorSettings
            {
                CollectionSelfLink  = "selfLink",
                MaxItemCount        = 5,
                FeedPollDelay       = TimeSpan.FromMilliseconds(16),
                PartitionKeyRangeId = "keyRangeId",
                RequestContinuation = "initialToken"
            };

            var document = new Document();

            batch1 = new List <Document> {
                document
            };

            document = new Document();
            batch2   = new List <Document> {
                document
            };

            feedResponse1 = Mock.Of <IFeedResponse <Document> >();
            Mock.Get(feedResponse1)
            .Setup(response => response.Count)
            .Returns(batch1.Count);
            Mock.Get(feedResponse1)
            .SetupSequence(response => response.ResponseContinuation)
            .Returns("token1");
            Mock.Get(feedResponse1)
            .SetupSequence(response => response.GetEnumerator())
            .Returns(batch1.GetEnumerator());

            feedResponse2 = Mock.Of <IFeedResponse <Document> >();
            Mock.Get(feedResponse2)
            .Setup(response => response.Count)
            .Returns(batch2.Count);
            Mock.Get(feedResponse2)
            .SetupSequence(response => response.ResponseContinuation)
            .Returns("token2");
            Mock.Get(feedResponse2)
            .SetupSequence(response => response.GetEnumerator())
            .Returns(batch2.GetEnumerator());

            documentQuery = Mock.Of <IChangeFeedDocumentQuery <Document> >();
            Mock.Get(documentQuery)
            .SetupSequence(query => query.HasMoreResults)
            .Returns(true)
            .Returns(false);

            Mock.Get(documentQuery)
            .SetupSequence(query => query.ExecuteNextAsync <Document>(It.Is <CancellationToken>(token => token == cancellationTokenSource.Token)))
            .Returns(Task.FromResult(feedResponse1))
            .Returns(Task.FromResult(feedResponse2));

            docClient = Mock.Of <IChangeFeedDocumentClient>();
            Mock.Get(docClient)
            .Setup(ex => ex.CreateDocumentChangeFeedQuery(processorSettings.CollectionSelfLink, It.IsAny <ChangeFeedOptions>()))
            .Returns(documentQuery);

            observer = Mock.Of <IChangeFeedObserver>();
            var checkPointer = new Mock <IPartitionCheckpointer>();

            partitionProcessor = new PartitionProcessor(observer, docClient, processorSettings, checkPointer.Object);

            var i = 0;

            Mock.Get(observer)
            .Setup(feedObserver => feedObserver
                   .ProcessChangesAsync(It.IsAny <ChangeFeedObserverContext>(), It.IsAny <IReadOnlyList <Document> >(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(false))
            .Callback(() =>
            {
                if (++i == 2)
                {
                    cancellationTokenSource.Cancel();
                }
            });
        }
        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);
        }
        async Task IPartitionObserver <DocumentServiceLease> .OnPartitionAcquiredAsync(DocumentServiceLease lease)
        {
            Debug.Assert(lease != null && !string.IsNullOrEmpty(lease.Owner), "lease");
            TraceLog.Informational(string.Format("Host '{0}' partition {1}: acquired!", this.HostName, lease.PartitionId));

#if DEBUG
            Interlocked.Increment(ref this.partitionCount);
#endif

            IChangeFeedObserver       observer = this.observerFactory.CreateObserver();
            ChangeFeedObserverContext context  = new ChangeFeedObserverContext {
                PartitionKeyRangeId = lease.PartitionId
            };
            CancellationTokenSource cancellation = new CancellationTokenSource();

            // Create ChangeFeedOptions to use for this worker.
            ChangeFeedOptions options = new ChangeFeedOptions
            {
                MaxItemCount        = this.changeFeedOptions.MaxItemCount,
                PartitionKeyRangeId = this.changeFeedOptions.PartitionKeyRangeId,
                SessionToken        = this.changeFeedOptions.SessionToken,
                StartFromBeginning  = this.changeFeedOptions.StartFromBeginning,
                RequestContinuation = this.changeFeedOptions.RequestContinuation
            };

            var workerTask = await Task.Factory.StartNew(async() =>
            {
                ChangeFeedObserverCloseReason?closeReason = null;
                try
                {
                    try
                    {
                        await observer.OpenAsync(context);
                    }
                    catch (Exception ex)
                    {
                        TraceLog.Error(string.Format("IChangeFeedObserver.OpenAsync exception: {0}", ex));
                        closeReason = ChangeFeedObserverCloseReason.ObserverError;
                        throw;
                    }

                    options.PartitionKeyRangeId = lease.PartitionId;
                    if (!string.IsNullOrEmpty(lease.ContinuationToken))
                    {
                        options.RequestContinuation = lease.ContinuationToken;
                    }

                    CheckpointStats checkpointStats = null;
                    if (!this.statsSinceLastCheckpoint.TryGetValue(lease.PartitionId, out checkpointStats) || checkpointStats == null)
                    {
                        // It could be that the lease was created by different host and we picked it up.
                        checkpointStats = this.statsSinceLastCheckpoint.AddOrUpdate(
                            lease.PartitionId,
                            new CheckpointStats(),
                            (partitionId, existingStats) => existingStats);
                        Trace.TraceWarning(string.Format("Added stats for partition '{0}' for which the lease was picked up after the host was started.", lease.PartitionId));
                    }

                    IDocumentQuery <Document> query = this.documentClient.CreateDocumentChangeFeedQuery(this.collectionSelfLink, options);

                    TraceLog.Verbose(string.Format("Worker start: partition '{0}', continuation '{1}'", lease.PartitionId, lease.ContinuationToken));

                    string lastContinuation = options.RequestContinuation;

                    try
                    {
                        while (this.isShutdown == 0)
                        {
                            do
                            {
                                ExceptionDispatchInfo exceptionDispatchInfo = null;
                                FeedResponse <Document> response            = null;

                                try
                                {
                                    response         = await query.ExecuteNextAsync <Document>();
                                    lastContinuation = response.ResponseContinuation;
                                }
                                catch (DocumentClientException ex)
                                {
                                    exceptionDispatchInfo = ExceptionDispatchInfo.Capture(ex);
                                }

                                if (exceptionDispatchInfo != null)
                                {
                                    DocumentClientException dcex = (DocumentClientException)exceptionDispatchInfo.SourceException;

                                    if (StatusCode.NotFound == (StatusCode)dcex.StatusCode && SubStatusCode.ReadSessionNotAvailable != (SubStatusCode)GetSubStatusCode(dcex))
                                    {
                                        // Most likely, the database or collection was removed while we were enumerating.
                                        // Shut down. The user will need to start over.
                                        // Note: this has to be a new task, can't await for shutdown here, as shudown awaits for all worker tasks.
                                        TraceLog.Error(string.Format("Partition {0}: resource gone (subStatus={1}). Aborting.", context.PartitionKeyRangeId, GetSubStatusCode(dcex)));
                                        await Task.Factory.StartNew(() => this.StopAsync(ChangeFeedObserverCloseReason.ResourceGone));
                                        break;
                                    }
                                    else if (StatusCode.Gone == (StatusCode)dcex.StatusCode)
                                    {
                                        SubStatusCode subStatusCode = (SubStatusCode)GetSubStatusCode(dcex);
                                        if (SubStatusCode.PartitionKeyRangeGone == subStatusCode)
                                        {
                                            bool isSuccess = await HandleSplitAsync(context.PartitionKeyRangeId, lastContinuation, lease.Id);
                                            if (!isSuccess)
                                            {
                                                TraceLog.Error(string.Format("Partition {0}: HandleSplit failed! Aborting.", context.PartitionKeyRangeId));
                                                await Task.Factory.StartNew(() => this.StopAsync(ChangeFeedObserverCloseReason.ResourceGone));
                                                break;
                                            }

                                            // Throw LeaseLostException so that we take the lease down.
                                            throw new LeaseLostException(lease, exceptionDispatchInfo.SourceException, true);
                                        }
                                        else if (SubStatusCode.Splitting == subStatusCode)
                                        {
                                            TraceLog.Warning(string.Format("Partition {0} is splitting. Will retry to read changes until split finishes. {1}", context.PartitionKeyRangeId, dcex.Message));
                                        }
                                        else
                                        {
                                            exceptionDispatchInfo.Throw();
                                        }
                                    }
                                    else if (StatusCode.TooManyRequests == (StatusCode)dcex.StatusCode ||
                                             StatusCode.ServiceUnavailable == (StatusCode)dcex.StatusCode)
                                    {
                                        TraceLog.Warning(string.Format("Partition {0}: retriable exception : {1}", context.PartitionKeyRangeId, dcex.Message));
                                    }
                                    else
                                    {
                                        exceptionDispatchInfo.Throw();
                                    }

                                    await Task.Delay(dcex.RetryAfter != TimeSpan.Zero ? dcex.RetryAfter : this.options.FeedPollDelay, cancellation.Token);
                                }

                                if (response != null)
                                {
                                    if (response.Count > 0)
                                    {
                                        List <Document> docs = new List <Document>();
                                        docs.AddRange(response);

                                        try
                                        {
                                            context.FeedResponse = response;
                                            await observer.ProcessChangesAsync(context, docs);
                                        }
                                        catch (Exception ex)
                                        {
                                            TraceLog.Error(string.Format("IChangeFeedObserver.ProcessChangesAsync exception: {0}", ex));
                                            closeReason = ChangeFeedObserverCloseReason.ObserverError;
                                            throw;
                                        }
                                        finally
                                        {
                                            context.FeedResponse = null;
                                        }
                                    }

                                    checkpointStats.ProcessedDocCount += (uint)response.Count;

                                    if (IsCheckpointNeeded(lease, checkpointStats))
                                    {
                                        lease = await CheckpointAsync(lease, response.ResponseContinuation, context);
                                        checkpointStats.Reset();
                                    }
                                    else if (response.Count > 0)
                                    {
                                        TraceLog.Informational(string.Format("Checkpoint: not checkpointing for partition {0}, {1} docs, new continuation '{2}' as frequency condition is not met", lease.PartitionId, response.Count, response.ResponseContinuation));
                                    }
                                }
                            }while (query.HasMoreResults && this.isShutdown == 0);

                            if (this.isShutdown == 0)
                            {
                                await Task.Delay(this.options.FeedPollDelay, cancellation.Token);
                            }
                        } // Outer while (this.isShutdown == 0) loop.

                        closeReason = ChangeFeedObserverCloseReason.Shutdown;
                    }
                    catch (TaskCanceledException)
                    {
                        Debug.Assert(cancellation.IsCancellationRequested, "cancellation.IsCancellationRequested");
                        TraceLog.Informational(string.Format("Cancel signal received for partition {0} worker!", context.PartitionKeyRangeId));
                    }
                }
                catch (LeaseLostException ex)
                {
                    closeReason = ex.IsGone ? ChangeFeedObserverCloseReason.LeaseGone : ChangeFeedObserverCloseReason.LeaseLost;
                }
                catch (Exception ex)
                {
                    TraceLog.Error(string.Format("Partition {0} exception: {1}", context.PartitionKeyRangeId, ex));
                    if (!closeReason.HasValue)
                    {
                        closeReason = ChangeFeedObserverCloseReason.Unknown;
                    }
                }

                if (closeReason.HasValue)
                {
                    TraceLog.Informational(string.Format("Releasing lease for partition {0} due to an error, reason: {1}!", context.PartitionKeyRangeId, closeReason.Value));

                    // Note: this has to be a new task, because OnPartitionReleasedAsync awaits for worker task.
                    await Task.Factory.StartNew(async() => await this.partitionManager.TryReleasePartitionAsync(context.PartitionKeyRangeId, true, closeReason.Value));
                }

                TraceLog.Informational(string.Format("Partition {0}: worker finished!", context.PartitionKeyRangeId));
            });

            var newWorkerData = new WorkerData(workerTask, observer, context, cancellation);
            this.partitionKeyRangeIdToWorkerMap.AddOrUpdate(context.PartitionKeyRangeId, newWorkerData, (string id, WorkerData d) => { return(newWorkerData); });
        }
Exemple #23
0
 public ObserverExceptionWrappingChangeFeedObserverDecorator(IChangeFeedObserver changeFeedObserver)
 {
     this.changeFeedObserver = changeFeedObserver;
 }
        async Task IPartitionObserver <DocumentServiceLease> .OnPartitionAcquiredAsync(DocumentServiceLease lease)
        {
            Debug.Assert(lease != null && !string.IsNullOrEmpty(lease.Owner), "lease");
            TraceLog.Informational(string.Format("Host '{0}' partition {1}: acquired!", this.HostName, lease.PartitionId));

#if DEBUG
            Interlocked.Increment(ref this.partitionCount);
#endif

            IChangeFeedObserver       observer = this.observerFactory.CreateObserver();
            ChangeFeedObserverContext context  = new ChangeFeedObserverContext {
                PartitionKeyRangeId = lease.PartitionId
            };
            CancellationTokenSource cancellation = new CancellationTokenSource();

            // Create ChangeFeedOptions to use for this worker.
            ChangeFeedOptions options = new ChangeFeedOptions
            {
                MaxItemCount        = this.changeFeedOptions.MaxItemCount,
                PartitionKeyRangeId = this.changeFeedOptions.PartitionKeyRangeId,
                SessionToken        = this.changeFeedOptions.SessionToken,
                StartFromBeginning  = this.changeFeedOptions.StartFromBeginning,
                RequestContinuation = this.changeFeedOptions.RequestContinuation
            };

            var workerTask = await Task.Factory.StartNew(async() =>
            {
                ChangeFeedObserverCloseReason?closeReason = null;
                try
                {
                    try
                    {
                        await observer.OpenAsync(context);
                    }
                    catch (Exception ex)
                    {
                        TraceLog.Error(string.Format("IChangeFeedObserver.OpenAsync exception: {0}", ex));
                        closeReason = ChangeFeedObserverCloseReason.ObserverError;
                        throw;
                    }

                    options.PartitionKeyRangeId = lease.PartitionId;
                    if (!string.IsNullOrEmpty(lease.ContinuationToken))
                    {
                        options.RequestContinuation = lease.ContinuationToken;
                    }

                    IDocumentQuery <Document> query = this.documentClient.CreateDocumentChangeFeedQuery(this.collectionSelfLink, options);

                    TraceLog.Verbose(string.Format("Worker start: partition '{0}', continuation '{1}'", lease.PartitionId, lease.ContinuationToken));

                    try
                    {
                        while (this.isShutdown == 0)
                        {
                            do
                            {
                                DocumentClientException dcex     = null;
                                FeedResponse <Document> response = null;

                                try
                                {
                                    response = await query.ExecuteNextAsync <Document>();
                                }
                                catch (DocumentClientException ex)
                                {
                                    if (StatusCode.NotFound != (StatusCode)ex.StatusCode &&
                                        StatusCode.TooManyRequests != (StatusCode)ex.StatusCode &&
                                        StatusCode.ServiceUnavailable != (StatusCode)ex.StatusCode)
                                    {
                                        throw;
                                    }

                                    dcex = ex;
                                }

                                if (dcex != null)
                                {
                                    const int ReadSessionNotAvailable = 1002;
                                    if (StatusCode.NotFound == (StatusCode)dcex.StatusCode && GetSubStatusCode(dcex) != ReadSessionNotAvailable)
                                    {
                                        // Most likely, the database or collection was removed while we were enumerating.
                                        // Shut down. The user will need to start over.
                                        // Note: this has to be a new task, can't await for shutdown here, as shudown awaits for all worker tasks.
                                        await Task.Factory.StartNew(() => this.StopAsync(ChangeFeedObserverCloseReason.ResourceGone));
                                        break;
                                    }
                                    else
                                    {
                                        Debug.Assert(StatusCode.TooManyRequests == (StatusCode)dcex.StatusCode || StatusCode.ServiceUnavailable == (StatusCode)dcex.StatusCode);
                                        TraceLog.Warning(string.Format("Partition {0}: retriable exception : {1}", context.PartitionKeyRangeId, dcex.Message));
                                        await Task.Delay(dcex.RetryAfter != TimeSpan.Zero ? dcex.RetryAfter : this.options.FeedPollDelay, cancellation.Token);
                                    }
                                }

                                if (response != null)
                                {
                                    if (response.Count > 0)
                                    {
                                        List <Document> docs = new List <Document>();
                                        docs.AddRange(response);

                                        try
                                        {
                                            await observer.ProcessChangesAsync(context, docs);
                                        }
                                        catch (Exception ex)
                                        {
                                            TraceLog.Error(string.Format("IChangeFeedObserver.ProcessChangesAsync exception: {0}", ex));
                                            closeReason = ChangeFeedObserverCloseReason.ObserverError;
                                            throw;
                                        }

                                        // Checkpoint after every successful delivery to the client.
                                        lease = await CheckpointAsync(lease, response.ResponseContinuation, context);
                                    }
                                    else if (string.IsNullOrEmpty(lease.ContinuationToken))
                                    {
                                        // Checkpoint if we've never done that for this lease.
                                        lease = await CheckpointAsync(lease, response.ResponseContinuation, context);
                                    }
                                }
                            }while (query.HasMoreResults && this.isShutdown == 0);

                            if (this.isShutdown == 0)
                            {
                                await Task.Delay(this.options.FeedPollDelay, cancellation.Token);
                            }
                        } // Outer while (this.isShutdown == 0) loop.
                    }
                    catch (TaskCanceledException)
                    {
                        Debug.Assert(cancellation.IsCancellationRequested, "cancellation.IsCancellationRequested");
                        TraceLog.Informational(string.Format("Cancel signal received for partition {0} worker!", context.PartitionKeyRangeId));
                    }
                }
                catch (LeaseLostException)
                {
                    closeReason = ChangeFeedObserverCloseReason.LeaseLost;
                }
                catch (Exception ex)
                {
                    TraceLog.Error(string.Format("Partition {0} exception: {1}", context.PartitionKeyRangeId, ex));
                    if (!closeReason.HasValue)
                    {
                        closeReason = ChangeFeedObserverCloseReason.Unknown;
                    }
                }

                if (closeReason.HasValue)
                {
                    TraceLog.Informational(string.Format("Releasing lease for partition {0} due to an error, reason: {1}!", context.PartitionKeyRangeId, closeReason.Value));

                    // Note: this has to be a new task, because OnPartitionReleasedAsync awaits for worker task.
                    await Task.Factory.StartNew(async() => await this.partitionManager.TryReleasePartitionAsync(context.PartitionKeyRangeId, true, closeReason.Value));
                }

                TraceLog.Informational(string.Format("Partition {0}: worker finished!", context.PartitionKeyRangeId));
            });

            var newWorkerData = new WorkerData(workerTask, observer, context, cancellation);
            this.partitionKeyRangeIdToWorkerMap.AddOrUpdate(context.PartitionKeyRangeId, newWorkerData, (string id, WorkerData d) => { return(newWorkerData); });
        }
Exemple #25
0
        public IPartitionProcessor Create(ILease lease, ILeaseCheckpointer leaseCheckpointer, IChangeFeedObserver observer)
        {
            if (observer == null)
            {
                throw new ArgumentNullException(nameof(observer));
            }
            if (lease == null)
            {
                throw new ArgumentNullException(nameof(lease));
            }
            if (leaseCheckpointer == null)
            {
                throw new ArgumentNullException(nameof(leaseCheckpointer));
            }

            var settings = new ProcessorSettings
            {
                CollectionSelfLink = this.collectionSelfLink,
                StartContinuation  = !string.IsNullOrEmpty(lease.ContinuationToken) ?
                                     lease.ContinuationToken :
                                     this.changeFeedProcessorOptions.StartContinuation,
                PartitionKeyRangeId = lease.PartitionId,
                FeedPollDelay       = this.changeFeedProcessorOptions.FeedPollDelay,
                MaxItemCount        = this.changeFeedProcessorOptions.MaxItemCount,
                StartFromBeginning  = this.changeFeedProcessorOptions.StartFromBeginning,
                StartTime           = this.changeFeedProcessorOptions.StartTime,
                SessionToken        = this.changeFeedProcessorOptions.SessionToken,
            };

            var checkpointer = new PartitionCheckpointer(leaseCheckpointer, lease);

            var changeFeedOptions = new ChangeFeedOptions
            {
                MaxItemCount        = settings.MaxItemCount,
                PartitionKeyRangeId = settings.PartitionKeyRangeId,
                SessionToken        = settings.SessionToken,
                StartFromBeginning  = settings.StartFromBeginning,
                RequestContinuation = settings.StartContinuation,
                StartTime           = settings.StartTime,
            };
            var changeFeedQuery = this.documentClient.CreateDocumentChangeFeedQuery(settings.CollectionSelfLink, changeFeedOptions);

            changeFeedQuery = new ChangeFeedQueryTimeoutDecorator(changeFeedQuery, this.healthMonitor, this.changeFeedProcessorOptions.ChangeFeedTimeout, lease);

            return(new PartitionProcessor(observer, changeFeedQuery, changeFeedOptions, settings, checkpointer));
        }
Exemple #26
0
 public IPartitionProcessor Create(ILease lease, ILeaseCheckpointer leaseCheckpointer, IChangeFeedObserver observer)
 {
     return(this.partitionProcessorFactory.Create(lease, observer));
 }