public async void EnsureConfigCurrentWithUnchangedSignature()
        {
            var dbConfig = CreateDbConfig();

            string signature = "sig";
            var    configDoc = CreateConfigDoc("Test", signature);

            _signatureGenerator.CreateSignature(Arg.Any <IList <DocumentStoreConfig> >()).Returns(signature);
            _documentClient.ReadDocumentAsync(Arg.Any <Uri>(), Arg.Is <RequestOptions>(x => x.PartitionKey != null)).Returns(WrapResource(configDoc));

            // Existing index that should remain.
            var includeIdx1 = new IncludedPath();

            includeIdx1.Path = "/content_Test_StoreA_DocA/*";
            includeIdx1.Indexes.Add(new HashIndex(DataType.String, -1));

            // Existing index that should be removed. It is no longer present.
            var includeIdx2 = new IncludedPath();

            includeIdx2.Path = "/content_Test_StoreB_DocA/PropA/*";
            includeIdx2.Indexes.Add(new RangeIndex(DataType.String));

            var manager = new ServiceDbConfigManager("Test", _signatureGenerator);

            _documentClient.UpsertDocumentAsync(Arg.Any <Uri>(), Arg.Is <Document>(x => x.GetPropertyValue <string>("Signature") == signature))
            .Returns(WrapResource(CreateConfigDoc(configDoc.Id, signature)));

            manager.RegisterStoreConfigSource(CreateConfigSource("A"));
            manager.RegisterStoreConfigSource(CreateConfigSource("B"));

            manager.EnsureConfigCurrent(_documentClient, dbConfig);

            await AssertNoUpdateTriggered();
        }
        public async void TestDocumentAttachments()
        {
            var configManager    = new ServiceDbConfigManager("TestService");
            var dbAccess         = CreateDbAccess(configManager);
            var dbAccessProvider = new TestDocumentDbAccessProvider(dbAccess);

            var store = new EmailStore(dbAccessProvider);

            await dbAccess.Open(new[] { store });

            var email = new Email
            {
                Id      = Guid.NewGuid(),
                Subject = "Re: Holiday"
            };

            var attachment = new EmailAttachment
            {
                Data = "test"
            };

            // Store without attachment.
            await store.Upsert(email, null);

            var actualAttachment = await store.GetEmailAttachment(email.Id);

            Assert.Null(actualAttachment);

            // Store with attachment.
            await store.Upsert(email, attachment);

            actualAttachment = await store.GetEmailAttachment(email.Id);

            Assert.Equal(attachment.Data, actualAttachment.Data);
        }
Пример #3
0
        /// <summary>
        /// Initialises a new instance of the <see cref="DocumentDbAccess"/> class for testing.
        /// </summary>
        /// <param name="dbConfig">The database config,</param>
        /// <param name="configManager">The document config manager.</param>
        /// <param name="documentClient">The document client.</param>
        /// <param name="dbService">The document db service.</param>
        /// <param name="queryPolicy">The document query policy.</param>
        /// <remarks>
        /// <para>This constructor should be used for internal testing only.</para>
        /// </remarks>
        internal DocumentDbAccess(
            DocumentDbConfig dbConfig,
            ServiceDbConfigManager configManager,
            IDocumentClient documentClient,
            IDocumentDbService dbService,
            IDocumentQueryPolicy queryPolicy)
        {
            if (dbConfig == null)
            {
                throw new ArgumentNullException(nameof(dbConfig));
            }
            if (configManager == null)
            {
                throw new ArgumentNullException(nameof(configManager));
            }
            if (documentClient == null)
            {
                throw new ArgumentNullException(nameof(documentClient));
            }
            if (dbService == null)
            {
                throw new ArgumentNullException(nameof(dbService));
            }
            if (queryPolicy == null)
            {
                throw new ArgumentNullException(nameof(queryPolicy));
            }

            _dbConfig      = dbConfig;
            _configManager = configManager;
            _queryPolicy   = queryPolicy;
            _client        = documentClient;
            _dbService     = dbService;
        }
        public async void TestServiceRegistrationAndUpdateConcurrency()
        {
            // This test performs a concurrency check to ensure that multiple threads can successfully interact with
            // the same config manager. The config manager is thread safe so multiple stores registering and performing
            // actions on different threads should produce a consistent result.

            var configManager = new ServiceDbConfigManager("TestService");
            var dbAccess      = await CreateDbAccess(configManager);

            var dbAccessProvider = new TestDocumentDbAccessProvider(dbAccess);

            List <Task> tasks = new List <Task>
            {
                Task.Run(async() =>
                {
                    var fruitStore = new FruitStore(dbAccessProvider);

                    var gala = new Apple {
                        Id = Guid.NewGuid(), Type = "Gala"
                    };
                    await fruitStore.UpsertApple(gala);
                }),
                Task.Run(async() =>
                {
                    var flowerStore = new FlowerStore(dbAccessProvider);

                    var daisy = new Daisy {
                        Id = Guid.NewGuid(), Colour = "Red"
                    };
                    await flowerStore.Upsert(daisy);
                })
            };

            Task.WaitAll(tasks.ToArray());
        }
