Esempio n. 1
0
        public async Task StartAsync()
        {
            var feedCollectionInfo = new DocumentCollectionInfo
            {
                DatabaseName   = _database,
                CollectionName = _eventContainer,
                Uri            = new Uri(_endpointUri),
                MasterKey      = _authKey
            };

            var leaseCollectionInfo = new DocumentCollectionInfo
            {
                DatabaseName   = _database,
                CollectionName = _leaseContainer,
                Uri            = new Uri(_endpointUri),
                MasterKey      = _authKey
            };

            var viewRepository = new CosmosDBViewRepository(_endpointUri, _authKey, _database);

            var builder = new ChangeFeedProcessorBuilder();

            _changeFeedProcessor = await builder
                                   .WithHostName("ProjectionHost")
                                   .WithFeedCollection(feedCollectionInfo)
                                   .WithLeaseCollection(leaseCollectionInfo)
                                   .WithObserverFactory(new EventObserverFactory(_projections, viewRepository))
                                   .WithProcessorOptions(new ChangeFeedProcessorOptions {
                StartFromBeginning = true
            })
                                   .BuildAsync();

            await _changeFeedProcessor.StartAsync();
        }
Esempio n. 2
0
        static async Task Main(string[] args)
        {
            log4net.Config.XmlConfigurator.Configure();

            // for using System.Diagnostics.TraceSource logging uncomment the line bellow
            // LogProvider.SetCurrentLogProvider(new TraceLogProvider());

            var dbUri          = "https://localhost:8081/";
            var key            = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
            var collectionName = "Input";

            await SetupEnvironmentAsync(dbUri, key, collectionName);

            CancellationTokenSource cts = new CancellationTokenSource();

            Task feedingTask = StartFeedingDataAsync(dbUri, key, collectionName, cts.Token);

            IChangeFeedProcessor processor = await RunChangeFeedProcessorAsync(dbUri, key, collectionName);

            Console.WriteLine("Running...[Press ENTER to stop]");
            Console.ReadLine();

            Console.WriteLine("Stopping...");
            cts.Cancel();
            await feedingTask.ConfigureAwait(false);

            await processor.StopAsync().ConfigureAwait(false);

            Console.WriteLine("Stopped");
            Console.ReadLine();
        }
Esempio n. 3
0
        static async Task Main(string[] args)
        {
            var dbUri          = "https://localhost:8081/";
            var key            = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
            var collectionName = "Input";

            await SetupEnvironmentAsync(dbUri, key, collectionName);

            CancellationTokenSource cts = new CancellationTokenSource();

            Task feedingTask    = StartFeedingDataAsync(dbUri, key, collectionName, cts.Token);
            Task monitoringTask = StartMonitoringAsync(dbUri, key, collectionName, cts.Token);

            IChangeFeedProcessor processor = await RunChangeFeedProcessorAsync(dbUri, key, collectionName);

            Console.WriteLine("Running...[Press ENTER to stop]");
            Console.ReadLine();

            Console.WriteLine("Stopping...");
            cts.Cancel();
            await feedingTask.ConfigureAwait(false);

            await processor.StopAsync().ConfigureAwait(false);

            await monitoringTask.ConfigureAwait(false);

            Console.WriteLine("Stopped");
            Console.ReadLine();
        }
Esempio n. 4
0
        private static async Task <IChangeFeedProcessor> StartChangeFeedProcessorAsync(
            string dataCollectionName,
            DocumentClient client,
            string dbName,
            string serverUri)
        {
            var leasesCollectionName = dataCollectionName + ".leases";
            await client.CreateDocumentCollectionIfNotExistsAsync(UriFactory.CreateDatabaseUri(dbName), new DocumentCollection { Id = leasesCollectionName });

            IChangeFeedProcessor processor = await new ChangeFeedProcessorBuilder()
                                             .WithObserver <ConsoleObserver>()
                                             .WithHostName("console_app_host")
                                             .WithFeedDocumentClient(client)
                                             .WithFeedCollection(new DocumentCollectionInfo
            {
                CollectionName = dataCollectionName,
                DatabaseName   = dbName,
                Uri            = new Uri(serverUri)
            })
                                             .WithLeaseDocumentClient(client)
                                             .WithLeaseCollection(new DocumentCollectionInfo
            {
                CollectionName = leasesCollectionName,
                DatabaseName   = dbName
            })
                                             .WithProcessorOptions(new ChangeFeedProcessorOptions()
            {
                StartFromBeginning = true
            })
                                             .BuildAsync();

            await processor.StartAsync().ConfigureAwait(false);

            return(processor);
        }
