mirror of https://github.com/Squidex/squidex.git
60 changed files with 943 additions and 741 deletions
Binary file not shown.
@ -0,0 +1 @@ |
|||||
|
vjOVukk+3TGMSUoQkGnSjeD6k8j6x0WWqjwFntWpc4BijggTJYyND35YV9+Or63br+003NEpxYtdJfQwgwyA2Q== |
||||
@ -0,0 +1,29 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd"> |
||||
|
<metadata> |
||||
|
<id>Orleans.Providers.MongoDB</id> |
||||
|
<version>2.0.0-preview2</version> |
||||
|
<authors>laredoza</authors> |
||||
|
<owners>laredoza</owners> |
||||
|
<requireLicenseAcceptance>false</requireLicenseAcceptance> |
||||
|
<licenseUrl>https://github.com/dotnet/Orleans#license</licenseUrl> |
||||
|
<projectUrl>https://github.com/OrleansContrib/Orleans.Providers.MongoDB</projectUrl> |
||||
|
<description>A MongoDb implementation of the Orleans Providers. This includes the Membership (IMembershipTable and IGatewayListProvider), Reminder (IReminderTable), MongoStatisticsPublisher and IStorageProvider providers.</description> |
||||
|
<releaseNotes>Binary serialization added to the Storage Provider by https://github.com/orthrus. The UseJsonFormat="true" parameter controls this. |
||||
|
|
||||
|
Switching between formats while there is data in the storage tables will end in tears (data will be lost). So don't do it. |
||||
|
|
||||
|
Feedback would be appreciated.</releaseNotes> |
||||
|
<copyright>MIT</copyright> |
||||
|
<tags>Orleans OrleansProviders MongoDB</tags> |
||||
|
<dependencies> |
||||
|
<group targetFramework=".NETStandard2.0"> |
||||
|
<dependency id="Microsoft.Orleans.OrleansProviders" version="2.0.0-beta1" exclude="Build,Analyzers" /> |
||||
|
<dependency id="Microsoft.Orleans.OrleansRuntime" version="2.0.0-beta1" exclude="Build,Analyzers" /> |
||||
|
<dependency id="MongoDB.Driver" version="2.4.4" exclude="Build,Analyzers" /> |
||||
|
<dependency id="System.Reflection.Metadata" version="1.5.0" exclude="Build,Analyzers" /> |
||||
|
<dependency id="System.ValueTuple" version="4.4.0" exclude="Build,Analyzers" /> |
||||
|
</group> |
||||
|
</dependencies> |
||||
|
</metadata> |
||||
|
</package> |
||||
@ -1,21 +1,20 @@ |
|||||
// ==========================================================================
|
// ==========================================================================
|
||||
// Helper.cs
|
// AppEntityExtensions.cs
|
||||
// Squidex Headless CMS
|
// Squidex Headless CMS
|
||||
// ==========================================================================
|
// ==========================================================================
|
||||
// Copyright (c) Squidex Group
|
// Copyright (c) Squidex Group
|
||||
// All rights reserved.
|
// All rights reserved.
|
||||
// ==========================================================================
|
// ==========================================================================
|
||||
|
|
||||
using System; |
using Squidex.Domain.Apps.Core; |
||||
using Squidex.Infrastructure.CQRS.Events; |
|
||||
|
|
||||
namespace Benchmarks.Utils |
namespace Squidex.Domain.Apps.Read.Apps |
||||
{ |
{ |
||||
public static class Helper |
public static class AppEntityExtensions |
||||
{ |
{ |
||||
public static EventData CreateEventData() |
public static PartitionResolver PartitionResolver(this IAppEntity entity) |
||||
{ |
{ |
||||
return new EventData { EventId = Guid.NewGuid(), Metadata = "EventMetdata", Payload = "EventPayload", Type = "MyEvent" }; |
return entity.LanguagesConfig.ToResolver(); |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
// ==========================================================================
|
||||
|
// IAppState.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Squidex.Domain.Apps.Read.Apps; |
||||
|
using Squidex.Domain.Apps.Read.Rules; |
||||
|
using Squidex.Domain.Apps.Read.Schemas; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Read |
||||
|
{ |
||||
|
public interface IAppState |
||||
|
{ |
||||
|
Task<IAppEntity> GetAppAsync(Guid appId); |
||||
|
|
||||
|
Task<ISchemaEntity> GetSchemaAsync(Guid appId, Guid id, bool provideDeleted = false); |
||||
|
|
||||
|
Task<ISchemaEntity> GetSchemaAsync(Guid appId, string name, bool provideDeleted = false); |
||||
|
|
||||
|
Task<List<ISchemaEntity>> GetSchemasAsync(Guid appId); |
||||
|
|
||||
|
Task<List<IRuleEntity>> GetRulesAsync(Guid appId); |
||||
|
|
||||
|
Task<List<IAppEntity>> GetUserApps(string userId); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,70 @@ |
|||||
|
// ==========================================================================
|
||||
|
// StateEventConsumer.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Threading.Tasks; |
||||
|
using Orleans; |
||||
|
using Squidex.Domain.Apps.Events; |
||||
|
using Squidex.Domain.Apps.Events.Apps; |
||||
|
using Squidex.Domain.Apps.Read.State.Orleans.Grains; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.CQRS.Events; |
||||
|
using Squidex.Infrastructure.Tasks; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Read.State |
||||
|
{ |
||||
|
public sealed class AppStateEventConsumer : IEventConsumer |
||||
|
{ |
||||
|
private readonly IGrainFactory factory; |
||||
|
|
||||
|
public string Name |
||||
|
{ |
||||
|
get { return typeof(AppStateEventConsumer).Name; } |
||||
|
} |
||||
|
|
||||
|
public string EventsFilter |
||||
|
{ |
||||
|
get { return @"(^app-)|(^schema-)|(^rule\-)"; } |
||||
|
} |
||||
|
|
||||
|
public AppStateEventConsumer(IGrainFactory factory) |
||||
|
{ |
||||
|
Guard.NotNull(factory, nameof(factory)); |
||||
|
|
||||
|
this.factory = factory; |
||||
|
} |
||||
|
|
||||
|
public Task ClearAsync() |
||||
|
{ |
||||
|
return TaskHelper.Done; |
||||
|
} |
||||
|
|
||||
|
public async Task On(Envelope<IEvent> @event) |
||||
|
{ |
||||
|
if (@event.Payload is AppEvent appEvent) |
||||
|
{ |
||||
|
var appGrain = factory.GetGrain<IAppStateGrain>(appEvent.AppId.Id); |
||||
|
|
||||
|
await appGrain.HandleAsync(new EventMessage { Event = @event }); |
||||
|
} |
||||
|
|
||||
|
if (@event.Payload is AppContributorAssigned contributorAssigned) |
||||
|
{ |
||||
|
var userGrain = factory.GetGrain<IAppUserGrain>(contributorAssigned.ContributorId); |
||||
|
|
||||
|
await userGrain.AddSchemaAsync(contributorAssigned.AppId.Id); |
||||
|
} |
||||
|
|
||||
|
if (@event.Payload is AppContributorRemoved contributorRemoved) |
||||
|
{ |
||||
|
var userGrain = factory.GetGrain<IAppUserGrain>(contributorRemoved.ContributorId); |
||||
|
|
||||
|
await userGrain.RemoveSchemaAsync(contributorRemoved.AppId.Id); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,22 +1,19 @@ |
|||||
// ==========================================================================
|
// ==========================================================================
|
||||
// IMongoEntity.cs
|
// EventMessage.cs
|
||||
// Squidex Headless CMS
|
// Squidex Headless CMS
|
||||
// ==========================================================================
|
// ==========================================================================
|
||||
// Copyright (c) Squidex Group
|
// Copyright (c) Squidex Group
|
||||
// All rights reserved.
|
// All rights reserved.
|
||||
// ==========================================================================
|
// ==========================================================================
|
||||
|
|
||||
using System; |
using Orleans.Concurrency; |
||||
using NodaTime; |
using Squidex.Infrastructure.CQRS.Events; |
||||
|
|
||||
namespace Squidex.Infrastructure.MongoDb |
namespace Squidex.Domain.Apps.Read.State.Orleans.Grains |
||||
{ |
{ |
||||
public interface IMongoEntity |
[Immutable] |
||||
|
public sealed class EventMessage |
||||
{ |
{ |
||||
Guid Id { get; set; } |
public Envelope<IEvent> Event { get; set; } |
||||
|
|
||||
Instant Created { get; set; } |
|
||||
|
|
||||
Instant LastModified { get; set; } |
|
||||
} |
} |
||||
} |
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
// ==========================================================================
|
||||
|
// IAppStateGrain.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Orleans; |
||||
|
using Squidex.Domain.Apps.Read.Apps; |
||||
|
using Squidex.Domain.Apps.Read.Rules; |
||||
|
using Squidex.Domain.Apps.Read.Schemas; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Read.State.Orleans.Grains |
||||
|
{ |
||||
|
public interface IAppStateGrain : IGrainWithGuidKey |
||||
|
{ |
||||
|
Task<IAppEntity> GetAppAsync(); |
||||
|
|
||||
|
Task<ISchemaEntity> GetSchemaAsync(Guid id, bool provideDeleted = false); |
||||
|
|
||||
|
Task<ISchemaEntity> GetSchemaAsync(string name, bool provideDeleted = false); |
||||
|
|
||||
|
Task<List<ISchemaEntity>> GetSchemasAsync(); |
||||
|
|
||||
|
Task<List<IRuleEntity>> GetRulesAsync(); |
||||
|
|
||||
|
Task HandleAsync(EventMessage message); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
// ==========================================================================
|
||||
|
// IAppUserGrain.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Orleans; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Read.State.Orleans.Grains |
||||
|
{ |
||||
|
public interface IAppUserGrain : IGrainWithStringKey |
||||
|
{ |
||||
|
Task<List<Guid>> GetSchemaIdsAsync(); |
||||
|
|
||||
|
Task AddSchemaAsync(Guid schemaId); |
||||
|
|
||||
|
Task RemoveSchemaAsync(Guid schemaId); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,77 @@ |
|||||
|
// ==========================================================================
|
||||
|
// AppStateGrain.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Orleans; |
||||
|
using Orleans.Providers; |
||||
|
using Squidex.Domain.Apps.Core.Schemas; |
||||
|
using Squidex.Domain.Apps.Read.Apps; |
||||
|
using Squidex.Domain.Apps.Read.Rules; |
||||
|
using Squidex.Domain.Apps.Read.Schemas; |
||||
|
using Squidex.Infrastructure; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations |
||||
|
{ |
||||
|
[StorageProvider(ProviderName = "Default")] |
||||
|
public sealed class AppStateGrain : Grain<AppStateGrainState>, IAppStateGrain |
||||
|
{ |
||||
|
private readonly FieldRegistry fieldRegistry; |
||||
|
|
||||
|
public AppStateGrain(FieldRegistry fieldRegistry) |
||||
|
{ |
||||
|
Guard.NotNull(fieldRegistry, nameof(fieldRegistry)); |
||||
|
|
||||
|
this.fieldRegistry = fieldRegistry; |
||||
|
} |
||||
|
|
||||
|
public Task<IAppEntity> GetAppAsync() |
||||
|
{ |
||||
|
var value = State.App; |
||||
|
|
||||
|
return Task.FromResult<IAppEntity>(value); |
||||
|
} |
||||
|
|
||||
|
public Task<List<IRuleEntity>> GetRulesAsync() |
||||
|
{ |
||||
|
var value = State.Rules.Values.OfType<IRuleEntity>().ToList(); |
||||
|
|
||||
|
return Task.FromResult(value); |
||||
|
} |
||||
|
|
||||
|
public Task<List<ISchemaEntity>> GetSchemasAsync() |
||||
|
{ |
||||
|
var value = State.Schemas.Values.Where(x => !x.IsDeleted).OfType<ISchemaEntity>().ToList(); |
||||
|
|
||||
|
return Task.FromResult(value); |
||||
|
} |
||||
|
|
||||
|
public Task<ISchemaEntity> GetSchemaAsync(Guid id, bool provideDeleted = false) |
||||
|
{ |
||||
|
var value = State.Schemas.Values.FirstOrDefault(x => x.Id == id && (!x.IsDeleted || provideDeleted)); |
||||
|
|
||||
|
return Task.FromResult<ISchemaEntity>(value); |
||||
|
} |
||||
|
|
||||
|
public Task<ISchemaEntity> GetSchemaAsync(string name, bool provideDeleted = false) |
||||
|
{ |
||||
|
var value = State.Schemas.Values.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase) && (!x.IsDeleted || provideDeleted)); |
||||
|
|
||||
|
return Task.FromResult<ISchemaEntity>(value); |
||||
|
} |
||||
|
|
||||
|
public Task HandleAsync(EventMessage message) |
||||
|
{ |
||||
|
State.Apply(message.Event, fieldRegistry); |
||||
|
|
||||
|
return WriteStateAsync(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,276 @@ |
|||||
|
// ==========================================================================
|
||||
|
// AppStateGrainState.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Newtonsoft.Json; |
||||
|
using Squidex.Domain.Apps.Core.Apps; |
||||
|
using Squidex.Domain.Apps.Core.Schemas; |
||||
|
using Squidex.Domain.Apps.Events.Apps; |
||||
|
using Squidex.Domain.Apps.Events.Apps.Utils; |
||||
|
using Squidex.Domain.Apps.Events.Rules; |
||||
|
using Squidex.Domain.Apps.Events.Rules.Utils; |
||||
|
using Squidex.Domain.Apps.Events.Schemas; |
||||
|
using Squidex.Domain.Apps.Events.Schemas.Old; |
||||
|
using Squidex.Domain.Apps.Events.Schemas.Utils; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.CQRS.Events; |
||||
|
using Squidex.Infrastructure.Reflection; |
||||
|
|
||||
|
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations |
||||
|
{ |
||||
|
public sealed class AppStateGrainState |
||||
|
{ |
||||
|
[JsonProperty] |
||||
|
public JsonAppEntity App { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public Dictionary<Guid, JsonRuleEntity> Rules { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public Dictionary<Guid, JsonSchemaEntity> Schemas { get; set; } |
||||
|
|
||||
|
public void Reset() |
||||
|
{ |
||||
|
Rules = new Dictionary<Guid, JsonRuleEntity>(); |
||||
|
|
||||
|
Schemas = new Dictionary<Guid, JsonSchemaEntity>(); |
||||
|
} |
||||
|
|
||||
|
public void Apply(Envelope<IEvent> envelope, FieldRegistry registry) |
||||
|
{ |
||||
|
switch (envelope.Payload) |
||||
|
{ |
||||
|
case AppCreated @event: |
||||
|
{ |
||||
|
Reset(); |
||||
|
|
||||
|
App = EntityMapper.Create<JsonAppEntity>(@event, envelope.Headers, a => |
||||
|
{ |
||||
|
SimpleMapper.Map(envelope, a); |
||||
|
|
||||
|
a.Clients = new AppClients(); |
||||
|
a.Contributors = new AppContributors(); |
||||
|
|
||||
|
a.LanguagesConfig = LanguagesConfig.Build(Language.EN); |
||||
|
}); |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
case AppPlanChanged @event: |
||||
|
App.Update(@event, envelope.Headers, a => |
||||
|
{ |
||||
|
SimpleMapper.Map(envelope, a); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case AppClientAttached @event: |
||||
|
App.Update(@event, envelope.Headers, a => |
||||
|
{ |
||||
|
a.Clients.Apply(@event); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case AppClientRevoked @event: |
||||
|
App.Update(@event, envelope.Headers, a => |
||||
|
{ |
||||
|
a.Clients.Apply(@event); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case AppClientRenamed @event: |
||||
|
App.Update(@event, envelope.Headers, a => |
||||
|
{ |
||||
|
a.Clients.Apply(@event); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case AppClientUpdated @event: |
||||
|
App.Update(@event, envelope.Headers, a => |
||||
|
{ |
||||
|
a.Clients.Apply(@event); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case AppContributorRemoved @event: |
||||
|
App.Update(@event, envelope.Headers, a => |
||||
|
{ |
||||
|
a.Contributors.Apply(@event); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case AppContributorAssigned @event: |
||||
|
App.Update(@event, envelope.Headers, a => |
||||
|
{ |
||||
|
a.Contributors.Apply(@event); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case AppLanguageAdded @event: |
||||
|
App.Update(@event, envelope.Headers, a => |
||||
|
{ |
||||
|
a.LanguagesConfig.Apply(@event); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case AppLanguageRemoved @event: |
||||
|
App.Update(@event, envelope.Headers, a => |
||||
|
{ |
||||
|
a.LanguagesConfig.Apply(@event); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case AppLanguageUpdated @event: |
||||
|
App.Update(@event, envelope.Headers, a => |
||||
|
{ |
||||
|
a.LanguagesConfig.Apply(@event); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case RuleCreated @event: |
||||
|
Rules[@event.RuleId] = EntityMapper.Create<JsonRuleEntity>(@event, envelope.Headers, r => |
||||
|
{ |
||||
|
r.Rule = RuleEventDispatcher.Create(@event); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case RuleUpdated @event: |
||||
|
Rules[@event.RuleId].Update(@event, envelope.Headers, r => |
||||
|
{ |
||||
|
r.Rule.Apply(@event); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case RuleEnabled @event: |
||||
|
Rules[@event.RuleId].Update(@event, envelope.Headers, r => |
||||
|
{ |
||||
|
r.Rule.Apply(@event); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case RuleDisabled @event: |
||||
|
Rules.Remove(@event.RuleId); |
||||
|
break; |
||||
|
|
||||
|
case SchemaCreated @event: |
||||
|
Schemas[@event.SchemaId.Id] = EntityMapper.Create<JsonSchemaEntity>(@event, envelope.Headers, s => |
||||
|
{ |
||||
|
s.SchemaDef = SchemaEventDispatcher.Create(@event, registry); |
||||
|
|
||||
|
SimpleMapper.Map(@event, s); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case FieldAdded @event: |
||||
|
Schemas[@event.SchemaId.Id].Update(@event, envelope.Headers, s => |
||||
|
{ |
||||
|
s.SchemaDef.Apply(@event, registry); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case FieldDeleted @event: |
||||
|
Schemas[@event.SchemaId.Id].Update(@event, envelope.Headers, s => |
||||
|
{ |
||||
|
s.SchemaDef.Apply(@event); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case FieldLocked @event: |
||||
|
Schemas[@event.SchemaId.Id].Update(@event, envelope.Headers, s => |
||||
|
{ |
||||
|
s.SchemaDef.Apply(@event); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case FieldHidden @event: |
||||
|
Schemas[@event.SchemaId.Id].Update(@event, envelope.Headers, s => |
||||
|
{ |
||||
|
s.SchemaDef.Apply(@event); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case FieldShown @event: |
||||
|
Schemas[@event.SchemaId.Id].Update(@event, envelope.Headers, s => |
||||
|
{ |
||||
|
s.SchemaDef.Apply(@event); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case FieldDisabled @event: |
||||
|
Schemas[@event.SchemaId.Id].Update(@event, envelope.Headers, s => |
||||
|
{ |
||||
|
s.SchemaDef.Apply(@event); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case FieldEnabled @event: |
||||
|
Schemas[@event.SchemaId.Id].Update(@event, envelope.Headers, s => |
||||
|
{ |
||||
|
s.SchemaDef.Apply(@event); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case FieldUpdated @event: |
||||
|
Schemas[@event.SchemaId.Id].Update(@event, envelope.Headers, s => |
||||
|
{ |
||||
|
s.SchemaDef.Apply(@event); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case SchemaFieldsReordered @event: |
||||
|
Schemas[@event.SchemaId.Id].Update(@event, envelope.Headers, s => |
||||
|
{ |
||||
|
s.SchemaDef.Apply(@event); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case SchemaUpdated @event: |
||||
|
Schemas[@event.SchemaId.Id].Update(@event, envelope.Headers, s => |
||||
|
{ |
||||
|
s.SchemaDef.Apply(@event); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case SchemaPublished @event: |
||||
|
Schemas[@event.SchemaId.Id].Update(@event, envelope.Headers, s => |
||||
|
{ |
||||
|
s.SchemaDef.Apply(@event); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case ScriptsConfigured @event: |
||||
|
Schemas[@event.SchemaId.Id].Update(@event, envelope.Headers, s => |
||||
|
{ |
||||
|
s.SchemaDef.Apply(@event); |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case SchemaDeleted @event: |
||||
|
Schemas.Remove(@event.SchemaId.Id); |
||||
|
break; |
||||
|
|
||||
|
case WebhookAdded @event: |
||||
|
Schemas[@event.SchemaId.Id].Update(@event, envelope.Headers, s => |
||||
|
{ |
||||
|
/* NOOP */ |
||||
|
}); |
||||
|
break; |
||||
|
|
||||
|
case WebhookDeleted @event: |
||||
|
Schemas[@event.SchemaId.Id].Update(@event, envelope.Headers, s => |
||||
|
{ |
||||
|
/* NOOP */ |
||||
|
}); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,41 @@ |
|||||
|
// ==========================================================================
|
||||
|
// AppUserGrain.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Orleans; |
||||
|
using Orleans.Providers; |
||||
|
using Squidex.Infrastructure.Tasks; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations |
||||
|
{ |
||||
|
[StorageProvider(ProviderName = "Default")] |
||||
|
public sealed class AppUserGrain : Grain<HashSet<Guid>>, IAppUserGrain |
||||
|
{ |
||||
|
public Task AddSchemaAsync(Guid schemaId) |
||||
|
{ |
||||
|
State.Add(schemaId); |
||||
|
|
||||
|
return TaskHelper.Done; |
||||
|
} |
||||
|
|
||||
|
public Task RemoveSchemaAsync(Guid schemaId) |
||||
|
{ |
||||
|
State.Remove(schemaId); |
||||
|
|
||||
|
return TaskHelper.Done; |
||||
|
} |
||||
|
|
||||
|
public Task<List<Guid>> GetSchemaIdsAsync() |
||||
|
{ |
||||
|
return Task.FromResult(State.ToList()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,35 @@ |
|||||
|
// ==========================================================================
|
||||
|
// JsonAppEntity.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using Newtonsoft.Json; |
||||
|
using Squidex.Domain.Apps.Core.Apps; |
||||
|
using Squidex.Domain.Apps.Read.Apps; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations |
||||
|
{ |
||||
|
public sealed class JsonAppEntity : JsonEntity, IAppEntity |
||||
|
{ |
||||
|
[JsonProperty] |
||||
|
public string Name { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public string PlanId { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public string PlanOwner { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public AppClients Clients { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public AppContributors Contributors { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public LanguagesConfig LanguagesConfig { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
// ==========================================================================
|
||||
|
// JsonEntity.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using Newtonsoft.Json; |
||||
|
using NodaTime; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations |
||||
|
{ |
||||
|
public abstract class JsonEntity |
||||
|
{ |
||||
|
[JsonProperty] |
||||
|
public Guid Id { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public Instant Created { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public Instant LastModified { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public long Version { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
// ==========================================================================
|
||||
|
// JsonRuleEntity.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using Newtonsoft.Json; |
||||
|
using Squidex.Domain.Apps.Core.Rules; |
||||
|
using Squidex.Domain.Apps.Read.Rules; |
||||
|
using Squidex.Infrastructure; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations |
||||
|
{ |
||||
|
public sealed class JsonRuleEntity : JsonEntity, IRuleEntity |
||||
|
{ |
||||
|
[JsonProperty] |
||||
|
public Guid AppId { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public RefToken CreatedBy { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public RefToken LastModifiedBy { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public Rule Rule { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,57 @@ |
|||||
|
// ==========================================================================
|
||||
|
// JsonSchemaEntity.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using Newtonsoft.Json; |
||||
|
using Squidex.Domain.Apps.Core.Schemas; |
||||
|
using Squidex.Domain.Apps.Read.Schemas; |
||||
|
using Squidex.Infrastructure; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations |
||||
|
{ |
||||
|
public sealed class JsonSchemaEntity : JsonEntity, ISchemaEntity |
||||
|
{ |
||||
|
[JsonProperty] |
||||
|
public string Name { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public Guid AppId { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public RefToken CreatedBy { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public RefToken LastModifiedBy { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public bool IsDeleted { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public string ScriptQuery { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public string ScriptCreate { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public string ScriptUpdate { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public string ScriptDelete { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public string ScriptChange { get; set; } |
||||
|
|
||||
|
[JsonProperty] |
||||
|
public Schema SchemaDef { get; set; } |
||||
|
|
||||
|
public bool IsPublished |
||||
|
{ |
||||
|
get { return SchemaDef.IsPublished; } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,71 @@ |
|||||
|
// ==========================================================================
|
||||
|
// OrleansAppState.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Orleans; |
||||
|
using Squidex.Domain.Apps.Read.Apps; |
||||
|
using Squidex.Domain.Apps.Read.Rules; |
||||
|
using Squidex.Domain.Apps.Read.Schemas; |
||||
|
using Squidex.Infrastructure; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Read.State.Orleans.Grains |
||||
|
{ |
||||
|
public sealed class OrleansAppState : IAppState |
||||
|
{ |
||||
|
private readonly IGrainFactory factory; |
||||
|
|
||||
|
public OrleansAppState(IGrainFactory factory) |
||||
|
{ |
||||
|
Guard.NotNull(factory, nameof(factory)); |
||||
|
|
||||
|
this.factory = factory; |
||||
|
} |
||||
|
|
||||
|
public Task<IAppEntity> GetAppAsync(Guid appId) |
||||
|
{ |
||||
|
return factory.GetGrain<IAppStateGrain>(appId).GetAppAsync(); |
||||
|
} |
||||
|
|
||||
|
public Task<List<IRuleEntity>> GetRulesAsync(Guid appId) |
||||
|
{ |
||||
|
return factory.GetGrain<IAppStateGrain>(appId).GetRulesAsync(); |
||||
|
} |
||||
|
|
||||
|
public Task<ISchemaEntity> GetSchemaAsync(Guid appId, Guid id, bool provideDeleted = false) |
||||
|
{ |
||||
|
return factory.GetGrain<IAppStateGrain>(appId).GetSchemaAsync(id, provideDeleted); |
||||
|
} |
||||
|
|
||||
|
public Task<ISchemaEntity> GetSchemaAsync(Guid appId, string name, bool provideDeleted = false) |
||||
|
{ |
||||
|
return factory.GetGrain<IAppStateGrain>(appId).GetSchemaAsync(name, provideDeleted); |
||||
|
} |
||||
|
|
||||
|
public Task<List<ISchemaEntity>> GetSchemasAsync(Guid appId) |
||||
|
{ |
||||
|
return factory.GetGrain<IAppStateGrain>(appId).GetSchemasAsync(); |
||||
|
} |
||||
|
|
||||
|
public async Task<List<IAppEntity>> GetUserApps(string userId) |
||||
|
{ |
||||
|
var schemaIds = await factory.GetGrain<IAppUserGrain>(userId).GetSchemaIdsAsync(); |
||||
|
|
||||
|
var tasks = |
||||
|
schemaIds |
||||
|
.Select(x => factory.GetGrain<IAppStateGrain>(x)) |
||||
|
.Select(x => x.GetAppAsync()); |
||||
|
|
||||
|
var apps = await Task.WhenAll(tasks); |
||||
|
|
||||
|
return apps.Where(a => a != null).ToList(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,30 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// OrleansClientEventNotifier.cs
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex Group
|
|
||||
// All rights reserved.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using Orleans; |
|
||||
using Squidex.Infrastructure.CQRS.Events.Orleans.Grains; |
|
||||
|
|
||||
namespace Squidex.Infrastructure.CQRS.Events.Orleans |
|
||||
{ |
|
||||
public sealed class OrleansClientEventNotifier : IEventNotifier |
|
||||
{ |
|
||||
private readonly IEventConsumerRegistryGrain eventConsumerRegistryGrain; |
|
||||
|
|
||||
public OrleansClientEventNotifier(IClusterClient orleans) |
|
||||
{ |
|
||||
Guard.NotNull(orleans, nameof(orleans)); |
|
||||
|
|
||||
eventConsumerRegistryGrain = orleans.GetGrain<IEventConsumerRegistryGrain>("Default"); |
|
||||
} |
|
||||
|
|
||||
public void NotifyEventsStored(string streamName) |
|
||||
{ |
|
||||
eventConsumerRegistryGrain.ActivateAsync(streamName); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,19 +0,0 @@ |
|||||
<Project Sdk="Microsoft.NET.Sdk"> |
|
||||
<PropertyGroup> |
|
||||
<OutputType>Exe</OutputType> |
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework> |
|
||||
</PropertyGroup> |
|
||||
<ItemGroup> |
|
||||
<ProjectReference Include="..\..\src\Squidex.Infrastructure.MongoDb\Squidex.Infrastructure.MongoDb.csproj" /> |
|
||||
<ProjectReference Include="..\..\src\Squidex.Infrastructure\Squidex.Infrastructure.csproj" /> |
|
||||
</ItemGroup> |
|
||||
<ItemGroup> |
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.4.4" /> |
|
||||
<PackageReference Include="RefactoringEssentials" Version="5.2.0" /> |
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2" /> |
|
||||
<PackageReference Include="System.ValueTuple" Version="4.4.0" /> |
|
||||
</ItemGroup> |
|
||||
<PropertyGroup> |
|
||||
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> |
|
||||
</PropertyGroup> |
|
||||
</Project> |
|
||||
@ -1,27 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// IBenchmark.cs
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex Group
|
|
||||
// All rights reserved.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
namespace Benchmarks |
|
||||
{ |
|
||||
public interface IBenchmark |
|
||||
{ |
|
||||
string Id { get; } |
|
||||
|
|
||||
string Name { get; } |
|
||||
|
|
||||
void Initialize(); |
|
||||
|
|
||||
void RunInitialize(); |
|
||||
|
|
||||
long Run(); |
|
||||
|
|
||||
void RunCleanup(); |
|
||||
|
|
||||
void Cleanup(); |
|
||||
} |
|
||||
} |
|
||||
@ -1,95 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Program.cs
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex Group
|
|
||||
// All rights reserved.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Diagnostics; |
|
||||
using System.Linq; |
|
||||
using Benchmarks.Tests; |
|
||||
|
|
||||
namespace Benchmarks |
|
||||
{ |
|
||||
public static class Program |
|
||||
{ |
|
||||
private static readonly List<IBenchmark> Benchmarks = new List<IBenchmark> |
|
||||
{ |
|
||||
new AppendToEventStore(), |
|
||||
new AppendToEventStoreWithManyWriters(), |
|
||||
new HandleEvents(), |
|
||||
new HandleEventsWithManyWriters() |
|
||||
}; |
|
||||
|
|
||||
public static void Main(string[] args) |
|
||||
{ |
|
||||
var id = args.Length > 0 ? args[0] : string.Empty; |
|
||||
|
|
||||
var benchmark = Benchmarks.Find(x => x.Id == id); |
|
||||
|
|
||||
if (benchmark == null) |
|
||||
{ |
|
||||
Console.WriteLine($"'{id}' is not a valid benchmark, please try: "); |
|
||||
|
|
||||
var longestId = Benchmarks.Max(x => x.Id.Length); |
|
||||
|
|
||||
foreach (var b in Benchmarks) |
|
||||
{ |
|
||||
Console.WriteLine($" * {b.Id.PadRight(longestId)}: {b.Name}"); |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
const int numRuns = 3; |
|
||||
|
|
||||
try |
|
||||
{ |
|
||||
var elapsed = 0d; |
|
||||
var count = 0L; |
|
||||
|
|
||||
Console.WriteLine($"{benchmark.Name}: Initialized"); |
|
||||
|
|
||||
benchmark.Initialize(); |
|
||||
|
|
||||
for (var run = 0; run < numRuns; run++) |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
benchmark.RunInitialize(); |
|
||||
|
|
||||
var watch = Stopwatch.StartNew(); |
|
||||
|
|
||||
count += benchmark.Run(); |
|
||||
|
|
||||
watch.Stop(); |
|
||||
|
|
||||
elapsed += watch.ElapsedMilliseconds; |
|
||||
|
|
||||
Console.WriteLine($"{benchmark.Name}: Run {run + 1} finished"); |
|
||||
} |
|
||||
finally |
|
||||
{ |
|
||||
benchmark.RunCleanup(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
var averageElapsed = TimeSpan.FromMilliseconds(elapsed / numRuns); |
|
||||
var averageSeconds = Math.Round(count / (numRuns * averageElapsed.TotalSeconds), 2); |
|
||||
|
|
||||
Console.WriteLine($"{benchmark.Name}: Completed after {averageElapsed}, {averageSeconds} items/s"); |
|
||||
} |
|
||||
catch (Exception e) |
|
||||
{ |
|
||||
Console.WriteLine($"Benchmark failed with '{e.Message}'"); |
|
||||
} |
|
||||
finally |
|
||||
{ |
|
||||
benchmark.Cleanup(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,8 +0,0 @@ |
|||||
{ |
|
||||
"profiles": { |
|
||||
"Benchmarks": { |
|
||||
"commandName": "Project", |
|
||||
"commandLineArgs": "handleEvents" |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,74 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// AppendToEventStore.cs
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex Group
|
|
||||
// All rights reserved.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using System; |
|
||||
using Benchmarks.Utils; |
|
||||
using MongoDB.Driver; |
|
||||
using Squidex.Infrastructure; |
|
||||
using Squidex.Infrastructure.CQRS.Events; |
|
||||
|
|
||||
namespace Benchmarks.Tests |
|
||||
{ |
|
||||
public sealed class AppendToEventStore : IBenchmark |
|
||||
{ |
|
||||
private IMongoClient mongoClient; |
|
||||
private IMongoDatabase mongoDatabase; |
|
||||
private IEventStore eventStore; |
|
||||
|
|
||||
public string Id |
|
||||
{ |
|
||||
get { return "appendToEventStore"; } |
|
||||
} |
|
||||
|
|
||||
public string Name |
|
||||
{ |
|
||||
get { return "Append events"; } |
|
||||
} |
|
||||
|
|
||||
public void Initialize() |
|
||||
{ |
|
||||
mongoClient = new MongoClient("mongodb://localhost"); |
|
||||
} |
|
||||
|
|
||||
public void RunInitialize() |
|
||||
{ |
|
||||
mongoDatabase = mongoClient.GetDatabase(Guid.NewGuid().ToString()); |
|
||||
|
|
||||
eventStore = new MongoEventStore(mongoDatabase, new DefaultEventNotifier(new InMemoryPubSub())); |
|
||||
} |
|
||||
|
|
||||
public long Run() |
|
||||
{ |
|
||||
const long numCommits = 100; |
|
||||
const long numStreams = 20; |
|
||||
|
|
||||
for (var streamId = 0; streamId < numStreams; streamId++) |
|
||||
{ |
|
||||
var eventOffset = -1; |
|
||||
var streamName = streamId.ToString(); |
|
||||
|
|
||||
for (var commitId = 0; commitId < numCommits; commitId++) |
|
||||
{ |
|
||||
eventStore.AppendEventsAsync(Guid.NewGuid(), streamName, eventOffset, new[] { Helper.CreateEventData() }).Wait(); |
|
||||
eventOffset++; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return numCommits * numStreams; |
|
||||
} |
|
||||
|
|
||||
public void RunCleanup() |
|
||||
{ |
|
||||
mongoClient.DropDatabase(mongoDatabase.DatabaseNamespace.DatabaseName); |
|
||||
} |
|
||||
|
|
||||
public void Cleanup() |
|
||||
{ |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,73 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// AppendToEventStoreWithManyWriters.cs
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex Group
|
|
||||
// All rights reserved.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using System; |
|
||||
using System.Threading.Tasks; |
|
||||
using Benchmarks.Utils; |
|
||||
using MongoDB.Driver; |
|
||||
using Squidex.Infrastructure; |
|
||||
using Squidex.Infrastructure.CQRS.Events; |
|
||||
|
|
||||
namespace Benchmarks.Tests |
|
||||
{ |
|
||||
public sealed class AppendToEventStoreWithManyWriters : IBenchmark |
|
||||
{ |
|
||||
private IMongoClient mongoClient; |
|
||||
private IMongoDatabase mongoDatabase; |
|
||||
private IEventStore eventStore; |
|
||||
|
|
||||
public string Id |
|
||||
{ |
|
||||
get { return "appendToEventStoreParallel"; } |
|
||||
} |
|
||||
|
|
||||
public string Name |
|
||||
{ |
|
||||
get { return "Append events parallel"; } |
|
||||
} |
|
||||
|
|
||||
public void Initialize() |
|
||||
{ |
|
||||
mongoClient = new MongoClient("mongodb://localhost"); |
|
||||
} |
|
||||
|
|
||||
public void RunInitialize() |
|
||||
{ |
|
||||
mongoDatabase = mongoClient.GetDatabase(Guid.NewGuid().ToString()); |
|
||||
|
|
||||
eventStore = new MongoEventStore(mongoDatabase, new DefaultEventNotifier(new InMemoryPubSub())); |
|
||||
} |
|
||||
|
|
||||
public long Run() |
|
||||
{ |
|
||||
const long numCommits = 200; |
|
||||
const long numStreams = 100; |
|
||||
|
|
||||
Parallel.For(0, numStreams, streamId => |
|
||||
{ |
|
||||
var streamName = streamId.ToString(); |
|
||||
|
|
||||
for (var commitId = 0; commitId < numCommits; commitId++) |
|
||||
{ |
|
||||
eventStore.AppendEventsAsync(Guid.NewGuid(), streamName, new[] { Helper.CreateEventData() }).Wait(); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
return numCommits * numStreams; |
|
||||
} |
|
||||
|
|
||||
public void RunCleanup() |
|
||||
{ |
|
||||
mongoClient.DropDatabase(mongoDatabase.DatabaseNamespace.DatabaseName); |
|
||||
} |
|
||||
|
|
||||
public void Cleanup() |
|
||||
{ |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,100 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// HandleEvents.cs
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex Group
|
|
||||
// All rights reserved.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using System; |
|
||||
using Benchmarks.Tests.TestData; |
|
||||
using MongoDB.Driver; |
|
||||
using Newtonsoft.Json; |
|
||||
using Squidex.Infrastructure; |
|
||||
using Squidex.Infrastructure.CQRS.Events; |
|
||||
using Squidex.Infrastructure.CQRS.Events.Actors; |
|
||||
using Squidex.Infrastructure.Json; |
|
||||
using Squidex.Infrastructure.Log; |
|
||||
|
|
||||
namespace Benchmarks.Tests |
|
||||
{ |
|
||||
public sealed class HandleEvents : IBenchmark |
|
||||
{ |
|
||||
private const int NumEvents = 5000; |
|
||||
private readonly TypeNameRegistry typeNameRegistry = new TypeNameRegistry().Map(typeof(MyEvent)); |
|
||||
private readonly EventDataFormatter formatter; |
|
||||
private readonly JsonSerializerSettings serializerSettings = new JsonSerializerSettings(); |
|
||||
private IMongoClient mongoClient; |
|
||||
private IMongoDatabase mongoDatabase; |
|
||||
private IEventStore eventStore; |
|
||||
private IEventNotifier eventNotifier; |
|
||||
private IEventConsumerInfoRepository eventConsumerInfos; |
|
||||
private EventConsumerActor eventConsumerActor; |
|
||||
private MyEventConsumer eventConsumer; |
|
||||
|
|
||||
public string Id |
|
||||
{ |
|
||||
get { return "handleEvents"; } |
|
||||
} |
|
||||
|
|
||||
public string Name |
|
||||
{ |
|
||||
get { return "Handle Events"; } |
|
||||
} |
|
||||
|
|
||||
public HandleEvents() |
|
||||
{ |
|
||||
serializerSettings.Converters.Add(new PropertiesBagConverter()); |
|
||||
|
|
||||
formatter = new EventDataFormatter(typeNameRegistry, serializerSettings); |
|
||||
} |
|
||||
|
|
||||
public void Initialize() |
|
||||
{ |
|
||||
mongoClient = new MongoClient("mongodb://localhost"); |
|
||||
} |
|
||||
|
|
||||
public void RunInitialize() |
|
||||
{ |
|
||||
mongoDatabase = mongoClient.GetDatabase(Guid.NewGuid().ToString()); |
|
||||
|
|
||||
var log = new SemanticLog(new ILogChannel[0], new ILogAppender[0], () => new JsonLogWriter(Formatting.Indented, true)); |
|
||||
|
|
||||
eventConsumerInfos = new MongoEventConsumerInfoRepository(mongoDatabase); |
|
||||
eventConsumer = new MyEventConsumer(NumEvents); |
|
||||
eventNotifier = new DefaultEventNotifier(new InMemoryPubSub()); |
|
||||
|
|
||||
eventStore = new MongoEventStore(mongoDatabase, eventNotifier); |
|
||||
|
|
||||
eventConsumerActor = new EventConsumerActor(formatter, eventStore, eventConsumerInfos, log); |
|
||||
eventConsumerActor.SubscribeAsync(eventConsumer); |
|
||||
} |
|
||||
|
|
||||
public long Run() |
|
||||
{ |
|
||||
var streamName = Guid.NewGuid().ToString(); |
|
||||
|
|
||||
for (var eventId = 0; eventId < NumEvents; eventId++) |
|
||||
{ |
|
||||
var eventData = formatter.ToEventData(new Envelope<IEvent>(new MyEvent { EventNumber = eventId + 1 }), Guid.NewGuid()); |
|
||||
|
|
||||
eventStore.AppendEventsAsync(Guid.NewGuid(), streamName, eventId - 1, new[] { eventData }).Wait(); |
|
||||
} |
|
||||
|
|
||||
eventConsumer.WaitAndVerify(); |
|
||||
|
|
||||
return NumEvents; |
|
||||
} |
|
||||
|
|
||||
public void RunCleanup() |
|
||||
{ |
|
||||
mongoClient.DropDatabase(mongoDatabase.DatabaseNamespace.DatabaseName); |
|
||||
|
|
||||
eventConsumerActor.Dispose(); |
|
||||
} |
|
||||
|
|
||||
public void Cleanup() |
|
||||
{ |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,107 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// HandleEventsWithManyWriters.cs
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex Group
|
|
||||
// All rights reserved.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using System; |
|
||||
using System.Threading.Tasks; |
|
||||
using Benchmarks.Tests.TestData; |
|
||||
using MongoDB.Driver; |
|
||||
using Newtonsoft.Json; |
|
||||
using Squidex.Infrastructure; |
|
||||
using Squidex.Infrastructure.CQRS.Events; |
|
||||
using Squidex.Infrastructure.CQRS.Events.Actors; |
|
||||
using Squidex.Infrastructure.Json; |
|
||||
using Squidex.Infrastructure.Log; |
|
||||
|
|
||||
namespace Benchmarks.Tests |
|
||||
{ |
|
||||
public sealed class HandleEventsWithManyWriters : IBenchmark |
|
||||
{ |
|
||||
private const int NumCommits = 200; |
|
||||
private const int NumStreams = 10; |
|
||||
private readonly TypeNameRegistry typeNameRegistry = new TypeNameRegistry().Map(typeof(MyEvent)); |
|
||||
private readonly EventDataFormatter formatter; |
|
||||
private readonly JsonSerializerSettings serializerSettings = new JsonSerializerSettings(); |
|
||||
private IMongoClient mongoClient; |
|
||||
private IMongoDatabase mongoDatabase; |
|
||||
private IEventStore eventStore; |
|
||||
private IEventNotifier eventNotifier; |
|
||||
private IEventConsumerInfoRepository eventConsumerInfos; |
|
||||
private EventConsumerActor eventConsumerActor; |
|
||||
private MyEventConsumer eventConsumer; |
|
||||
|
|
||||
public string Id |
|
||||
{ |
|
||||
get { return "handleEventsParallel"; } |
|
||||
} |
|
||||
|
|
||||
public string Name |
|
||||
{ |
|
||||
get { return "Handle events parallel"; } |
|
||||
} |
|
||||
|
|
||||
public HandleEventsWithManyWriters() |
|
||||
{ |
|
||||
serializerSettings.Converters.Add(new PropertiesBagConverter()); |
|
||||
|
|
||||
formatter = new EventDataFormatter(typeNameRegistry, serializerSettings); |
|
||||
} |
|
||||
|
|
||||
public void Initialize() |
|
||||
{ |
|
||||
mongoClient = new MongoClient("mongodb://localhost"); |
|
||||
} |
|
||||
|
|
||||
public void RunInitialize() |
|
||||
{ |
|
||||
mongoDatabase = mongoClient.GetDatabase(Guid.NewGuid().ToString()); |
|
||||
|
|
||||
var log = new SemanticLog(new ILogChannel[0], new ILogAppender[0], () => new JsonLogWriter(Formatting.Indented, true)); |
|
||||
|
|
||||
eventConsumerInfos = new MongoEventConsumerInfoRepository(mongoDatabase); |
|
||||
eventConsumer = new MyEventConsumer(NumStreams * NumCommits); |
|
||||
eventNotifier = new DefaultEventNotifier(new InMemoryPubSub()); |
|
||||
|
|
||||
eventStore = new MongoEventStore(mongoDatabase, eventNotifier); |
|
||||
|
|
||||
eventConsumerActor = new EventConsumerActor(formatter, eventStore, eventConsumerInfos, log); |
|
||||
eventConsumerActor.SubscribeAsync(eventConsumer); |
|
||||
} |
|
||||
|
|
||||
public long Run() |
|
||||
{ |
|
||||
Parallel.For(0, NumStreams, streamId => |
|
||||
{ |
|
||||
var eventOffset = -1; |
|
||||
var streamName = streamId.ToString(); |
|
||||
|
|
||||
for (var commitId = 0; commitId < NumCommits; commitId++) |
|
||||
{ |
|
||||
var eventData = formatter.ToEventData(new Envelope<IEvent>(new MyEvent()), Guid.NewGuid()); |
|
||||
|
|
||||
eventStore.AppendEventsAsync(Guid.NewGuid(), streamName, eventOffset - 1, new[] { eventData }).Wait(); |
|
||||
eventOffset++; |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
eventConsumer.WaitAndVerify(); |
|
||||
|
|
||||
return NumStreams * NumCommits; |
|
||||
} |
|
||||
|
|
||||
public void RunCleanup() |
|
||||
{ |
|
||||
mongoClient.DropDatabase(mongoDatabase.DatabaseNamespace.DatabaseName); |
|
||||
|
|
||||
eventConsumerActor.Dispose(); |
|
||||
} |
|
||||
|
|
||||
public void Cleanup() |
|
||||
{ |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,19 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// MyEvent.cs
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex Group
|
|
||||
// All rights reserved.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using Squidex.Infrastructure; |
|
||||
using Squidex.Infrastructure.CQRS.Events; |
|
||||
|
|
||||
namespace Benchmarks.Tests.TestData |
|
||||
{ |
|
||||
[TypeName("MyEvent")] |
|
||||
public sealed class MyEvent : IEvent |
|
||||
{ |
|
||||
public int EventNumber { get; set; } |
|
||||
} |
|
||||
} |
|
||||
@ -1,79 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// MyEventConsumer.cs
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex Group
|
|
||||
// All rights reserved.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Threading.Tasks; |
|
||||
using Squidex.Infrastructure.CQRS.Events; |
|
||||
using Squidex.Infrastructure.Tasks; |
|
||||
|
|
||||
namespace Benchmarks.Tests.TestData |
|
||||
{ |
|
||||
public sealed class MyEventConsumer : IEventConsumer |
|
||||
{ |
|
||||
private readonly TaskCompletionSource<object> completion = new TaskCompletionSource<object>(); |
|
||||
private readonly int numEvents; |
|
||||
|
|
||||
public List<int> EventNumbers { get; } = new List<int>(); |
|
||||
|
|
||||
public string Name |
|
||||
{ |
|
||||
get { return typeof(MyEventConsumer).Name; } |
|
||||
} |
|
||||
|
|
||||
public string EventsFilter |
|
||||
{ |
|
||||
get { return string.Empty; } |
|
||||
} |
|
||||
|
|
||||
public MyEventConsumer(int numEvents) |
|
||||
{ |
|
||||
this.numEvents = numEvents; |
|
||||
} |
|
||||
|
|
||||
public Task ClearAsync() |
|
||||
{ |
|
||||
return TaskHelper.Done; |
|
||||
} |
|
||||
|
|
||||
public void WaitAndVerify() |
|
||||
{ |
|
||||
completion.Task.Wait(); |
|
||||
|
|
||||
if (EventNumbers.Count != numEvents) |
|
||||
{ |
|
||||
throw new InvalidOperationException($"{EventNumbers.Count} Events have been handled"); |
|
||||
} |
|
||||
|
|
||||
for (var i = 0; i < EventNumbers.Count; i++) |
|
||||
{ |
|
||||
var value = EventNumbers[i]; |
|
||||
|
|
||||
if (value != i + 1) |
|
||||
{ |
|
||||
throw new InvalidOperationException($"Event[{i}] != value"); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public Task On(Envelope<IEvent> @event) |
|
||||
{ |
|
||||
if (@event.Payload is MyEvent myEvent) |
|
||||
{ |
|
||||
EventNumbers.Add(myEvent.EventNumber); |
|
||||
|
|
||||
if (myEvent.EventNumber == numEvents) |
|
||||
{ |
|
||||
completion.SetResult(true); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return TaskHelper.Done; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue