55 lines
1.6 KiB
C#
55 lines
1.6 KiB
C#
using System.Collections.Concurrent;
|
|
|
|
namespace NATS.Server.JetStream.Publish;
|
|
|
|
public sealed class PublishPreconditions
|
|
{
|
|
private readonly ConcurrentDictionary<string, DedupeEntry> _dedupe = new(StringComparer.Ordinal);
|
|
|
|
public bool IsDuplicate(string? msgId, int duplicateWindowMs, out ulong existingSequence)
|
|
{
|
|
existingSequence = 0;
|
|
if (string.IsNullOrEmpty(msgId))
|
|
return false;
|
|
|
|
if (!_dedupe.TryGetValue(msgId, out var entry))
|
|
return false;
|
|
|
|
if (duplicateWindowMs > 0
|
|
&& DateTime.UtcNow - entry.TimestampUtc > TimeSpan.FromMilliseconds(duplicateWindowMs))
|
|
{
|
|
_dedupe.TryRemove(msgId, out _);
|
|
return false;
|
|
}
|
|
|
|
existingSequence = entry.Sequence;
|
|
return true;
|
|
}
|
|
|
|
public void Record(string? msgId, ulong sequence)
|
|
{
|
|
if (string.IsNullOrEmpty(msgId))
|
|
return;
|
|
|
|
_dedupe[msgId] = new DedupeEntry(sequence, DateTime.UtcNow);
|
|
}
|
|
|
|
public void TrimOlderThan(int duplicateWindowMs)
|
|
{
|
|
if (duplicateWindowMs <= 0)
|
|
return;
|
|
|
|
var cutoff = DateTime.UtcNow.AddMilliseconds(-duplicateWindowMs);
|
|
foreach (var (key, entry) in _dedupe)
|
|
{
|
|
if (entry.TimestampUtc < cutoff)
|
|
_dedupe.TryRemove(key, out _);
|
|
}
|
|
}
|
|
|
|
public bool CheckExpectedLastSeq(ulong expectedLastSeq, ulong actualLastSeq)
|
|
=> expectedLastSeq == 0 || expectedLastSeq == actualLastSeq;
|
|
|
|
private readonly record struct DedupeEntry(ulong Sequence, DateTime TimestampUtc);
|
|
}
|