Esempio n. 5
0
        internal virtual async Task StartProcessorAsync()
        {
            if (this._host == null)
            {
                this._host = await this._hostBuilder.BuildAsync().ConfigureAwait(false);
            }

            await this._host.StartAsync().ConfigureAwait(false);
        }
Esempio n. 6
0
        private async Task CreateHost()
        {
            if (this.processor != null)
            {
                throw new Exception("Host was already initialized.");
            }

            this.processor = await this.builder.BuildAsync().ConfigureAwait(false);
        }
Esempio n. 7
0
        public IChangeFeedProcessor CreateOrGetChangeProcessorBuilder(string monitoredUri, string monitoredSecretKey, string monitoredDbName, string monitoredCollectionName, string leaseUri, string leaseSecretKey, string leaseDbName, string leaseCollectionName)
        {
            if (_changeFeedProcessor == null)
            {
                _changeFeedProcessor = CreateChangeFeedProcessor(monitoredUri, monitoredSecretKey, monitoredDbName, monitoredCollectionName, leaseUri, leaseSecretKey, leaseDbName, leaseCollectionName);
            }

            return(_changeFeedProcessor);
        }
Esempio n. 8
0
        internal virtual async Task StartProcessorAsync()
        {
            if (_host == null)
            {
                _host = await _hostBuilder.BuildAsync();
            }

            await _host.StartAsync();
        }
        public LoggingChangeFeedProcessor(
            IChangeFeedProcessor changeFeedProcessor,
            ILogger <LoggingChangeFeedProcessor> logger)
        {
            EnsureArg.IsNotNull(changeFeedProcessor, nameof(changeFeedProcessor));
            EnsureArg.IsNotNull(logger, nameof(logger));

            _changeFeedProcessor = changeFeedProcessor;
            _logger = logger;
        }
Esempio n. 10
0
 public bool UnRegisterDependency(string key)
 {
     lock (this)
     {
         if (DependencyChangeCorrelator.Instance.UnRegisterDependency(key))
         {
             _changeFeedProcessor.StopAsync();
             _changeFeedProcessor = null;
         }
     }
     return(true);
 }
Esempio n. 11
0
        public DicomCastWorker(
            IOptions <DicomCastWorkerConfiguration> dicomCastWorkerConfiguration,
            IChangeFeedProcessor changeFeedProcessor,
            ILogger <DicomCastWorker> logger)
        {
            EnsureArg.IsNotNull(dicomCastWorkerConfiguration?.Value, nameof(dicomCastWorkerConfiguration));
            EnsureArg.IsNotNull(changeFeedProcessor, nameof(changeFeedProcessor));
            EnsureArg.IsNotNull(logger, nameof(logger));

            _dicomCastWorkerConfiguration = dicomCastWorkerConfiguration.Value;
            _changeFeedProcessor          = changeFeedProcessor;
            _logger = logger;
        }
