Browse Source

CosmosDB adjustments.

pull/349/head
Sebastian Stehle 7 years ago
parent
commit
7ef3570392
  1. 28
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentDraftCollection.cs
  2. 20
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentPublishedCollection.cs
  3. 9
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs
  4. 12
      src/Squidex.Domain.Apps.Entities.MongoDb/History/MongoHistoryEventRepository.cs
  5. 15
      src/Squidex.Infrastructure.Azure/EventSourcing/CosmosDbEventStore.cs
  6. 8
      src/Squidex.Infrastructure.Azure/EventSourcing/CosmosDbEventStore_Writer.cs
  7. 27
      src/Squidex.Infrastructure.Azure/EventSourcing/FilterBuilder.cs
  8. 62
      src/Squidex.Infrastructure.Azure/EventSourcing/FilterExtensions.cs
  9. 14
      src/Squidex.Infrastructure.MongoDb/MongoDb/MongoDbOptions.cs
  10. 4
      src/Squidex.Infrastructure/Configuration/Alternatives.cs
  11. 2
      src/Squidex.Infrastructure/Configuration/ConfigurationExtensions.cs
  12. 2
      src/Squidex/Config/Domain/AssetServices.cs
  13. 2
      src/Squidex/Config/Domain/EventStoreServices.cs
  14. 11
      src/Squidex/Config/Domain/StoreServices.cs
  15. 4
      src/Squidex/Config/Orleans/OrleansServices.cs
  16. 12
      tools/Migrate_01/Migrations/ConvertOldSnapshotStores.cs

28
src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentDraftCollection.cs

