Browse Source

Event consumer grain.

pull/532/head
Sebastian 6 years ago
parent
commit
4db57c236a
  1. 44
      backend/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs
  2. 4
      backend/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerManagerGrain.cs
  3. 32
      backend/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerState.cs
  4. 14
      backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs
  5. 6
      backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerManagerGrainTests.cs

44
backend/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs

@ -28,6 +28,12 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
private IEventSubscription? currentSubscription; private IEventSubscription? currentSubscription;
private IEventConsumer? eventConsumer; private IEventConsumer? eventConsumer;
private EventConsumerState State
{
get => state.Value;
set => state.Value = value;
}
public EventConsumerGrain( public EventConsumerGrain(
EventConsumerFactory eventConsumerFactory, EventConsumerFactory eventConsumerFactory,
IGrainState<EventConsumerState> state, IGrainState<EventConsumerState> state,
@ -65,7 +71,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
private Immutable<EventConsumerInfo> CreateInfo() private Immutable<EventConsumerInfo> CreateInfo()
{ {
return state.Value.ToInfo(eventConsumer!.Name).AsImmutable(); return State.ToInfo(eventConsumer!.Name).AsImmutable();
} }
public Task OnEventAsync(Immutable<IEventSubscription> subscription, Immutable<StoredEvent> storedEvent) public Task OnEventAsync(Immutable<IEventSubscription> subscription, Immutable<StoredEvent> storedEvent)
@ -87,7 +93,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
} }
} }
state.Value = state.Value.Handled(storedEvent.Value.EventPosition); State = State.Handled(storedEvent.Value.EventPosition);
}); });
} }
@ -102,32 +108,39 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
{ {
Unsubscribe(); Unsubscribe();
state.Value = state.Value.Failed(exception.Value); State = State.Stopped(exception.Value);
}); });
} }
public Task ActivateAsync() public async Task ActivateAsync()
{ {
if (!state.Value.IsStopped) if (State.IsFailed)
{ {
Subscribe(state.Value.Position); await DoAndUpdateStateAsync(() =>
} {
Subscribe(State.Position);
return Task.CompletedTask; State = State.Started();
});
}
else if (!State.IsStopped)
{
Subscribe(State.Position);
}
} }
public async Task<Immutable<EventConsumerInfo>> StartAsync() public async Task<Immutable<EventConsumerInfo>> StartAsync()
{ {
if (!state.Value.IsStopped) if (!State.IsStopped)
{ {
return CreateInfo(); return CreateInfo();
} }
await DoAndUpdateStateAsync(() => await DoAndUpdateStateAsync(() =>
{ {
Subscribe(state.Value.Position); Subscribe(State.Position);
state.Value = state.Value.Started(); State = State.Started();
}); });
return CreateInfo(); return CreateInfo();
@ -135,7 +148,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
public async Task<Immutable<EventConsumerInfo>> StopAsync() public async Task<Immutable<EventConsumerInfo>> StopAsync()
{ {
if (state.Value.IsStopped) if (State.IsStopped)
{ {
return CreateInfo(); return CreateInfo();
} }
@ -144,7 +157,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
{ {
Unsubscribe(); Unsubscribe();
state.Value = state.Value.Stopped(); State = State.Stopped();
}); });
return CreateInfo(); return CreateInfo();
@ -160,7 +173,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
Subscribe(null); Subscribe(null);
state.Value = state.Value.Reset(); State = State.Reset();
}); });
return CreateInfo(); return CreateInfo();
@ -193,7 +206,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
.WriteProperty("status", "Failed") .WriteProperty("status", "Failed")
.WriteProperty("eventConsumer", eventConsumer!.Name)); .WriteProperty("eventConsumer", eventConsumer!.Name));
state.Value = state.Value.Failed(ex); State = State.Stopped(ex);
} }
await state.WriteAsync(); await state.WriteAsync();
@ -259,7 +272,6 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
{ {
if (currentSubscription == null) if (currentSubscription == null)
{ {
currentSubscription?.StopAsync().Forget();
currentSubscription = CreateSubscription(eventConsumer!.EventsFilter, position); currentSubscription = CreateSubscription(eventConsumer!.EventsFilter, position);
} }
else else

4
backend/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerManagerGrain.cs

@ -41,7 +41,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
{ {
DelayDeactivation(TimeSpan.FromDays(1)); DelayDeactivation(TimeSpan.FromDays(1));
RegisterOrUpdateReminder("Default", TimeSpan.Zero, TimeSpan.FromMinutes(10)); RegisterOrUpdateReminder("Default", TimeSpan.Zero, TimeSpan.FromMinutes(5));
RegisterTimer(x => ActivateAsync(null), null, TimeSpan.Zero, TimeSpan.FromSeconds(10)); RegisterTimer(x => ActivateAsync(null), null, TimeSpan.Zero, TimeSpan.FromSeconds(10));
return Task.FromResult(true); return Task.FromResult(true);
@ -112,7 +112,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
public Task ReceiveReminder(string reminderName, TickStatus status) public Task ReceiveReminder(string reminderName, TickStatus status)
{ {
return StartAllAsync(); return ActivateAsync(null);
} }
} }
} }

