feat: execute post-baseline jetstream parity plan
This commit is contained in:
@@ -93,6 +93,8 @@ public sealed class StreamManager
|
||||
{
|
||||
if (!_streams.TryGetValue(name, out var stream))
|
||||
return false;
|
||||
if (stream.Config.Sealed || stream.Config.DenyPurge)
|
||||
return false;
|
||||
|
||||
stream.Store.PurgeAsync(default).GetAwaiter().GetResult();
|
||||
return true;
|
||||
@@ -110,6 +112,8 @@ public sealed class StreamManager
|
||||
{
|
||||
if (!_streams.TryGetValue(name, out var stream))
|
||||
return false;
|
||||
if (stream.Config.Sealed || stream.Config.DenyDelete)
|
||||
return false;
|
||||
|
||||
return stream.Store.RemoveAsync(sequence, default).GetAwaiter().GetResult();
|
||||
}
|
||||
@@ -156,6 +160,17 @@ public sealed class StreamManager
|
||||
if (stream == null)
|
||||
return null;
|
||||
|
||||
if (stream.Config.MaxMsgSize > 0 && payload.Length > stream.Config.MaxMsgSize)
|
||||
{
|
||||
return new PubAck
|
||||
{
|
||||
Stream = stream.Config.Name,
|
||||
ErrorCode = 10054,
|
||||
};
|
||||
}
|
||||
|
||||
PruneExpiredMessages(stream, DateTime.UtcNow);
|
||||
|
||||
var stateBefore = stream.Store.GetStateAsync(default).GetAwaiter().GetResult();
|
||||
if (stream.Config.MaxBytes > 0 && (long)stateBefore.Bytes + payload.Length > stream.Config.MaxBytes)
|
||||
{
|
||||
@@ -179,7 +194,7 @@ public sealed class StreamManager
|
||||
_ = replicaGroup.ProposeAsync($"PUB {subject}", default).GetAwaiter().GetResult();
|
||||
|
||||
var seq = stream.Store.AppendAsync(subject, payload, default).GetAwaiter().GetResult();
|
||||
EnforceLimits(stream);
|
||||
EnforceRuntimePolicies(stream, DateTime.UtcNow);
|
||||
var stored = stream.Store.LoadAsync(seq, default).GetAwaiter().GetResult();
|
||||
if (stored != null)
|
||||
ReplicateIfConfigured(stream.Config.Name, stored);
|
||||
@@ -209,14 +224,25 @@ public sealed class StreamManager
|
||||
MaxBytes = config.MaxBytes,
|
||||
MaxMsgsPer = config.MaxMsgsPer,
|
||||
MaxAgeMs = config.MaxAgeMs,
|
||||
MaxMsgSize = config.MaxMsgSize,
|
||||
MaxConsumers = config.MaxConsumers,
|
||||
DuplicateWindowMs = config.DuplicateWindowMs,
|
||||
Sealed = config.Sealed,
|
||||
DenyDelete = config.DenyDelete,
|
||||
DenyPurge = config.DenyPurge,
|
||||
AllowDirect = config.AllowDirect,
|
||||
Retention = config.Retention,
|
||||
Discard = config.Discard,
|
||||
Storage = config.Storage,
|
||||
Replicas = config.Replicas,
|
||||
Mirror = config.Mirror,
|
||||
Source = config.Source,
|
||||
Sources = config.Sources.Count == 0 ? [] : [.. config.Sources.Select(s => new StreamSourceConfig { Name = s.Name })],
|
||||
Sources = config.Sources.Count == 0 ? [] : [.. config.Sources.Select(s => new StreamSourceConfig
|
||||
{
|
||||
Name = s.Name,
|
||||
SubjectTransformPrefix = s.SubjectTransformPrefix,
|
||||
SourceAccount = s.SourceAccount,
|
||||
})],
|
||||
};
|
||||
|
||||
return copy;
|
||||
@@ -235,6 +261,13 @@ public sealed class StreamManager
|
||||
};
|
||||
}
|
||||
|
||||
private static void EnforceRuntimePolicies(StreamHandle stream, DateTime nowUtc)
|
||||
{
|
||||
EnforceLimits(stream);
|
||||
PrunePerSubject(stream);
|
||||
PruneExpiredMessages(stream, nowUtc);
|
||||
}
|
||||
|
||||
private static void EnforceLimits(StreamHandle stream)
|
||||
{
|
||||
if (stream.Config.MaxMsgs <= 0)
|
||||
@@ -251,6 +284,34 @@ public sealed class StreamManager
|
||||
fileStore.TrimToMaxMessages(maxMessages);
|
||||
}
|
||||
|
||||
private static void PrunePerSubject(StreamHandle stream)
|
||||
{
|
||||
if (stream.Config.MaxMsgsPer <= 0)
|
||||
return;
|
||||
|
||||
var maxPerSubject = stream.Config.MaxMsgsPer;
|
||||
var messages = stream.Store.ListAsync(default).GetAwaiter().GetResult();
|
||||
foreach (var group in messages.GroupBy(m => m.Subject, StringComparer.Ordinal))
|
||||
{
|
||||
foreach (var message in group.OrderByDescending(m => m.Sequence).Skip(maxPerSubject))
|
||||
stream.Store.RemoveAsync(message.Sequence, default).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
|
||||
private static void PruneExpiredMessages(StreamHandle stream, DateTime nowUtc)
|
||||
{
|
||||
if (stream.Config.MaxAgeMs <= 0)
|
||||
return;
|
||||
|
||||
var cutoff = nowUtc.AddMilliseconds(-stream.Config.MaxAgeMs);
|
||||
var messages = stream.Store.ListAsync(default).GetAwaiter().GetResult();
|
||||
foreach (var message in messages)
|
||||
{
|
||||
if (message.TimestampUtc < cutoff)
|
||||
stream.Store.RemoveAsync(message.Sequence, default).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
|
||||
private void RebuildReplicationCoordinators()
|
||||
{
|
||||
_mirrorsByOrigin.Clear();
|
||||
@@ -269,7 +330,7 @@ public sealed class StreamManager
|
||||
&& _streams.TryGetValue(stream.Config.Source, out _))
|
||||
{
|
||||
var list = _sourcesByOrigin.GetOrAdd(stream.Config.Source, _ => []);
|
||||
list.Add(new SourceCoordinator(stream.Store));
|
||||
list.Add(new SourceCoordinator(stream.Store, new StreamSourceConfig { Name = stream.Config.Source }));
|
||||
}
|
||||
|
||||
if (stream.Config.Sources.Count > 0)
|
||||
@@ -280,7 +341,7 @@ public sealed class StreamManager
|
||||
continue;
|
||||
|
||||
var list = _sourcesByOrigin.GetOrAdd(source.Name, _ => []);
|
||||
list.Add(new SourceCoordinator(stream.Store));
|
||||
list.Add(new SourceCoordinator(stream.Store, source));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -320,6 +381,7 @@ public sealed class StreamManager
|
||||
StorageType.File => new FileStore(new FileStoreOptions
|
||||
{
|
||||
Directory = Path.Combine(Path.GetTempPath(), "natsdotnet-js-store", config.Name),
|
||||
MaxAgeMs = config.MaxAgeMs,
|
||||
}),
|
||||
_ => new MemStore(),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user