Esempio n. 12
0
        public async Task Schema_DefaultsToNoLeaseToken()
        {
            TestObserverFactory observerFactory = new TestObserverFactory(
                openProcessor: null,
                (FeedProcessing.IChangeFeedObserverContext context, IReadOnlyList <Document> docs) =>
            {
                return(Task.CompletedTask);
            });

            IChangeFeedProcessor changeFeedProcessorBuilder = await new ChangeFeedProcessorBuilder()
                                                              .WithObserverFactory(observerFactory)
                                                              .WithHostName("smoke_test")
                                                              .WithFeedCollection(this.MonitoredCollectionInfo)
                                                              .WithLeaseCollection(this.LeaseCollectionInfo)
                                                              .BuildAsync();

            await changeFeedProcessorBuilder.StartAsync();

            await this.WaitUntilLeaseStoreIsInitializedAsync(new CancellationTokenSource(5000).Token);

            await changeFeedProcessorBuilder.StopAsync();

            // Verify that no leases have LeaseToken (V3 contract)
            int leasesProcessed = 0;

            using (DocumentClient client = new DocumentClient(this.LeaseCollectionInfo.Uri, this.LeaseCollectionInfo.MasterKey, this.LeaseCollectionInfo.ConnectionPolicy))
            {
                Uri collectionUri = UriFactory.CreateDocumentCollectionUri(this.LeaseCollectionInfo.DatabaseName, this.LeaseCollectionInfo.CollectionName);

                IDocumentQuery <JObject> query = client.CreateDocumentQuery <JObject>(collectionUri, "SELECT * FROM c").AsDocumentQuery();
                while (query.HasMoreResults)
                {
                    foreach (JObject lease in await query.ExecuteNextAsync())
                    {
                        string leaseId = lease.Value <string>("id");
                        if (leaseId.Contains(".info"))
                        {
                            // These are the store initialization marks
                            continue;
                        }

                        Assert.NotNull(lease.Value <string>("PartitionId"));
                        Assert.Null(lease.Value <string>("LeaseToken"));
                        leasesProcessed++;
                    }
                }
            }

            Assert.True(leasesProcessed > 0);
        }
        public async Task CloseAsync()
        {
            if (GetDestinationCollectionClient() != null)
            {
                GetDestinationCollectionClient().Dispose();
            }

            if (changeFeedProcessor != null)
            {
                await changeFeedProcessor.StopAsync();
            }

            destinationCollectionClient = null;
            changeFeedProcessor         = null;
        }
Esempio n. 14
0
        private async void OnStarted()
        // private void OnStarted()
        {
            _logger.LogInformation("OnStarted has been called.");

            // Perform post-startup activities here
            _logger.LogInformation("Log directory is " + localLogdirectory);

            string hostName = Guid.NewGuid().ToString();

            DocumentCollectionInfo documentCollectionInfo = new DocumentCollectionInfo
            {
                Uri            = new Uri(_configuration["az_cosmos_uri"]),
                MasterKey      = _configuration["az_cosmos_key"],
                DatabaseName   = _configuration["az_cosmos_db_name"],
                CollectionName = _configuration["az_cosmos_collection_name"]
            };

            DocumentCollectionInfo leaseCollectionInfo = new DocumentCollectionInfo
            {
                Uri            = new Uri(_configuration["az_cosmos_uri"]),
                MasterKey      = _configuration["az_cosmos_key"],
                DatabaseName   = _configuration["az_cosmos_db_name"],
                CollectionName = _configuration["az_cosmos_lease_collection_name"]
            };

            DocumentFeedObserverFactory docObserverFactory = new DocumentFeedObserverFactory();
            ChangeFeedOptions           feedOptions        = new ChangeFeedOptions();

            feedOptions.StartFromBeginning = true;

            ChangeFeedProcessorOptions feedProcessorOptions = new ChangeFeedProcessorOptions();

            feedProcessorOptions.LeaseRenewInterval = TimeSpan.FromSeconds(15);

            this.builder
            .WithHostName(hostName)
            .WithFeedCollection(documentCollectionInfo)
            .WithLeaseCollection(leaseCollectionInfo)
            .WithProcessorOptions(feedProcessorOptions)
            .WithObserverFactory(new DocumentFeedObserverFactory());   //;
//               .WithObserver<DocumentFeedObserver>();  //or just pass a observer

            iChangeFeedProcessor = await this.builder.BuildAsync();

            await iChangeFeedProcessor.StartAsync();
        }
