Skip to content

Commit

Permalink
fix: don't use db transaction in the backoffice projections
Browse files Browse the repository at this point in the history
  • Loading branch information
jvandaal committed May 29, 2024
1 parent 7902eea commit 1e940bf
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ public BackOfficeContext(DbContextOptions<BackOfficeContext> options)
public async Task<BuildingUnitBuilding> AddIdempotentBuildingUnitBuilding(
BuildingPersistentLocalId buildingPersistentLocalId,
BuildingUnitPersistentLocalId buildingUnitPersistentLocalId,
CancellationToken cancellationToken)
CancellationToken cancellationToken,
bool saveChanges = true)
{
var relation = await FindBuildingUnitBuildingRelation(
new BuildingUnitPersistentLocalId(buildingUnitPersistentLocalId), cancellationToken);
Expand All @@ -40,7 +41,11 @@ public async Task<BuildingUnitBuilding> AddIdempotentBuildingUnitBuilding(
{
relation = new BuildingUnitBuilding(buildingUnitPersistentLocalId, buildingPersistentLocalId);
await BuildingUnitBuildings.AddAsync(relation, cancellationToken);
await SaveChangesAsync(cancellationToken);

if (saveChanges)
{
await SaveChangesAsync(cancellationToken);
}
}
catch (DbUpdateException exception)
{
Expand All @@ -62,7 +67,8 @@ public async Task<BuildingUnitBuilding> AddIdempotentBuildingUnitBuilding(

public async Task RemoveIdempotentBuildingUnitBuildingRelation(
BuildingUnitPersistentLocalId buildingUnitPersistentLocalId,
CancellationToken cancellationToken)
CancellationToken cancellationToken,
bool saveChanges = true)
{
var relation = await FindBuildingUnitBuildingRelation(buildingUnitPersistentLocalId, cancellationToken);

Expand All @@ -72,7 +78,11 @@ public async Task RemoveIdempotentBuildingUnitBuildingRelation(
}

BuildingUnitBuildings.Remove(relation);
await SaveChangesAsync(cancellationToken);

if (saveChanges)
{
await SaveChangesAsync(cancellationToken);
}
}

public async Task<BuildingUnitBuilding?> FindBuildingUnitBuildingRelation(
Expand Down Expand Up @@ -156,7 +166,8 @@ public async Task RemoveBuildingUnitAddressRelations(BuildingUnitPersistentLocal
public async Task MoveBuildingUnitAddressRelations(
BuildingUnitPersistentLocalId buildingUnitPersistentLocalId,
BuildingPersistentLocalId destinationBuildingPersistentLocalId,
CancellationToken cancellationToken)
CancellationToken cancellationToken,
bool saveChanges = true)
{
var buildingUnitAddressRelations = await FindAllBuildingUnitAddressRelations(buildingUnitPersistentLocalId, cancellationToken);

Expand All @@ -165,7 +176,10 @@ public async Task MoveBuildingUnitAddressRelations(
buildingUnitAddressRelation.BuildingPersistentLocalId = destinationBuildingPersistentLocalId;
}

await SaveChangesAsync(cancellationToken);
if (saveChanges)
{
await SaveChangesAsync(cancellationToken);
}
}

public async Task<List<BuildingUnitAddressRelation>> FindAllBuildingUnitAddressRelations(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,79 +1,110 @@
namespace BuildingRegistry.Projections.BackOffice
{
using System;
using System.Threading;
using System.Threading.Tasks;
using Be.Vlaanderen.Basisregisters.EventHandling;
using Be.Vlaanderen.Basisregisters.ProjectionHandling.Connector;
using Be.Vlaanderen.Basisregisters.ProjectionHandling.SqlStreamStore;
using BuildingRegistry.Api.BackOffice.Abstractions;
using Building;
using Building.Events;
using Microsoft.EntityFrameworkCore;
using System.Threading;
using Microsoft.Extensions.Configuration;

public class BackOfficeProjections : ConnectedProjection<BackOfficeProjectionsContext>
{
public BackOfficeProjections(IDbContextFactory<BackOfficeContext> backOfficeContextFactory)
public BackOfficeProjections(IDbContextFactory<BackOfficeContext> backOfficeContextFactory, IConfiguration configuration)
{
var delayInSeconds = configuration.GetValue("DelayInSeconds", 10);

When<Envelope<BuildingUnitWasPlannedV2>>(async (_, message, cancellationToken) =>
{
await DelayProjection(message, delayInSeconds, cancellationToken);

await using var backOfficeContext = await backOfficeContextFactory.CreateDbContextAsync(cancellationToken);
await backOfficeContext.AddIdempotentBuildingUnitBuilding(
new BuildingPersistentLocalId(message.Message.BuildingPersistentLocalId), new BuildingUnitPersistentLocalId(message.Message.BuildingUnitPersistentLocalId), cancellationToken);
new BuildingPersistentLocalId(message.Message.BuildingPersistentLocalId),
new BuildingUnitPersistentLocalId(message.Message.BuildingUnitPersistentLocalId), cancellationToken);
await backOfficeContext.SaveChangesAsync(cancellationToken);
});

When<Envelope<CommonBuildingUnitWasAddedV2>>(async (_, message, cancellationToken) =>
{
await DelayProjection(message, delayInSeconds, cancellationToken);

await using var backOfficeContext = await backOfficeContextFactory.CreateDbContextAsync(cancellationToken);
await backOfficeContext.AddIdempotentBuildingUnitBuilding(
new BuildingPersistentLocalId(message.Message.BuildingPersistentLocalId), new BuildingUnitPersistentLocalId(message.Message.BuildingUnitPersistentLocalId), cancellationToken);
new BuildingPersistentLocalId(message.Message.BuildingPersistentLocalId),
new BuildingUnitPersistentLocalId(message.Message.BuildingUnitPersistentLocalId), cancellationToken);
await backOfficeContext.SaveChangesAsync(cancellationToken);
});

When<Envelope<BuildingUnitAddressWasAttachedV2>>(async (_, message, cancellationToken) =>
{
await DelayProjection(message, delayInSeconds, cancellationToken);

await using var backOfficeContext = await backOfficeContextFactory.CreateDbContextAsync(cancellationToken);
await backOfficeContext.AddIdempotentBuildingUnitAddressRelation(
new BuildingPersistentLocalId(message.Message.BuildingPersistentLocalId),
new BuildingUnitPersistentLocalId(message.Message.BuildingUnitPersistentLocalId),
new AddressPersistentLocalId(message.Message.AddressPersistentLocalId),
cancellationToken);
await backOfficeContext.SaveChangesAsync(cancellationToken);
});

When<Envelope<BuildingUnitAddressWasDetachedV2>>(async (_, message, cancellationToken) =>
{
await DelayProjection(message, delayInSeconds, cancellationToken);

await using var backOfficeContext = await backOfficeContextFactory.CreateDbContextAsync(cancellationToken);
await backOfficeContext.RemoveIdempotentBuildingUnitAddressRelation(
new BuildingUnitPersistentLocalId(message.Message.BuildingUnitPersistentLocalId),
new AddressPersistentLocalId(message.Message.AddressPersistentLocalId),
cancellationToken);
await backOfficeContext.SaveChangesAsync(cancellationToken);
});

When<Envelope<BuildingUnitAddressWasDetachedBecauseAddressWasRejected>>(async (_, message, cancellationToken) =>
{
await DelayProjection(message, delayInSeconds, cancellationToken);

await using var backOfficeContext = await backOfficeContextFactory.CreateDbContextAsync(cancellationToken);
await backOfficeContext.RemoveIdempotentBuildingUnitAddressRelation(
new BuildingUnitPersistentLocalId(message.Message.BuildingUnitPersistentLocalId),
new AddressPersistentLocalId(message.Message.AddressPersistentLocalId),
cancellationToken);
await backOfficeContext.SaveChangesAsync(cancellationToken);
});

When<Envelope<BuildingUnitAddressWasDetachedBecauseAddressWasRetired>>(async (_, message, cancellationToken) =>
{
await DelayProjection(message, delayInSeconds, cancellationToken);

await using var backOfficeContext = await backOfficeContextFactory.CreateDbContextAsync(cancellationToken);
await backOfficeContext.RemoveIdempotentBuildingUnitAddressRelation(
new BuildingUnitPersistentLocalId(message.Message.BuildingUnitPersistentLocalId),
new AddressPersistentLocalId(message.Message.AddressPersistentLocalId),
cancellationToken);
await backOfficeContext.SaveChangesAsync(cancellationToken);
});

When<Envelope<BuildingUnitAddressWasDetachedBecauseAddressWasRemoved>>(async (_, message, cancellationToken) =>
{
await DelayProjection(message, delayInSeconds, cancellationToken);

await using var backOfficeContext = await backOfficeContextFactory.CreateDbContextAsync(cancellationToken);
await backOfficeContext.RemoveIdempotentBuildingUnitAddressRelation(
new BuildingUnitPersistentLocalId(message.Message.BuildingUnitPersistentLocalId),
new AddressPersistentLocalId(message.Message.AddressPersistentLocalId),
cancellationToken);
await backOfficeContext.SaveChangesAsync(cancellationToken);
});

When<Envelope<BuildingUnitAddressWasReplacedBecauseAddressWasReaddressed>>(async (_, message, cancellationToken) =>
{
await DelayProjection(message, delayInSeconds, cancellationToken);

await using var backOfficeContext = await backOfficeContextFactory.CreateDbContextAsync(cancellationToken);

await backOfficeContext.RemoveIdempotentBuildingUnitAddressRelation(
Expand All @@ -90,26 +121,40 @@ await backOfficeContext.AddIdempotentBuildingUnitAddressRelation(

When<Envelope<BuildingUnitWasMovedOutOfBuilding>>(async (_, message, cancellationToken) =>
{
await DelayProjection(message, delayInSeconds, cancellationToken);

await using var backOfficeContext = await backOfficeContextFactory.CreateDbContextAsync(cancellationToken);
await using var transaction = await backOfficeContext.Database.BeginTransactionAsync(cancellationToken);

var buildingUnitPersistentLocalId = new BuildingUnitPersistentLocalId(message.Message.BuildingUnitPersistentLocalId);
var destinationBuildingPersistentLocalId = new BuildingPersistentLocalId(message.Message.DestinationBuildingPersistentLocalId);

await backOfficeContext.RemoveIdempotentBuildingUnitBuildingRelation(
buildingUnitPersistentLocalId,
cancellationToken);
await backOfficeContext.AddIdempotentBuildingUnitBuilding(
destinationBuildingPersistentLocalId,
buildingUnitPersistentLocalId,
cancellationToken);
var buildingBuildingUnit = await backOfficeContext
.FindBuildingUnitBuildingRelation(buildingUnitPersistentLocalId, cancellationToken);

if (buildingBuildingUnit is not null
&& buildingBuildingUnit!.BuildingPersistentLocalId == message.Message.BuildingPersistentLocalId)
{
buildingBuildingUnit!.BuildingPersistentLocalId = destinationBuildingPersistentLocalId;
}

await backOfficeContext.MoveBuildingUnitAddressRelations(
buildingUnitPersistentLocalId,
destinationBuildingPersistentLocalId,
cancellationToken);
cancellationToken,
saveChanges: false);

await transaction.CommitAsync(cancellationToken);
await backOfficeContext.SaveChangesAsync(cancellationToken);
});
}

private static async Task DelayProjection<TMessage>(Envelope<TMessage> envelope, int delayInSeconds, CancellationToken cancellationToken)
where TMessage : IMessage
{
var differenceInSeconds = (DateTime.UtcNow - envelope.CreatedUtc).TotalSeconds;
if (differenceInSeconds < delayInSeconds)
{
await Task.Delay(TimeSpan.FromSeconds(delayInSeconds - differenceInSeconds), cancellationToken);
}
}
}
}
4 changes: 3 additions & 1 deletion src/BuildingRegistry.Projections.BackOffice/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,9 @@ public static async Task Main(string[] args)
builder.RegisterModule(new ProjectorModule(hostContext.Configuration));

builder.RegisterProjections<BackOfficeProjections, BackOfficeProjectionsContext>(
c => new BackOfficeProjections(c.Resolve<IDbContextFactory<BackOfficeContext>>()),
c => new BackOfficeProjections(
c.Resolve<IDbContextFactory<BackOfficeContext>>(),
hostContext.Configuration),
ConnectedProjectionSettings.Default);
})
.UseConsoleLifetime()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
namespace BuildingRegistry.Tests.ProjectionTests.BackOffice
{
using System;
using System.Collections.Generic;
using Be.Vlaanderen.Basisregisters.EventHandling;
using Be.Vlaanderen.Basisregisters.ProjectionHandling.SqlStreamStore;
using Be.Vlaanderen.Basisregisters.ProjectionHandling.Testing;
using BuildingRegistry.Api.BackOffice.Abstractions;
using BuildingRegistry.Projections.BackOffice;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Moq;

public abstract class BuildingBackOfficeProjectionsTest
Expand All @@ -17,7 +21,7 @@ protected BuildingBackOfficeProjectionsTest()
BackOfficeContextMock = new Mock<IDbContextFactory<BackOfficeContext>>();
Sut = new ConnectedProjectionTest<BackOfficeProjectionsContext, BackOfficeProjections>(
CreateContext,
() => new BackOfficeProjections(BackOfficeContextMock.Object));
() => new BackOfficeProjections(BackOfficeContextMock.Object, new ConfigurationBuilder().Build()));
}

protected virtual BackOfficeProjectionsContext CreateContext()
Expand All @@ -28,5 +32,14 @@ protected virtual BackOfficeProjectionsContext CreateContext()

return new BackOfficeProjectionsContext(options);
}

protected Envelope<TMessage> BuildEnvelope<TMessage>(TMessage message)
where TMessage : IMessage
{
return new Envelope<TMessage>(new Envelope(message, new Dictionary<string, object>
{
{ Envelope.CreatedUtcMetadataKey, DateTime.UtcNow }
}));
}
}
}
Loading

0 comments on commit 1e940bf

Please sign in to comment.