feat(push): add service-owned firebase channels and delivery
Add push contracts, provider delivery and channel diagnostics. Link email channels to services and document migration and validation. Ref: IT-1074
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user