实现事务性发件箱模式
This commit is contained in:
1 parent
d47ff517ad
commit
16386a8ea5
21 files changed
+241
-54
No files matched your search
@@ -0,0 +1,89 @@
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
using HelloShop.OrderingService.Entities.EventLogs;
|
||||
using HelloShop.ServiceDefaults.DistributedEvents.Abstractions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace HelloShop.OrderingService.Services
|
||||
{
|
||||
public class DistributedEventService<TContext>(TContext dbContext, IDistributedEventBus distributedEventBus, ILogger<DistributedEventService<TContext>> logger) : IDistributedEventService, IDisposable where TContext : DbContext
|
||||
{
|
||||
private volatile bool _disposedValue;
|
||||
|
||||
public async Task UpdateEventStatusAsync(Guid eventId, DistributedEventStatus status, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var eventLogEntry = dbContext.Set<DistributedEventLog>().Single(ie => ie.EventId == eventId);
|
||||
|
||||
eventLogEntry.Status = status;
|
||||
|
||||
if (status == DistributedEventStatus.InProgress)
|
||||
{
|
||||
eventLogEntry.TimesSent++;
|
||||
}
|
||||
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<DistributedEventLog>> RetrieveEventLogsPendingToPublishAsync(Guid transactionId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var result = await dbContext.Set<DistributedEventLog>().Where(e => e.TransactionId == transactionId && e.Status == DistributedEventStatus.NotPublished).ToListAsync(cancellationToken: cancellationToken);
|
||||
|
||||
return result.Count != 0 ? result.OrderBy(o => o.CreationTime) : [];
|
||||
}
|
||||
|
||||
public async Task AddAndSaveEventAsync(DistributedEvent @event, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var transaction = dbContext.Database.CurrentTransaction ?? throw new InvalidOperationException("This method must be called within a transaction scope.");
|
||||
|
||||
var eventLog = new DistributedEventLog(@event, transaction.TransactionId);
|
||||
|
||||
await dbContext.AddAsync(eventLog, cancellationToken);
|
||||
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task PublishEventsThroughEventBusAsync(Guid transactionId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var pendingEventLogs = await RetrieveEventLogsPendingToPublishAsync(transactionId, cancellationToken);
|
||||
|
||||
foreach (var eventLog in pendingEventLogs)
|
||||
{
|
||||
logger.LogInformation("Publishing integration event {EventId} {DistributedEvent}", eventLog.EventId, eventLog.DistributedEvent);
|
||||
|
||||
try
|
||||
{
|
||||
await UpdateEventStatusAsync(eventLog.EventId, DistributedEventStatus.InProgress, cancellationToken);
|
||||
await distributedEventBus.PublishAsync(eventLog.DistributedEvent, cancellationToken);
|
||||
await UpdateEventStatusAsync(eventLog.EventId, DistributedEventStatus.Published, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Error publishing distributed event {EventId}", eventLog.EventId);
|
||||
|
||||
await UpdateEventStatusAsync(eventLog.EventId, DistributedEventStatus.PublishedFailed, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
dbContext.Dispose();
|
||||
}
|
||||
|
||||
_disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user