Пример #5
0
        /// <summary>
        /// Initialises a new instance of the <see cref="DocumentDbService"/> class.
        /// </summary>
        /// <param name="configManager">The service config manager.</param>
        /// <param name="dbConfig">The document db config.</param>
        public DocumentDbService(ServiceDbConfigManager configManager, DocumentDbConfig dbConfig)
        {
            if (configManager == null)
            {
                throw new ArgumentNullException(nameof(configManager));
            }
            if (dbConfig == null)
            {
                throw new ArgumentNullException(nameof(dbConfig));
            }

            _configManager = configManager;
            _dbConfig      = dbConfig;

            var connectionPolicy = new ConnectionPolicy {
                ConnectionMode     = ConnectionMode.Direct,
                ConnectionProtocol = Protocol.Tcp,
                RetryOptions       = new RetryOptions {
                    MaxRetryAttemptsOnThrottledRequests = 20,
                    MaxRetryWaitTimeInSeconds           = 60
                }
            };

            _client = new DocumentClient(new Uri(dbConfig.ServiceEndpoint), dbConfig.AuthKey, connectionPolicy);
        }
Пример #6
0
        protected DocumentDbAccess CreateDbAccess(
            ServiceDbConfigManager configManager,
            int collectionRuLimit = 1000)
        {
            var dbAccess = new DocumentDbAccess(CreateDbConfig(_databaseId, collectionRuLimit), configManager);

            _dbAccesses.Add(dbAccess);

            return(dbAccess);
        }
        public void IsStoreConfigRegisteredFalseWhenNotRegistered()
        {
            var configDoc = CreateConfigDoc("Test", "sig");

            _signatureGenerator.CreateSignature(Arg.Any <IList <DocumentStoreConfig> >()).Throws(new Exception());
            _documentClient.ReadDocumentAsync(Arg.Any <Uri>()).Returns(WrapResource(configDoc));

            var manager = new ServiceDbConfigManager("Test", _signatureGenerator);

            Assert.False(manager.IsStoreConfigRegistered(_configSourceA));
        }
        public async void TestLargeNumberOfVersionsPerformance()
        {
            // Test the performance of writing and reading a single document with a large number of versions.
            //
            // CosmosDb emulator setup
            // - 2000 RU/s collection
            // - Rate limiting.
            //
            // Current performance
            // - 2nd write: 0.04 sec
            // - 1000th write: 0.04 sec
            // - Read: 0.04 sec
            //
            // The current implementation does not work when thrashed with lower RU/s due to the RU cost of
            // the read and write operations. There is a timeout waiting for retries. An improved
            // implementation should be considered to lower the cost of versioned read and write against
            // latest documents. E.g., a @latest flag would solve this issue.

            const int collectionRuLimit = 2000;
            const int numberOfVersions  = 1000;

            var configManager = new ServiceDbConfigManager("TestService");
            var dbAccess      = await CreateDbAccess(configManager, collectionRuLimit);

            var dbAccessProvider = new TestDocumentDbAccessProvider(dbAccess);

            var store = new LargeDocumentStore(dbAccessProvider);

            var attachment = JsonConvert.DeserializeObject <LargeDocument>(File.ReadAllText("TestData/LargeDocument.json"));

            var document = new SmallDocumentWithLargeAttachment();

            document.Id = Guid.NewGuid();

            Stopwatch sw = new Stopwatch();

            sw.Start();

            for (var i = 0; i < numberOfVersions; i++)
            {
                sw.Restart();

                await store.UpsertDocument(document, attachment);

                TestOutputHelper.WriteLine("Write={0}", sw.Elapsed);
            }

            var result = await store.GetSmallDocument(document.Id);

            TestOutputHelper.WriteLine("Read={0}", sw.Elapsed);

            Assert.NotNull(result);
        }
        private async Task SetupStore()
        {
            var configManager    = new ServiceDbConfigManager("TestService");
            var dbAccess         = CreateDbAccess(configManager);
            var dbAccessProvider = new TestDocumentDbAccessProvider(dbAccess);

            var store = new TestDocumentStore(dbAccessProvider);

            await dbAccess.Open(new[] { store });

            _store = store;
        }
        public async void TestLargeNumberOfDocuments()
        {
            var configManager    = new ServiceDbConfigManager("TestService");
            var dbAccess         = CreateDbAccess(configManager);
            var dbAccessProvider = new TestDocumentDbAccessProvider(dbAccess);

            var fruitStore = new FruitStore(dbAccessProvider);

            await dbAccess.Open(new[] { fruitStore });

            var apples = new List <Apple>();

            for (int i = 0; i < 800; i++)
            {
                var gala = new Apple
                {
                    Id   = Guid.NewGuid(),
                    Type = "Gala"
                };

                var fuji = new Apple
                {
                    Id   = Guid.NewGuid(),
                    Type = "Fuji"
                };

                await fruitStore.UpsertApple(gala);

                await fruitStore.UpsertApple(fuji);

                apples.AddRange(new[] { gala, fuji });
            }

            var r1 = await fruitStore.GetAppleByQuery("[x].Type = 'Gala'");

            Assert.Equal(800, r1.Length);
            Assert.True(r1.All(x => x.Type == "Gala"));

            var r2 = await fruitStore.GetAllApples();

            Assert.Equal(1600, r2.Length);

            var lastApple = apples.Last();
            var r3        = await fruitStore.GetAppleById(lastApple.Id);

            Assert.Equal(lastApple.Id, r3.Id);
            Assert.Equal(lastApple.Type, r3.Type);

            var r4 = await fruitStore.GetAppleByIds(apples.Select(x => x.Id.ToString()));

            Assert.Equal(1600, r4.Length);
        }
        public async void TestDbParameterQueries()
        {
            var configManager    = new ServiceDbConfigManager("TestService");
            var dbAccess         = CreateDbAccess(configManager);
            var dbAccessProvider = new TestDocumentDbAccessProvider(dbAccess);

            var fruitStore = new FruitStore(dbAccessProvider);

            await dbAccess.Open(new[] { fruitStore });

            var pears = new List <Pear>();

            for (int i = 0; i < 50; i++)
            {
                var bartlett = new Pear
                {
                    Id     = Guid.NewGuid(),
                    Colour = "Red"
                };

                var comice = new Pear
                {
                    Id     = Guid.NewGuid(),
                    Colour = "Green"
                };

                await fruitStore.UpsertPear(bartlett);

                await fruitStore.UpsertPear(comice);

                pears.AddRange(new[] { bartlett, comice });
            }

            var r1 = await fruitStore.GetPearByQuery("[x].Colour = @colour", new[] { new DbParameter("colour", "Red") });

            Assert.Equal(50, r1.Length);
            Assert.True(r1.All(x => x.Colour == "Red"));

            var r2 = await fruitStore.GetPearByQuery("[x].Colour = @colour", new[] { new DbParameter("@colour", "Red") });

            Assert.Equal(50, r2.Length);
            Assert.True(r2.All(x => x.Colour == "Red"));

            var r3 = await fruitStore.GetPearByQuery("[x].Colour = @colour", new[] { new DbParameter("colour", "Green") });

            Assert.Equal(50, r3.Length);
            Assert.True(r3.All(x => x.Colour == "Green"));

            var r4 = await fruitStore.GetPearByQuery("[x].Colour = @colour", new[] { new DbParameter("colour", "Blue") });

            Assert.Empty(r4);
        }
        public async void TestMetadataQueries()
        {
            var configManager = new ServiceDbConfigManager("TestService");
            var dbAccess      = await CreateDbAccess(configManager);

            var dbAccessProvider = new TestDocumentDbAccessProvider(dbAccess);

            var fruitStore = new FruitStore(dbAccessProvider);

            var bartlett = new Pear
            {
                Id     = Guid.NewGuid(),
                Colour = "Red"
            };

            var comice = new Pear
            {
                Id     = Guid.NewGuid(),
                Colour = "Green"
            };

            for (int i = 0; i < 50; i++)
            {
                // Mutate the record.
                bartlett.Colour = i % 2 == 0 ? "Red" : "DarkRed";

                await fruitStore.UpsertPear(bartlett);

                await fruitStore.UpsertPear(comice);
            }

            await fruitStore.DeletePearById(bartlett.Id);

            var r1 = await fruitStore.GetPearByQuery("[x].Colour = @colour", new[] { new DbParameter("colour", "Red") });

            Assert.True(r1.All(x => x.Colour == "Red"));

            var r2 = await fruitStore.GetPearVersions(bartlett.Id.ToString());

            Assert.NotNull(r2);
            Assert.Equal(51, r2.Metadata.Count);
            Assert.Equal(1, r2.Metadata[0].Version);
            Assert.False(r2.Metadata[0].IsDeleted);

            var lastVersion = r2.Metadata.Last();

            Assert.Equal(51, lastVersion.Version);
            Assert.True(lastVersion.IsDeleted);
            Assert.True(lastVersion.CreatedTime < lastVersion.ModifiedTime);
            Assert.Equal(r2.Metadata[0].CreatedTime, lastVersion.CreatedTime);
        }
        public async void TestWideQueryMultipleServices()
        {
            // This test ensures that a wide query (e.g., 'true OR true') does not return or attempt
            // to return documents from other services.

            var configManager1    = new ServiceDbConfigManager("TestService1");
            var dbAccess1         = CreateDbAccess(configManager1);
            var dbAccessProvider1 = new TestDocumentDbAccessProvider(dbAccess1);

            var configManager2    = new ServiceDbConfigManager("TestService2");
            var dbAccess2         = CreateDbAccess(configManager2);
            var dbAccessProvider2 = new TestDocumentDbAccessProvider(dbAccess2);

            var fruitStore1 = new FruitStore(dbAccessProvider1);
            var fruitStore2 = new FruitStore(dbAccessProvider2);

            await dbAccess1.Open(new[] { fruitStore1 });

            await dbAccess2.Open(new[] { fruitStore2 });

            var gala = new Apple
            {
                Id   = Guid.NewGuid(),
                Type = "Gala"
            };

            var fuji = new Apple
            {
                Id   = Guid.NewGuid(),
                Type = "Fuji"
            };

            await fruitStore1.UpsertApple(gala);

            await fruitStore2.UpsertApple(fuji);

            var r1 = await fruitStore1.GetApplesByQuery("true OR true");

            Assert.Equal(1, r1.Loaded.Count);
            Assert.Empty(r1.Failed);
            Assert.Equal(gala.Id, r1.Loaded[0].Document.Id);

            var r2 = await fruitStore2.GetApplesByQuery("true OR true");

            Assert.Equal(1, r2.Loaded.Count);
            Assert.Empty(r2.Failed);
            Assert.Equal(fuji.Id, r2.Loaded[0].Document.Id);
        }
        public void EnsureConfigCurrentThrowsForSignatureFailure()
        {
            var dbConfig  = CreateDbConfig();
            var configDoc = CreateConfigDoc("Test", "sig");

            _signatureGenerator.CreateSignature(Arg.Any <IList <DocumentStoreConfig> >()).Throws(new Exception());
            _documentClient.ReadDocumentAsync(Arg.Any <Uri>()).Returns(WrapResource(configDoc));

            var manager = new ServiceDbConfigManager("Test", _signatureGenerator);

            manager.RegisterStoreConfigSource(_configSourceA);

            var ex = Assert.Throws <NebulaConfigException>(() => manager.EnsureConfigCurrent(_documentClient, dbConfig));

            Assert.Contains("signature generation failed", ex.Message);
        }
        public async void TestLargeDocumentAsAttachmentWithMultipleVersionsPerformance()
        {
            // Test the performance of writing and reading a single document with multiple versions and attachment data.
            //
            // CosmosDb emulator setup
            // - 1000 RU/s collection.
            // - Rate limiting.
            //
            // Current performance
            // - Write: 1.9sec
            // - Read: 0.1sec

            const int numberOfVersions = 20;

            var configManager = new ServiceDbConfigManager("TestService");
            var dbAccess      = await CreateDbAccess(configManager);

            var dbAccessProvider = new TestDocumentDbAccessProvider(dbAccess);

            var store = new LargeDocumentStore(dbAccessProvider);

            var attachment = JsonConvert.DeserializeObject <LargeDocument>(File.ReadAllText("TestData/LargeDocument.json"));

            var document = new SmallDocumentWithLargeAttachment();

            document.Id = Guid.NewGuid();

            Stopwatch sw = new Stopwatch();

            sw.Start();

            // The same document is stored multiple times to simulate multiple versions.
            for (var i = 0; i < numberOfVersions; i++)
            {
                await store.UpsertDocument(document, attachment);
            }

            TestOutputHelper.WriteLine("Write={0}", sw.Elapsed);

            sw.Restart();

            var result = await store.GetSmallDocumentAttachment(document.Id);

            TestOutputHelper.WriteLine("Read={0}", sw.Elapsed);

            Assert.NotNull(result);
        }
        public async void EnsureConfigCurrentWithDuplicateStoreConfigRegistrationsAndNoSignatureChange()
        {
            var dbConfig  = CreateDbConfig();
            var configDoc = CreateConfigDoc("Test", "sig");

            _signatureGenerator.CreateSignature(Arg.Any <IList <DocumentStoreConfig> >()).Returns("sig");
            _documentClient.ReadDocumentAsync(Arg.Any <Uri>(), Arg.Is <RequestOptions>(x => x.PartitionKey != null)).Returns(WrapResource(configDoc));

            var manager = new ServiceDbConfigManager("Test", _signatureGenerator);

            manager.RegisterStoreConfigSource(CreateConfigSource("A"));
            manager.RegisterStoreConfigSource(CreateConfigSource("A"));

            manager.EnsureConfigCurrent(_documentClient, dbConfig);

            await AssertNoUpdateTriggered();
        }