Esempio n. 15
0
        public override async Task StartAsync(CancellationToken cancellationToken)
        {
            var hostName = Environment.MachineName;
            var builder  = new ChangeFeedProcessorBuilder()
                           .WithHostName(hostName)
                           .WithFeedCollection(_feedCollection)
                           .WithLeaseCollection(_leaseCollection)
                           .WithObserverFactory(this);

            _processor = await builder.BuildAsync();

            await _processor.StartAsync();

            _logger.LogInformation($"Change feed processor started at {hostName}...");

            await base.StartAsync(cancellationToken);
        }
Esempio n. 16
0
        static async Task MainAsync(string[] args)
        {
            try
            {
                //setup our DI
                var serviceProvider = new ServiceCollection()
                                      .AddLogging((builder) =>
                {
                    builder
                    .AddConsole()
                    .SetMinimumLevel(LogLevel.Information);
                })
                                      .AddSingleton <CosmosDbSink>()
                                      .BuildServiceProvider();

                var loggerFactory = serviceProvider.GetService <ILoggerFactory>();
                var logger        = loggerFactory.CreateLogger <Program>();
                logger.LogDebug("Starting application");

                AppDomain.CurrentDomain.FirstChanceException +=
                    new FirstChanceExceptionLogger(loggerFactory).FirstChanceHandler;

                // Set the maximum number of concurrent connections
                ServicePointManager.DefaultConnectionLimit = 1200;

                var changeFeed = new ChangeFeed(new CosmosDbSink(loggerFactory), loggerFactory);

                IChangeFeedProcessor processor = await changeFeed.StartAsync();

                logger.LogInformation("Changefeed started successfully");

                Console.WriteLine("Press any key to stop listening for changes...");
                Console.ReadKey();

                await processor.StopAsync();
            }
            catch (Exception error)
            {
                Console.WriteLine("UNHANDLED EXCEPTION: {0}", error);

                // TODO fabianm REMOVE
                Console.ReadKey();
                throw;
            }
        }
Esempio n. 17
0
        public DicomCastWorker(
            IOptions <DicomCastWorkerConfiguration> dicomCastWorkerConfiguration,
            IChangeFeedProcessor changeFeedProcessor,
            ILogger <DicomCastWorker> logger,
            IHostApplicationLifetime hostApplicationLifetime,
            IFhirService fhirService)
        {
            EnsureArg.IsNotNull(dicomCastWorkerConfiguration?.Value, nameof(dicomCastWorkerConfiguration));
            EnsureArg.IsNotNull(changeFeedProcessor, nameof(changeFeedProcessor));
            EnsureArg.IsNotNull(logger, nameof(logger));
            EnsureArg.IsNotNull(hostApplicationLifetime, nameof(hostApplicationLifetime));
            EnsureArg.IsNotNull(fhirService, nameof(fhirService));

            _dicomCastWorkerConfiguration = dicomCastWorkerConfiguration.Value;
            _changeFeedProcessor          = changeFeedProcessor;
            _logger = logger;
            _hostApplicationLifetime = hostApplicationLifetime;
            _fhirService             = fhirService;
        }
        /// <summary>
        /// Begin monitoring the CosmosDB Change Feed
        /// </summary>
        public async Task Start()
        {
            this._processor = await new Microsoft.Azure.Documents.ChangeFeedProcessor.ChangeFeedProcessorBuilder()
                              .WithHostName(this._siloName)
                              .WithProcessorOptions(new Microsoft.Azure.Documents.ChangeFeedProcessor.ChangeFeedProcessorOptions
            {
                CheckpointFrequency = new Microsoft.Azure.Documents.ChangeFeedProcessor.CheckpointFrequency
                {
                    ExplicitCheckpoint = true
                }
#if DEBUG
                ,
                LeasePrefix = "DEBUG"
#endif
            })
                              .WithFeedCollection(this._options.FeedCollectionInfo)
                              .WithLeaseCollection(this._options.LeaseCollectionInfo)
                              .WithObserverFactory(this)
                              .BuildAsync();
            await this._processor.StartAsync();
        }