@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using MongoDB.Driver;
using NodaTime;
using Squidex.Domain.Apps.Core.ConvertContent;
@ -29,28 +30,33 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{
internal sealed class MongoContentDraftCollection : MongoContentCollection
{
public MongoContentDraftCollection(IMongoDatabase database, IJsonSerializer serializer)
private readonly MongoDbOptions options;
public MongoContentDraftCollection(IMongoDatabase database, IJsonSerializer serializer, IOptions<MongoDbOptions> options)
: base(database, serializer, "State_Content_Draft")
{
this.options = options.Value;
}
protected override async Task SetupCollectionAsync(IMongoCollection<MongoContentEntity> collection, CancellationToken ct = default)
{
await collection.Indexes.CreateManyAsync(
new[]
{
new CreateIndexModel<MongoContentEntity>(
Index
.Ascending(x => x.IndexedSchemaId)
.Ascending(x => x.Id)
.Ascending(x => x.IsDeleted)),
await collection.Indexes.CreateOneAsync(
new CreateIndexModel<MongoContentEntity>(
Index
.Ascending(x => x.IndexedSchemaId)
.Ascending(x => x.Id)
.Ascending(x => x.IsDeleted)), null, ct);
if (!options.IsCosmosDb)
{
await collection.Indexes.CreateOneAsync(
new CreateIndexModel<MongoContentEntity>(
Index
.Text(x => x.DataText)
.Ascending(x => x.IndexedSchemaId)
.Ascending(x => x.IsDeleted)
.Ascending(x => x.Status))
}, ct);
.Ascending(x => x.Status)), null, ct);
}
await base.SetupCollectionAsync(collection, ct);
}

20
src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentPublishedCollection.cs

@ -8,6 +8,7 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using MongoDB.Driver;
using Squidex.Domain.Apps.Core.ConvertContent;
using Squidex.Domain.Apps.Entities.Apps;
@ -20,19 +21,24 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{
internal sealed class MongoContentPublishedCollection : MongoContentCollection
{
public MongoContentPublishedCollection(IMongoDatabase database, IJsonSerializer serializer)
private readonly MongoDbOptions options;
public MongoContentPublishedCollection(IMongoDatabase database, IJsonSerializer serializer, IOptions<MongoDbOptions> options)
: base(database, serializer, "State_Content_Published")
{
this.options = options.Value;
}
protected override async Task SetupCollectionAsync(IMongoCollection<MongoContentEntity> collection, CancellationToken ct = default)
{
await collection.Indexes.CreateManyAsync(
new[]
{
new CreateIndexModel<MongoContentEntity>(Index.Text(x => x.DataText).Ascending(x => x.IndexedSchemaId)),
new CreateIndexModel<MongoContentEntity>(Index.Ascending(x => x.IndexedSchemaId).Ascending(x => x.Id))
}, ct);
await collection.Indexes.CreateOneAsync(
new CreateIndexModel<MongoContentEntity>(Index.Ascending(x => x.IndexedSchemaId).Ascending(x => x.Id)), null, ct);
if (!options.IsCosmosDb)
{
await collection.Indexes.CreateOneAsync(
new CreateIndexModel<MongoContentEntity>(Index.Text(x => x.DataText).Ascending(x => x.IndexedSchemaId)), null, ct);
}
await base.SetupCollectionAsync(collection, ct);
}

9
src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs

@ -9,6 +9,7 @@ using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using MongoDB.Driver;
using NodaTime;
using Squidex.Domain.Apps.Core.Contents;
@ -19,6 +20,7 @@ using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.Queries;
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
@ -31,17 +33,18 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
private readonly MongoContentDraftCollection contentsDraft;
private readonly MongoContentPublishedCollection contentsPublished;
public MongoContentRepository(IMongoDatabase database, IAppProvider appProvider, IJsonSerializer serializer)
public MongoContentRepository(IMongoDatabase database, IAppProvider appProvider, IJsonSerializer serializer, IOptions<MongoDbOptions> options)
{
Guard.NotNull(appProvider, nameof(appProvider));
Guard.NotNull(serializer, nameof(serializer));
Guard.NotNull(options, nameof(options));
this.appProvider = appProvider;
this.serializer = serializer;
contentsDraft = new MongoContentDraftCollection(database, serializer);
contentsPublished = new MongoContentPublishedCollection(database, serializer);
contentsDraft = new MongoContentDraftCollection(database, serializer, options);
contentsPublished = new MongoContentPublishedCollection(database, serializer, options);
this.database = database;
}

12
src/Squidex.Domain.Apps.Entities.MongoDb/History/MongoHistoryEventRepository.cs

@ -9,6 +9,8 @@ using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using MongoDB.Bson.Serialization;
using MongoDB.Driver;
using Squidex.Domain.Apps.Entities.History;
using Squidex.Domain.Apps.Entities.History.Repositories;
@ -18,9 +20,17 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.History
{
public class MongoHistoryEventRepository : MongoRepositoryBase<HistoryEvent>, IHistoryEventRepository
{
public MongoHistoryEventRepository(IMongoDatabase database)
public MongoHistoryEventRepository(IMongoDatabase database, IOptions<MongoDbOptions> options)
: base(database)
{
if (options.Value.IsCosmosDb)
{
var classMap = BsonClassMap.RegisterClassMap<HistoryEvent>();
classMap.MapProperty(x => x.Created)
.SetElementName("_ts");
classMap.AutoMap();
}
}
protected override string CollectionName()

15
src/Squidex.Infrastructure.Azure/EventSourcing/CosmosDbEventStore.cs

@ -84,6 +84,21 @@ namespace Squidex.Infrastructure.EventSourcing
await documentClient.CreateDocumentCollectionIfNotExistsAsync(databaseUri,
new DocumentCollection
{
IndexingPolicy = new IndexingPolicy
{
IncludedPaths = new Collection<IncludedPath>
{
new IncludedPath
{
Path = "/*",
Indexes = new Collection<Index>
{
Index.Range(DataType.Number),
Index.Range(DataType.String),
}
}
}
},
UniqueKeyPolicy = new UniqueKeyPolicy
{
UniqueKeys = new Collection<UniqueKey>

8
src/Squidex.Infrastructure.Azure/EventSourcing/CosmosDbEventStore_Writer.cs

@ -59,7 +59,7 @@ namespace Squidex.Infrastructure.EventSourcing
return;
}
var currentVersion = GetEventStreamOffset(streamName);
var currentVersion = await GetEventStreamOffsetAsync(streamName);
if (expectedVersion != EtagVersion.Any && expectedVersion != currentVersion)
{
@ -80,7 +80,7 @@ namespace Squidex.Infrastructure.EventSourcing
{
if (ex.StatusCode == HttpStatusCode.Conflict)
{
currentVersion = GetEventStreamOffset(streamName);
currentVersion = await GetEventStreamOffsetAsync(streamName);
if (expectedVersion != EtagVersion.Any)
{
@ -105,13 +105,13 @@ namespace Squidex.Infrastructure.EventSourcing
}
}
private long GetEventStreamOffset(string streamName)
private async Task<long> GetEventStreamOffsetAsync(string streamName)
{
var query =
documentClient.CreateDocumentQuery<CosmosDbEventCommit>(collectionUri,
FilterBuilder.LastPosition(streamName));
var document = query.ToList().FirstOrDefault();
var document = await query.FirstOrDefaultAsync();
if (document != null)
{

27
src/Squidex.Infrastructure.Azure/EventSourcing/FilterBuilder.cs

@ -5,41 +5,14 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.Documents.Linq;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Infrastructure.EventSourcing
{
internal static class FilterBuilder
{
public static async Task QueryAsync(this DocumentClient documentClient, Uri collectionUri, SqlQuerySpec querySpec, Func<CosmosDbEventCommit, Task> handler, CancellationToken ct = default)
{
var query =
documentClient.CreateDocumentQuery<CosmosDbEventCommit>(collectionUri, querySpec)
.AsDocumentQuery();
using (query)
{
var result = new List<StoredEvent>();
while (query.HasMoreResults && !ct.IsCancellationRequested)
{
var commits = await query.ExecuteNextAsync<CosmosDbEventCommit>(ct);
foreach (var commit in commits)
{
await handler(commit);
}
}
}
}
public static SqlQuerySpec AllIds(string streamName)
{
var query =

62
src/Squidex.Infrastructure.Azure/EventSourcing/FilterExtensions.cs

@ -0,0 +1,62 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.Documents.Linq;
namespace Squidex.Infrastructure.EventSourcing
{
internal static class FilterExtensions
{
public static async Task<T> FirstOrDefaultAsync<T>(this IQueryable<T> queryable, CancellationToken ct = default)
{
var documentQuery = queryable.AsDocumentQuery();
using (documentQuery)
{
if (documentQuery.HasMoreResults)
{
var results = await documentQuery.ExecuteNextAsync<T>(ct);
return results.FirstOrDefault();
}
}
return default;
}
public static Task QueryAsync(this DocumentClient documentClient, Uri collectionUri, SqlQuerySpec querySpec, Func<CosmosDbEventCommit, Task> handler, CancellationToken ct = default)
{
var query = documentClient.CreateDocumentQuery<CosmosDbEventCommit>(collectionUri, querySpec);
return query.QueryAsync(handler, ct);
}
public static async Task QueryAsync<T>(this IQueryable<T> queryable, Func<T, Task> handler, CancellationToken ct = default)
{
var documentQuery = queryable.AsDocumentQuery();
using (documentQuery)
{
while (documentQuery.HasMoreResults && !ct.IsCancellationRequested)
{
var items = await documentQuery.ExecuteNextAsync<T>(ct);
foreach (var item in items)
{
await handler(item);
}
}
}
}
}
}

14
src/Squidex.Infrastructure.MongoDb/MongoDb/MongoDbOptions.cs

@ -0,0 +1,14 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Infrastructure.MongoDb
{
public sealed class MongoDbOptions
{
public bool IsCosmosDb { get; set; }
}
}

4
src/Squidex.Infrastructure/Configuration/Options.cs → src/Squidex.Infrastructure/Configuration/Alternatives.cs

@ -10,9 +10,9 @@ using System.Collections.Generic;
namespace Microsoft.Extensions.Configuration
{
public sealed class Options : Dictionary<string, Action>
public sealed class Alternatives : Dictionary<string, Action>
{
public Options()
public Alternatives()
: base(StringComparer.OrdinalIgnoreCase)
{
}

2
src/Squidex.Infrastructure/Configuration/ConfigurationExtensions.cs

@ -46,7 +46,7 @@ namespace Microsoft.Extensions.Configuration
return value;
}
public static string ConfigureByOption(this IConfiguration config, string path, Options options)
public static string ConfigureByOption(this IConfiguration config, string path, Alternatives options)
{
var value = config.GetRequiredValue(path);

2
src/Squidex/Config/Domain/AssetServices.cs

@ -20,7 +20,7 @@ namespace Squidex.Config.Domain
{
public static void AddMyAssetServices(this IServiceCollection services, IConfiguration config)
{
config.ConfigureByOption("assetStore:type", new Options
config.ConfigureByOption("assetStore:type", new Alternatives
{
["Default"] = () =>
{

2
src/Squidex/Config/Domain/EventStoreServices.cs

@ -26,7 +26,7 @@ namespace Squidex.Config.Domain
{
public static void AddMyEventStoreServices(this IServiceCollection services, IConfiguration config)
{
config.ConfigureByOption("eventStore:type", new Options
config.ConfigureByOption("eventStore:type", new Alternatives
{
["MongoDb"] = () =>
{

11
src/Squidex/Config/Domain/StoreServices.cs

@ -10,6 +10,7 @@ using IdentityServer4.Stores;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Migrate_01.Migrations;
using MongoDB.Driver;
using Squidex.Domain.Apps.Entities;
@ -31,6 +32,7 @@ using Squidex.Infrastructure.Diagnostics;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Migrations;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.States;
using Squidex.Infrastructure.UsageTracking;
@ -40,7 +42,7 @@ namespace Squidex.Config.Domain
{
public static void AddMyStoreServices(this IServiceCollection services, IConfiguration config)
{
config.ConfigureByOption("store:type", new Options
config.ConfigureByOption("store:type", new Alternatives
{
["MongoDB"] = () =>
{
@ -48,6 +50,10 @@ namespace Squidex.Config.Domain
var mongoDatabaseName = config.GetRequiredValue("store:mongoDb:database");
var mongoContentDatabaseName = config.GetOptionalValue("store:mongoDb:contentDatabase", mongoDatabaseName);
var isCosmosDb = config.GetOptionalValue<bool>("store:mongoDB:isCosmosDB");
services.Configure<MongoDbOptions>(config.GetSection("store:mongoDB"));
services.AddSingleton(typeof(ISnapshotStore<,>), typeof(MongoSnapshotStore<,>));
services.AddSingletonAs(_ => Singletons<IMongoClient>.GetOrAdd(mongoConfiguration, s => new MongoClient(s)))
@ -97,7 +103,8 @@ namespace Squidex.Config.Domain
services.AddSingletonAs(c => new MongoContentRepository(
c.GetRequiredService<IMongoClient>().GetDatabase(mongoContentDatabaseName),
c.GetRequiredService<IAppProvider>(),
c.GetRequiredService<IJsonSerializer>()))
c.GetRequiredService<IJsonSerializer>(),
c.GetRequiredService<IOptions<MongoDbOptions>>()))
.AsOptional<IContentRepository>()
.AsOptional<ISnapshotStore<ContentState, Guid>>()
.AsOptional<IEventConsumer>();

4
src/Squidex/Config/Orleans/OrleansServices.cs

@ -58,7 +58,7 @@ namespace Squidex.Config.Orleans
var siloPort = config.GetOptionalValue("orleans:siloPort", 11111);
config.ConfigureByOption("orleans:clustering", new Options
config.ConfigureByOption("orleans:clustering", new Alternatives
{
["MongoDB"] = () =>
{
@ -81,7 +81,7 @@ namespace Squidex.Config.Orleans
}
});
config.ConfigureByOption("store:type", new Options
config.ConfigureByOption("store:type", new Alternatives
{
["MongoDB"] = () =>
{

12
tools/Migrate_01/Migrations/ConvertOldSnapshotStores.cs

@ -7,23 +7,33 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using Squidex.Infrastructure.Migrations;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.Tasks;
namespace Migrate_01.Migrations
{
public sealed class ConvertOldSnapshotStores : IMigration
{
private readonly IMongoDatabase database;
private readonly MongoDbOptions options;
public ConvertOldSnapshotStores(IMongoDatabase database)
public ConvertOldSnapshotStores(IMongoDatabase database, IOptions<MongoDbOptions> options)
{
this.database = database;
this.options = options.Value;
}
public Task UpdateAsync()
{
if (options.IsCosmosDb)
{
return TaskHelper.Done;
}
var collections = new[]
{
"States_Apps",

Loading…
Cancel
Save