32
backend/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerState.cs

@ -18,29 +18,43 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
public string? Position { get; set; } public string? Position { get; set; }
public EventConsumerState Reset() public bool IsPaused
{ {
return new EventConsumerState(); get { return IsStopped && string.IsNullOrWhiteSpace(Error); }
} }
public EventConsumerState Handled(string position) public bool IsFailed
{
get { return IsStopped && !string.IsNullOrWhiteSpace(Error); }
}
public EventConsumerState()
{ {
return new EventConsumerState { Position = position };
} }
public EventConsumerState Failed(Exception ex) public EventConsumerState(string? position)
{
Position = position;
}
public EventConsumerState Reset()
{
return new EventConsumerState();
}
public EventConsumerState Handled(string position)
{ {
return new EventConsumerState { Position = Position, IsStopped = true, Error = ex?.ToString() }; return new EventConsumerState(position);
} }
public EventConsumerState Stopped() public EventConsumerState Stopped(Exception? ex = null)
{ {
return new EventConsumerState { Position = Position, IsStopped = true }; return new EventConsumerState(Position) { IsStopped = true, Error = ex?.ToString() };
} }
public EventConsumerState Started() public EventConsumerState Started()
{ {
return new EventConsumerState { Position = Position, IsStopped = false }; return new EventConsumerState(Position) { IsStopped = false };
} }
public EventConsumerInfo ToInfo(string name) public EventConsumerInfo ToInfo(string name)

14
backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs

@ -107,6 +107,20 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
.MustHaveHappened(1, Times.Exactly); .MustHaveHappened(1, Times.Exactly);
} }
[Fact]
public async Task Should_subscribe_to_event_store_when_failed()
{
grainState.Value = grainState.Value.Stopped(new InvalidOperationException());
await sut.ActivateAsync(consumerName);
await sut.ActivateAsync();
grainState.Value.Should().BeEquivalentTo(new EventConsumerState { IsStopped = false, Position = initialPosition, Error = null });
A.CallTo(() => eventStore.CreateSubscription(A<IEventSubscriber>._, A<string>._, A<string>._))
.MustHaveHappened(1, Times.Exactly);
}
[Fact] [Fact]
public async Task Should_subscribe_to_event_store_when_not_stopped_in_db() public async Task Should_subscribe_to_event_store_when_not_stopped_in_db()
{ {

6
backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerManagerGrainTests.cs

@ -67,14 +67,14 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
} }
[Fact] [Fact]
public async Task Should_start_all_grains_on_reminder() public async Task Should_activate_all_grains_on_reminder()
{ {
await sut.ReceiveReminder(null!, default); await sut.ReceiveReminder(null!, default);
A.CallTo(() => grainA.StartAsync()) A.CallTo(() => grainA.ActivateAsync())
.MustHaveHappened(); .MustHaveHappened();
A.CallTo(() => grainB.StartAsync()) A.CallTo(() => grainB.ActivateAsync())
.MustHaveHappened(); .MustHaveHappened();
} }

Loading…
Cancel
Save