Esempio n. 19
0
        public async Task StartAsync(CancellationToken cancellationToken)
        {
            var previousStatus = Interlocked.CompareExchange(ref _listenerStatus, ListenerRegistering, ListenerNotRegistered);

            if (previousStatus == ListenerRegistering)
            {
                throw new InvalidOperationException("The listener is already starting.");
            }

            if (previousStatus == ListenerRegistered)
            {
                throw new InvalidOperationException("The listener has already started.");
            }

            InitializeBuilder();

            try
            {
                await this.StartProcessorAsync();

                Interlocked.CompareExchange(ref _listenerStatus, ListenerRegistered, ListenerRegistering);
            }
            catch (Exception ex)
            {
                // Reset to NotRegistered
                _listenerStatus = ListenerNotRegistered;

                // Throw a custom error if NotFound.
                if (ex is DocumentClientException docEx && docEx.StatusCode == HttpStatusCode.NotFound)
                {
                    // Throw a custom error so that it's easier to decipher.
                    var message = $"Either the source collection '{_monitorCollection.CollectionName}' (in database '{_monitorCollection.DatabaseName}')  or the lease collection '{_leaseCollection.CollectionName}' (in database '{_leaseCollection.DatabaseName}') does not exist. Both collections must exist before the listener starts. To automatically create the lease collection, set '{nameof(CosmosStoreTriggerAttribute.CreateLeaseCollectionIfNotExists)}' to 'true'.";
                    _host = null;
                    throw new InvalidOperationException(message, ex);
                }

                throw;
            }
        }
        public async Task WriteToDocDB_SouldGeneratesIncomingEvent_IfStartingFromDefaultPoint()
        {
            string sessionId = Guid.NewGuid().ToString();

            IntegrationTestsHelper.GetConfigurationSettings(out DocumentCollectionInfo feedCollectionInfo, out DocumentCollectionInfo leaseCollectionInfo, out int feedOfferThroughput, out int leaseOfferThroughput);
            feedCollectionInfo.CollectionName  = Guid.NewGuid().ToString();
            leaseCollectionInfo.CollectionName = feedCollectionInfo.CollectionName + "-lease";

            using (var feedClient = new DocumentClient(
                       feedCollectionInfo.Uri,
                       feedCollectionInfo.MasterKey,
                       feedCollectionInfo.ConnectionPolicy))
            {
                await IntegrationTestsHelper.CreateDocumentCollectionAsync(feedClient, feedCollectionInfo.DatabaseName, new DocumentCollection { Id = feedCollectionInfo.CollectionName }, 400);

                using (var client = new DocumentClient(leaseCollectionInfo.Uri, leaseCollectionInfo.MasterKey, leaseCollectionInfo.ConnectionPolicy))
                {
                    await IntegrationTestsHelper.CreateDocumentCollectionAsync(
                        client,
                        leaseCollectionInfo.DatabaseName,
                        new DocumentCollection { Id = leaseCollectionInfo.CollectionName },
                        400);
                }

                TaskCompletionSource <bool> documentReceived = new TaskCompletionSource <bool>();
                Task Process(IChangeFeedObserverContext changeFeedObserverContext, IReadOnlyList <Document> readOnlyList)
                {
                    if (readOnlyList.Any(d => d.GetPropertyValue <string>("payload") == sessionId))
                    {
                        documentReceived.SetResult(true);
                    }
                    return(Task.CompletedTask);
                }

                FeedProcessing.IChangeFeedObserverFactory observerFactory = new DelegatingMemoryObserverFactory(
                    ctx => Task.CompletedTask,
                    (ctx, reason) => Task.CompletedTask,
                    Process);

                IChangeFeedProcessor changeFeedProcessor = await new ChangeFeedProcessorBuilder()
                                                           .WithObserverFactory(observerFactory)
                                                           .WithHostName("smoke_test")
                                                           .WithFeedCollection(feedCollectionInfo)
                                                           .WithLeaseCollection(leaseCollectionInfo)
                                                           .BuildAsync();

                await changeFeedProcessor.StartAsync().ConfigureAwait(false);

                try
                {
                    var generateDocsTask = await Task.Factory.StartNew(async() =>
                    {
                        while (!documentReceived.Task.IsCompleted)
                        {
                            var document = new Document();
                            document.SetPropertyValue("payload", sessionId);
                            var collectionUri = UriFactory.CreateDocumentCollectionUri(
                                feedCollectionInfo.DatabaseName,
                                feedCollectionInfo.CollectionName);
                            await feedClient.CreateDocumentAsync(collectionUri, document);
                            await Task.Delay(TimeSpan.FromMilliseconds(100));
                        }
                    });

                    await documentReceived.Task;
                    await generateDocsTask;
                }
                finally
                {
                    await changeFeedProcessor.StopAsync();
                }
            }
        }
        public async Task <IChangeFeedProcessor> RunChangeFeedHostAsync()
        {
            string hostName = Guid.NewGuid().ToString();

            Trace.TraceInformation("Host name {0}", hostName);

            // monitored collection info
            var documentCollectionLocation = new DocumentCollectionInfo
            {
                Uri            = new Uri(this.config.MonitoredUri),
                MasterKey      = this.config.MonitoredSecretKey,
                DatabaseName   = this.config.MonitoredDbName,
                CollectionName = this.config.MonitoredCollectionName
            };

            var policy = new ConnectionPolicy()
            {
                ConnectionMode     = ConnectionMode.Direct,
                ConnectionProtocol = Protocol.Tcp
            };

            policy.PreferredLocations.Add("North Europe");

            // lease collection info
            var leaseCollectionLocation = new DocumentCollectionInfo
            {
                Uri              = new Uri(this.config.LeaseUri),
                MasterKey        = this.config.LeaseSecretKey,
                DatabaseName     = this.config.LeaseDbName,
                CollectionName   = this.config.LeaseCollectionName,
                ConnectionPolicy = policy
            };

            // destination collection info
            var destCollInfo = new DocumentCollectionInfo
            {
                Uri            = new Uri(this.config.DestUri),
                MasterKey      = this.config.DestSecretKey,
                DatabaseName   = this.config.DestDbName,
                CollectionName = this.config.DestCollectionName
            };

            var processorOptions = new ChangeFeedProcessorOptions();

            if (config.DataAgeInHours.HasValue)
            {
                if (config.DataAgeInHours.Value >= 0)
                {
                    processorOptions.StartTime = DateTime.UtcNow.Subtract(TimeSpan.FromHours(config.DataAgeInHours.Value));
                }
            }
            else
            {
                processorOptions.StartFromBeginning = true;
            }

            processorOptions.LeaseRenewInterval = TimeSpan.FromSeconds(30);

            Trace.TraceInformation("Processor options Starts from Beginning - {0}, Lease renew interval - {1}",
                                   processorOptions.StartFromBeginning,
                                   processorOptions.LeaseRenewInterval.ToString());

            processorOptions.MaxItemCount = 1000;
            var destClient = new DocumentClient(destCollInfo.Uri, destCollInfo.MasterKey,
                                                new ConnectionPolicy()
            {
                ConnectionMode = ConnectionMode.Direct, ConnectionProtocol = Protocol.Tcp
            },
                                                ConsistencyLevel.Eventual);

            var docTransformer = new DefaultDocumentTransformer();

            BlobContainerClient containerClient = null;

            if (!String.IsNullOrEmpty(config.BlobConnectionString))
            {
                BlobServiceClient blobServiceClient = new BlobServiceClient(config.BlobConnectionString);
                containerClient = blobServiceClient.GetBlobContainerClient(config.BlobContainerName);
                await containerClient.CreateIfNotExistsAsync();
            }

            var docObserverFactory = new DocumentFeedObserverFactory(config.SourcePartitionKeys, config.TargetPartitionKey, destClient, destCollInfo, docTransformer, containerClient);

            changeFeedProcessor = await new ChangeFeedProcessorBuilder()
                                  .WithObserverFactory(docObserverFactory)
                                  .WithHostName(hostName)
                                  .WithFeedCollection(documentCollectionLocation)
                                  .WithLeaseCollection(leaseCollectionLocation)
                                  .WithProcessorOptions(processorOptions)
                                  .WithFeedDocumentClient(new DocumentClient(documentCollectionLocation.Uri, documentCollectionLocation.MasterKey, policy, ConsistencyLevel.Eventual))
                                  .BuildAsync();
            await changeFeedProcessor.StartAsync().ConfigureAwait(false);

            return(changeFeedProcessor);
        }
