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
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using Squidex.Infrastructure.CQRS.Events; |
|||
using Squidex.Domain.Apps.Core; |
|||
|
|||
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
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using NodaTime; |
|||
using Orleans.Concurrency; |
|||
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; } |
|||
|
|||
Instant Created { get; set; } |
|||
|
|||
Instant LastModified { get; set; } |
|||
public Envelope<IEvent> Event { 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