1631786ce2
Add push contracts, provider delivery and channel diagnostics. Link email channels to services and document migration and validation. Ref: IT-1074
37 lines
2.0 KiB
C#
37 lines
2.0 KiB
C#
namespace HrynCo.NotificationService.DAL.EF.Repositories;
|
|
|
|
using HrynCo.NotificationService.DAL.Abstract.Push;
|
|
using HrynCo.NotificationService.DAL.EF.Entities;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
public sealed class PushDeliveryRepository(NotificationDbContext db) : IPushDeliveryRepository
|
|
{
|
|
public async Task<PushDeliveryOutcome> DeliverOnceAsync(Guid messageId, string serviceName,
|
|
Func<CancellationToken, Task<PushDeliveryOutcome>> deliver, CancellationToken cancellationToken)
|
|
{
|
|
// Thirty days exceeds the maximum payload lifetime; old replays cannot send expired data.
|
|
var cutoff = DateTimeOffset.UtcNow.AddDays(-30);
|
|
await db.Set<PushDeliveryEntity>().Where(x => x.CompletedAt < cutoff).ExecuteDeleteAsync(cancellationToken);
|
|
await using var transaction = await db.Database.BeginTransactionAsync(cancellationToken);
|
|
var identity = $"{serviceName}:{messageId:D}";
|
|
await db.Database.ExecuteSqlInterpolatedAsync(
|
|
$"SELECT pg_advisory_xact_lock(hashtextextended({identity}, 1074))", cancellationToken);
|
|
var previous = await db.Set<PushDeliveryEntity>().AsNoTracking()
|
|
.SingleOrDefaultAsync(x => x.MessageId == messageId && x.ServiceName == serviceName, cancellationToken);
|
|
if (previous is not null)
|
|
return new PushDeliveryOutcome(previous.Error, previous.InvalidRegistration);
|
|
|
|
// One bounded provider call while holding this message's lock prevents concurrent sends.
|
|
// A crash after FCM accepts still requires client-side message-id deduplication.
|
|
var outcome = await deliver(cancellationToken);
|
|
db.Add(new PushDeliveryEntity
|
|
{
|
|
MessageId = messageId, ServiceName = serviceName, Error = outcome.Error,
|
|
InvalidRegistration = outcome.InvalidRegistration, CompletedAt = DateTimeOffset.UtcNow
|
|
});
|
|
await db.SaveChangesAsync(cancellationToken);
|
|
await transaction.CommitAsync(cancellationToken);
|
|
return outcome;
|
|
}
|
|
}
|