Esempio n. 22
0
        public async Task Schema_OnV2MigrationMaintainLeaseToken()
        {
            const int batchSize      = 10;
            int       partitionCount = await IntegrationTestsHelper.GetPartitionCount(this.MonitoredCollectionInfo);

            int openedCount = 0;
            ManualResetEvent    allObserversStarted         = new ManualResetEvent(false);
            List <int>          expectedIds                 = Enumerable.Range(0, batchSize * 2).ToList();
            ManualResetEvent    firstSetOfResultsProcessed  = new ManualResetEvent(false);
            ManualResetEvent    secondSetOfResultsProcessed = new ManualResetEvent(false);
            List <int>          receivedIds                 = new List <int>();
            TestObserverFactory observerFactory             = new TestObserverFactory(
                context =>
            {
                int newCount = Interlocked.Increment(ref openedCount);
                if (newCount == partitionCount)
                {
                    allObserversStarted.Set();
                }
                return(Task.CompletedTask);
            },
                (FeedProcessing.IChangeFeedObserverContext context, IReadOnlyList <Document> docs) =>
            {
                foreach (Document doc in docs)
                {
                    receivedIds.Add(int.Parse(doc.Id));
                }

                if (receivedIds.Count == batchSize)
                {
                    firstSetOfResultsProcessed.Set();
                }

                if (receivedIds.Count == batchSize * 2)
                {
                    secondSetOfResultsProcessed.Set();
                }

                return(Task.CompletedTask);
            });

            IChangeFeedProcessor changeFeedProcessorBuilder = await new ChangeFeedProcessorBuilder()
                                                              .WithObserverFactory(observerFactory)
                                                              .WithHostName("smoke_test")
                                                              .WithFeedCollection(this.MonitoredCollectionInfo)
                                                              .WithLeaseCollection(this.LeaseCollectionInfo)
                                                              .BuildAsync();

            await changeFeedProcessorBuilder.StartAsync();

            await this.WaitUntilLeaseStoreIsInitializedAsync(new CancellationTokenSource(5000).Token);

            // Inserting some documents
            using (DocumentClient client = new DocumentClient(this.MonitoredCollectionInfo.Uri, this.MonitoredCollectionInfo.MasterKey, this.MonitoredCollectionInfo.ConnectionPolicy))
            {
                Uri collectionUri = UriFactory.CreateDocumentCollectionUri(this.MonitoredCollectionInfo.DatabaseName, this.MonitoredCollectionInfo.CollectionName);

                foreach (int id in expectedIds.Take(10))
                {
                    await client.CreateDocumentAsync(collectionUri, new { id = id.ToString() });
                }
            }

            Assert.True(firstSetOfResultsProcessed.WaitOne(IntegrationTest.changeWaitTimeout), "Timed out waiting for first set of items to be received.");

            await changeFeedProcessorBuilder.StopAsync();

            // At this point we have leases for V2, so we will simulate V3 by manually adding LeaseToken and removing PartitionId
            using (DocumentClient client = new DocumentClient(this.LeaseCollectionInfo.Uri, this.LeaseCollectionInfo.MasterKey, this.LeaseCollectionInfo.ConnectionPolicy))
            {
                Uri collectionUri = UriFactory.CreateDocumentCollectionUri(this.LeaseCollectionInfo.DatabaseName, this.LeaseCollectionInfo.CollectionName);

                IDocumentQuery <JObject> query = client.CreateDocumentQuery <JObject>(collectionUri, "SELECT * FROM c").AsDocumentQuery();
                while (query.HasMoreResults)
                {
                    foreach (JObject lease in await query.ExecuteNextAsync())
                    {
                        string leaseId = lease.Value <string>("id");
                        if (leaseId.Contains(".info"))
                        {
                            // These are the store initialization marks
                            continue;
                        }

                        // create the LeaseToken property
                        lease.Add("LeaseToken", lease.Value <string>("PartitionId"));

                        lease.Remove("PartitionId");

                        await client.UpsertDocumentAsync(collectionUri, lease);
                    }
                }
            }

            // Now all leases are V3 leases, start another processor that should migrate to V2 schema and maintain LeaseToken for compatibility
            openedCount = 0;
            allObserversStarted.Reset();
            changeFeedProcessorBuilder = await new ChangeFeedProcessorBuilder()
                                         .WithObserverFactory(observerFactory)
                                         .WithHostName("smoke_test")
                                         .WithFeedCollection(this.MonitoredCollectionInfo)
                                         .WithLeaseCollection(this.LeaseCollectionInfo)
                                         .BuildAsync();

            await changeFeedProcessorBuilder.StartAsync();

            Assert.True(allObserversStarted.WaitOne(IntegrationTest.changeWaitTimeout + IntegrationTest.changeWaitTimeout), "Timed out waiting for observres to start");
            // Create the rest of the documents
            using (DocumentClient client = new DocumentClient(this.MonitoredCollectionInfo.Uri, this.MonitoredCollectionInfo.MasterKey, this.MonitoredCollectionInfo.ConnectionPolicy))
            {
                Uri collectionUri = UriFactory.CreateDocumentCollectionUri(this.MonitoredCollectionInfo.DatabaseName, this.MonitoredCollectionInfo.CollectionName);

                foreach (int id in expectedIds.TakeLast(10))
                {
                    await client.CreateDocumentAsync(collectionUri, new { id = id.ToString() });
                }
            }

            Assert.True(secondSetOfResultsProcessed.WaitOne(IntegrationTest.changeWaitTimeout), "Timed out waiting for second set of items to be received.");
            await changeFeedProcessorBuilder.StopAsync();

            // Verify we processed all items (including when using the V3 leases)
            Assert.True(!expectedIds.Except(receivedIds).Any() && expectedIds.Count == expectedIds.Count);


            // Verify the after-migration leases have both PartitionId and LeaseToken with the same value
            using (DocumentClient client = new DocumentClient(this.LeaseCollectionInfo.Uri, this.LeaseCollectionInfo.MasterKey, this.LeaseCollectionInfo.ConnectionPolicy))
            {
                Uri collectionUri = UriFactory.CreateDocumentCollectionUri(this.LeaseCollectionInfo.DatabaseName, this.LeaseCollectionInfo.CollectionName);

                IDocumentQuery <JObject> query = client.CreateDocumentQuery <JObject>(collectionUri, "SELECT * FROM c").AsDocumentQuery();
                while (query.HasMoreResults)
                {
                    foreach (JObject lease in await query.ExecuteNextAsync())
                    {
                        string leaseId = lease.Value <string>("id");
                        if (leaseId.Contains(".info"))
                        {
                            // These are the store initialization marks
                            continue;
                        }

                        Assert.NotNull(lease.Value <string>("PartitionId"));
                        Assert.Null(lease.Value <string>("LeaseToken"));
                    }
                }
            }
        }