Пример #17
0
        private async Task <DocumentDbAccess> CreateDbAccess(IDocumentClient documentClient)
        {
            var signatureGenerator = Substitute.For <IServiceConfigSignatureGenerator>();

            var configManager = new ServiceDbConfigManager("Test", signatureGenerator);

            var queryPolicy = Substitute.For <IDocumentQueryPolicy>();

            queryPolicy.GetIdSearchLimit(Arg.Any <ICollection <string> >()).Returns(1000);
            queryPolicy.IsQueryValid(Arg.Any <string>()).Returns(true);

            var dbService = Substitute.For <IDocumentDbService>();

            var documentDbAccess = new DocumentDbAccess(CreateDbConfig(), configManager, documentClient, dbService, queryPolicy);

            await documentDbAccess.Open(new IDocumentStoreConfigSource[0]);

            return(documentDbAccess);
        }
Пример #18
0
        /// <summary>
        /// Initialises a new instance of the <see cref="DocumentDbAccess"/> class.
        /// </summary>
        /// <param name="dbConfig">The database config.</param>
        /// <param name="configManager">The document config manager.</param>
        public DocumentDbAccess(DocumentDbConfig dbConfig, ServiceDbConfigManager configManager)
        {
            if (dbConfig == null)
            {
                throw new ArgumentNullException(nameof(dbConfig));
            }
            if (configManager == null)
            {
                throw new ArgumentNullException(nameof(configManager));
            }

            _dbConfig      = dbConfig;
            _configManager = configManager;
            _queryPolicy   = new DocumentQueryPolicy();

            var dbService = new DocumentDbService(configManager, dbConfig);

            _dbService = dbService;
            _client    = dbService.Client;
        }
        public async void TestSingleLargeDocumentPerformance()
        {
            // Test the performance of writing and reading a single large, complex document.
            //
            // CosmosDb emulator setup
            // - 1000 RU/s collection.
            // - Rate limiting.
            //
            // Current performance
            // - Write: 0.5sec
            // - Read: 0.9sec

            var configManager = new ServiceDbConfigManager("TestService");
            var dbAccess      = await CreateDbAccess(configManager);

            var dbAccessProvider = new TestDocumentDbAccessProvider(dbAccess);

            var store = new LargeDocumentStore(dbAccessProvider);

            var document = JsonConvert.DeserializeObject <LargeDocument>(File.ReadAllText("TestData/LargeDocument.json"));

            document.Id = Guid.NewGuid();

            Stopwatch sw = new Stopwatch();

            sw.Start();

            await store.UpsertDocument(document);

            TestOutputHelper.WriteLine("Write={0}", sw.Elapsed);

            sw.Restart();

            var result = await store.GetLargeDocument(document.Id);

            TestOutputHelper.WriteLine("Read={0}", sw.Elapsed);

            Assert.NotNull(result);
        }
        public async void TestCustomDocumentMetadata()
        {
            var configManager    = new ServiceDbConfigManager("TestService");
            var dbAccess         = CreateDbAccess(configManager);
            var dbAccessProvider = new TestDocumentDbAccessProvider(dbAccess);
            var metadataSource   = new TestDocumentMetadataSource("User1");

            var fruitStore = new FruitStore(dbAccessProvider, metadataSource);

            await dbAccess.Open(new[] { fruitStore });

            var bartlett = new Pear
            {
                Id     = Guid.NewGuid(),
                Colour = "Red"
            };

            await fruitStore.UpsertPear(bartlett);

            metadataSource.ActorId = "User2";
            await fruitStore.UpsertPear(bartlett);

            metadataSource.ActorId = "User3";
            await fruitStore.DeletePearById(bartlett.Id);

            var r1 = await fruitStore.GetPearById(bartlett.Id, 1);

            Assert.Equal("User1", r1.Metadata.ActorId);

            var r2 = await fruitStore.GetPearById(bartlett.Id, 2);

            Assert.Equal("User2", r2.Metadata.ActorId);

            var r3 = await fruitStore.GetPearById(bartlett.Id, 3);

            Assert.True(r3.Metadata.IsDeleted);
            Assert.Equal("User3", r3.Metadata.ActorId);
        }
        public async void TestDbConcurrency()
        {
            var configManager    = new ServiceDbConfigManager("TestService");
            var dbAccess         = CreateDbAccess(configManager);
            var dbAccessProvider = new TestDocumentDbAccessProvider(dbAccess);

            var fruitStore = new FruitStore(dbAccessProvider);

            await dbAccess.Open(new[] { fruitStore });

            var gala = new Apple
            {
                Id   = Guid.NewGuid(),
                Type = "Gala"
            };

            await fruitStore.UpsertApple(gala);

            var t1 = Task.Run(async() => await SeedCounterAsync(gala.Id, fruitStore));
            var t2 = Task.Run(async() => await SeedCounterAsync(gala.Id, fruitStore, shouldSleep: true));

            await Assert.ThrowsAsync <NebulaStoreConcurrencyException>(() => Task.WhenAll(t1, t2));
        }
        public async void TestDbConcurrency()
        {
            var configManager = new ServiceDbConfigManager("TestService");
            var dbAccess      = await CreateDbAccess(configManager);

            var dbAccessProvider = new TestDocumentDbAccessProvider(dbAccess);

            var fruitStore = new FruitStore(dbAccessProvider);

            var gala = new Apple
            {
                Id   = Guid.NewGuid(),
                Type = "Gala"
            };

            await fruitStore.UpsertApple(gala);

            var t1 = Task.Run(async() => await SeedCounterAsync(gala.Id, fruitStore));
            var t2 = Task.Run(async() => await SeedCounterAsync(gala.Id, fruitStore, shouldSleep: true));

            var exception = Assert.Throws <AggregateException>(() => Task.WaitAll(t1, t2));

            Assert.Equal(typeof(NebulaStoreConcurrencyException), exception.InnerException.GetType());
        }
        public async void TestDocumentPurge()
        {
            var configManager    = new ServiceDbConfigManager("TestService");
            var dbAccess         = CreateDbAccess(configManager);
            var dbAccessProvider = new TestDocumentDbAccessProvider(dbAccess);

            var fruitStore = new FruitStore(dbAccessProvider);

            await dbAccess.Open(new[] { fruitStore });

            var gala = new Apple {
                Id = Guid.NewGuid(), Type = "Gala"
            };
            var opal = new Apple {
                Id = Guid.NewGuid(), Type = "Opal"
            };

            var redBartlett = new Pear {
                Id = Guid.NewGuid(), Colour = "Red"
            };
            var darkRedBartlett = new Pear {
                Id = Guid.NewGuid(), Colour = "DarkRed"
            };

            await fruitStore.UpsertApple(gala);

            await fruitStore.UpsertApple(gala);

            await fruitStore.UpsertApple(gala);

            await fruitStore.UpsertApple(opal);

            await fruitStore.UpsertPear(redBartlett);

            await fruitStore.UpsertPear(darkRedBartlett);

            await fruitStore.UpsertPear(darkRedBartlett);

            await fruitStore.DeletePearById(darkRedBartlett.Id);

            var galaResult = await fruitStore.GetAppleVersions(gala.Id);

            var opalResult = await fruitStore.GetAppleVersions(opal.Id);

            var redBartlettResult = await fruitStore.GetPearVersions(redBartlett.Id);

            var darkRedBartlettResult = await fruitStore.GetPearVersions(redBartlett.Id);

            Assert.NotNull(galaResult);
            Assert.NotNull(opalResult);
            Assert.NotNull(redBartlettResult);
            Assert.NotNull(darkRedBartlettResult);

            await fruitStore.PurgeAllPears();

            galaResult = await fruitStore.GetAppleVersions(gala.Id);

            opalResult = await fruitStore.GetAppleVersions(opal.Id);

            redBartlettResult = await fruitStore.GetPearVersions(redBartlett.Id);

            darkRedBartlettResult = await fruitStore.GetPearVersions(redBartlett.Id);

            Assert.NotNull(galaResult);
            Assert.NotNull(opalResult);
            Assert.Null(redBartlettResult);
            Assert.Null(darkRedBartlettResult);

            await fruitStore.PurgeApple(gala.Id);

            galaResult = await fruitStore.GetAppleVersions(gala.Id);

            opalResult = await fruitStore.GetAppleVersions(opal.Id);

            Assert.Null(galaResult);
            Assert.NotNull(opalResult);

            await fruitStore.UpsertApple(gala);

            var versionedGalaResult = await fruitStore.GetVersionedAppleById(gala.Id);

            Assert.NotNull(versionedGalaResult);
            Assert.Equal(1, versionedGalaResult.Metadata.Version);
        }
        public async void TestVersionedQueries()
        {
            var configManager    = new ServiceDbConfigManager("TestService");
            var dbAccess         = CreateDbAccess(configManager);
            var dbAccessProvider = new TestDocumentDbAccessProvider(dbAccess);

            var fruitStore = new FruitStore(dbAccessProvider);

            await dbAccess.Open(new[] { fruitStore });

            var bartlett = new Pear
            {
                Id     = Guid.NewGuid(),
                Colour = "Red"
            };

            var comice = new Pear
            {
                Id     = Guid.NewGuid(),
                Colour = "Green"
            };

            for (int i = 0; i < 50; i++)
            {
                // Mutate the record.
                bartlett.Colour = i % 2 == 0 ? "Red" : "DarkRed";

                await fruitStore.UpsertPear(bartlett);

                await fruitStore.UpsertPear(comice);
            }

            await fruitStore.DeletePearById(bartlett.Id);

            const int lastVersion = 51;

            var r1 = await fruitStore.GetPearById(bartlett.Id, lastVersion);

            Assert.Equal(lastVersion, r1.Metadata.Version);
            Assert.True(r1.Metadata.IsDeleted);
            Assert.Equal("DarkRed", r1.Document.Colour);

            var r2 = await fruitStore.GetPearById(bartlett.Id, lastVersion - 1);

            Assert.Equal(lastVersion - 1, r2.Metadata.Version);
            Assert.False(r2.Metadata.IsDeleted);
            Assert.Equal("DarkRed", r2.Document.Colour);

            var r3 = await fruitStore.GetPearById(bartlett.Id, lastVersion - 2);

            Assert.Equal(lastVersion - 2, r3.Metadata.Version);
            Assert.False(r3.Metadata.IsDeleted);
            Assert.Equal("Red", r3.Document.Colour);

            var r4 = await fruitStore.GetPearById(bartlett.Id, 1);

            Assert.Equal(1, r4.Metadata.Version);
            Assert.False(r4.Metadata.IsDeleted);
            Assert.Equal("Red", r4.Document.Colour);

            var r5 = await fruitStore.GetPearById(comice.Id, 1);

            Assert.Equal(1, r5.Metadata.Version);
            Assert.False(r5.Metadata.IsDeleted);
            Assert.Equal("Green", r5.Document.Colour);
        }
        public async void TestMetadataQueries()
        {
            var configManager    = new ServiceDbConfigManager("TestService");
            var dbAccess         = CreateDbAccess(configManager);
            var dbAccessProvider = new TestDocumentDbAccessProvider(dbAccess);

            var fruitStore = new FruitStore(dbAccessProvider);

            await dbAccess.Open(new[] { fruitStore });

            var bartlett = new Pear
            {
                Id     = Guid.NewGuid(),
                Colour = "Red"
            };

            var comice = new Pear
            {
                Id     = Guid.NewGuid(),
                Colour = "Green"
            };

            for (int i = 0; i < 5; i++)
            {
                // Mutate the record.
                bartlett.Colour = i % 2 == 0 ? "Red" : "DarkRed";

                await fruitStore.UpsertPear(bartlett);

                await fruitStore.UpsertPear(comice);

                Thread.Sleep(1000);
            }

            await fruitStore.DeletePearById(bartlett.Id);

            var r1 = await fruitStore.GetPearByQuery("[x].Colour = @colour", new[] { new DbParameter("colour", "Red") });

            Assert.True(r1.All(x => x.Colour == "Red"));

            var r2 = await fruitStore.GetPearVersions(bartlett.Id);

            Assert.NotNull(r2);
            Assert.Equal(6, r2.Metadata.Count);
            Assert.Equal(1, r2.Metadata[0].Version);
            Assert.False(r2.Metadata[0].IsDeleted);

            var lastVersion = r2.Metadata.Last();

            Assert.Equal(6, lastVersion.Version);
            Assert.True(lastVersion.IsDeleted);
            Assert.True(lastVersion.CreatedTime < lastVersion.ModifiedTime);
            Assert.Equal(r2.Metadata[0].CreatedTime, lastVersion.CreatedTime);

            for (var i = 1; i < r2.Metadata.Count; i++)
            {
                var prev    = r2.Metadata[i - 1];
                var current = r2.Metadata[i];

                Assert.True(current.ModifiedTime > prev.ModifiedTime);
                Assert.True(current.ModifiedTime - prev.ModifiedTime >= TimeSpan.FromSeconds(1));

                Assert.True(current.ModifiedTime > prev.CreatedTime);
                Assert.Equal(current.CreatedTime, prev.CreatedTime);

                Assert.False(prev.IsDeleted);
            }
        }
        public async void EnsureConfigCurrentWithChangedSignature()
        {
            var dbConfig = CreateDbConfig();

            string oldSig    = "old_sig";
            string newSig    = "new_sig";
            var    configDoc = CreateConfigDoc("Test", oldSig);

            DocumentStoreConfigBuilder storeABuilder = new DocumentStoreConfigBuilder("StoreA");

            storeABuilder.AddDocument("DocA");

            DocumentStoreConfigBuilder storeBBuilder = new DocumentStoreConfigBuilder("StoreB");

            storeBBuilder.AddDocument("DocA");
            storeBBuilder.AddDocument("DocB");

            var configSourceA = CreateConfigSource(storeABuilder);
            var configSourceB = CreateConfigSource(storeBBuilder);

            _signatureGenerator.CreateSignature(Arg.Any <IList <DocumentStoreConfig> >()).Returns(newSig);
            _documentClient.ReadDocumentAsync(Arg.Any <Uri>(), Arg.Is <RequestOptions>(x => x.PartitionKey != null)).Returns(WrapResource(configDoc));

            // Existing index that should remain.
            var includeIdx1 = new IncludedPath();

            includeIdx1.Path = "/content_Test_StoreA_DocA/*";
            includeIdx1.Indexes.Add(new HashIndex(DataType.String, -1));

            // Existing index that should be removed. It is no longer present.
            var includeIdx2 = new IncludedPath();

            includeIdx2.Path = "/content_Test_StoreB_DocA/PropA/*";
            includeIdx2.Indexes.Add(new RangeIndex(DataType.String));

            var col1 = new DocumentCollection();

            col1.IndexingPolicy.IncludedPaths.Add(includeIdx1);
            col1.IndexingPolicy.IncludedPaths.Add(includeIdx2);

            _documentClient.ReadDocumentCollectionAsync(Arg.Any <Uri>()).Returns(WrapResource(col1));

            var manager = new ServiceDbConfigManager("Test", _signatureGenerator);

            var foo = WrapResource(col1);

            _documentClient.ReplaceDocumentCollectionAsync(Arg.Any <DocumentCollection>(), Arg.Any <RequestOptions>()).Returns(foo);

            _documentClient.UpsertDocumentAsync(
                Arg.Any <Uri>(),
                Arg.Is <object>(r => ((ServiceDbConfigManager.ServiceConfigRecord)r).Signature == newSig),
                Arg.Any <RequestOptions>())
            .Returns(WrapResource(CreateConfigDoc(configDoc.Id, newSig)));

            manager.RegisterStoreConfigSource(configSourceA);
            manager.RegisterStoreConfigSource(configSourceB);

            manager.EnsureConfigCurrent(_documentClient, dbConfig);

            await _documentClient.Received(1).UpsertDocumentAsync(Arg.Any <Uri>(), Arg.Any <object>(), Arg.Any <RequestOptions>());

            await _documentClient.Received().ReplaceDocumentCollectionAsync(
                Arg.Is <DocumentCollection>(c =>
                                            IncludedPathCheck(
                                                c.IndexingPolicy.IncludedPaths, "/content_Test_StoreA_DocA/*", "/content_Test_StoreB_DocA/*", "/content_Test_StoreB_DocB/*")),
                Arg.Any <RequestOptions>());
        }
 public VersionedStorePerformanceTests(ITestOutputHelper testOutputHelper)
     : base(testOutputHelper)
 {
     _configManager = new ServiceDbConfigManager("TestService");
 }
        public async void TestMultipleServicesDocumentPurge()
        {
            var configManager1    = new ServiceDbConfigManager("TestService1");
            var dbAccess1         = CreateDbAccess(configManager1);
            var dbAccessProvider1 = new TestDocumentDbAccessProvider(dbAccess1);

            var configManager2    = new ServiceDbConfigManager("TestService2");
            var dbAccess2         = CreateDbAccess(configManager2);
            var dbAccessProvider2 = new TestDocumentDbAccessProvider(dbAccess2);

            var fruitStore1 = new FruitStore(dbAccessProvider1);
            var fruitStore2 = new FruitStore(dbAccessProvider2);

            await dbAccess1.Open(new[] { fruitStore1 });

            await dbAccess2.Open(new[] { fruitStore2 });

            var apples = new List <Apple>();

            for (int i = 0; i < 20; i++)
            {
                var gala = new Apple
                {
                    Id   = Guid.NewGuid(),
                    Type = "Gala"
                };

                var fuji = new Apple
                {
                    Id   = Guid.NewGuid(),
                    Type = "Fuji"
                };

                await fruitStore1.UpsertApple(gala);

                await fruitStore1.UpsertApple(fuji);

                await fruitStore2.UpsertApple(gala);

                await fruitStore2.UpsertApple(fuji);

                apples.AddRange(new[] { gala, fuji });
            }

            var r1 = await fruitStore1.GetAppleByQuery("[x].Type = 'Gala'");

            Assert.Equal(20, r1.Length);
            Assert.True(r1.All(x => x.Type == "Gala"));

            var r2 = await fruitStore1.GetAllApples();

            Assert.Equal(40, r2.Length);

            var lastApple = apples.Last();
            var r3        = await fruitStore1.GetAppleById(lastApple.Id);

            Assert.Equal(lastApple.Id, r3.Id);
            Assert.Equal(lastApple.Type, r3.Type);

            var r4 = await fruitStore1.GetAppleByIds(apples.Select(x => x.Id.ToString()));

            Assert.Equal(40, r4.Length);

            ServiceManager serviceManager1 = new ServiceManager(dbAccess1);
            ServiceManager serviceManager2 = new ServiceManager(dbAccess2);

            // Purge a service's docs.
            await serviceManager1.PurgeDocumentsAsync();

            Assert.Empty(await fruitStore1.GetAllApples());
            Assert.NotEmpty(await fruitStore2.GetAllApples());

            // Purge other service's docs.
            await serviceManager2.PurgeDocumentsAsync();

            Assert.Empty(await fruitStore1.GetAllApples());
            Assert.Empty(await fruitStore2.GetAllApples());
        }
        public async void TestMultipleStoreDocumentTypes()
        {
            var configManager    = new ServiceDbConfigManager("TestService");
            var dbAccess         = CreateDbAccess(configManager);
            var dbAccessProvider = new TestDocumentDbAccessProvider(dbAccess);

            var fruitStore = new FruitStore(dbAccessProvider);

            await dbAccess.Open(new[] { fruitStore });

            var apples = new List <Apple>();
            var pears  = new List <Pear>();

            for (int i = 0; i < 50; i++)
            {
                var gala = new Apple
                {
                    Id   = Guid.NewGuid(),
                    Type = "Gala"
                };

                var fuji = new Apple
                {
                    Id   = Guid.NewGuid(),
                    Type = "Fuji"
                };

                await fruitStore.UpsertApple(gala);

                await fruitStore.UpsertApple(fuji);

                apples.AddRange(new[] { gala, fuji });

                var bartlett = new Pear
                {
                    Id     = Guid.NewGuid(),
                    Colour = "Red"
                };

                var comice = new Pear
                {
                    Id     = Guid.NewGuid(),
                    Colour = "Green"
                };

                await fruitStore.UpsertPear(bartlett);

                await fruitStore.UpsertPear(comice);

                pears.AddRange(new[] { bartlett, comice });
            }

            var r1 = await fruitStore.GetAppleByQuery("[x].Type = 'Gala'");

            Assert.Equal(50, r1.Length);
            Assert.True(r1.All(x => x.Type == "Gala"));

            var r2 = await fruitStore.GetAllApples();

            Assert.Equal(100, r2.Length);

            var lastApple = apples.Last();
            var r3        = await fruitStore.GetAppleById(lastApple.Id);

            Assert.Equal(lastApple.Id, r3.Id);
            Assert.Equal(lastApple.Type, r3.Type);

            var r4 = await fruitStore.GetAppleByIds(apples.Select(x => x.Id.ToString()));

            Assert.Equal(100, r4.Length);

            var r5 = await fruitStore.GetPearByQuery("[x].Colour = @colour", new[] { new DbParameter("colour", "Red") });

            Assert.Equal(50, r5.Length);
            Assert.True(r5.All(x => x.Colour == "Red"));

            var r6 = await fruitStore.GetAllPears();

            Assert.Equal(100, r6.Length);

            var lastPear = pears.Last();
            var r7       = await fruitStore.GetPearById(lastPear.Id);

            Assert.Equal(lastPear.Id, r7.Id);
            Assert.Equal(lastPear.Colour, r7.Colour);
        }
        public async void TestMultipleServiceStores()
        {
            var configManager    = new ServiceDbConfigManager("TestService");
            var dbAccess         = CreateDbAccess(configManager);
            var dbAccessProvider = new TestDocumentDbAccessProvider(dbAccess);

            var fruitStore  = new FruitStore(dbAccessProvider);
            var flowerStore = new FlowerStore(dbAccessProvider);

            await dbAccess.Open(new VersionedDocumentStore[] { fruitStore, flowerStore });

            var apples  = new List <Apple>();
            var daisies = new List <Daisy>();

            for (int i = 0; i < 10; i++)
            {
                var gala = new Apple
                {
                    Id   = Guid.NewGuid(),
                    Type = "Gala"
                };

                var fuji = new Apple
                {
                    Id   = Guid.NewGuid(),
                    Type = "Fuji"
                };

                await fruitStore.UpsertApple(gala);

                await fruitStore.UpsertApple(fuji);

                apples.AddRange(new[] { gala, fuji });

                var daisy = new Daisy
                {
                    Id     = Guid.NewGuid(),
                    Colour = "Red"
                };

                await flowerStore.Upsert(daisy);

                daisies.Add(daisy);
            }

            var r1 = await fruitStore.GetAppleByQuery("[x].Type = 'Gala'");

            Assert.Equal(10, r1.Length);
            Assert.True(r1.All(x => x.Type == "Gala"));

            var r2 = await fruitStore.GetAllApples();

            Assert.Equal(20, r2.Length);

            var lastApple = apples.Last();
            var r3        = await fruitStore.GetAppleById(lastApple.Id);

            Assert.Equal(lastApple.Id, r3.Id);
            Assert.Equal(lastApple.Type, r3.Type);

            var r4 = await fruitStore.GetAppleByIds(apples.Select(x => x.Id.ToString()));

            Assert.Equal(20, r4.Length);

            var lastDaisy = daisies.Last();
            var r5        = await flowerStore.GetById(lastDaisy.Id);

            Assert.Equal(lastDaisy.Id, r5.Id);
            Assert.Equal(lastDaisy.Colour, r5.Colour);
        }