feat: complete final jetstream parity transport and runtime baselines

This commit is contained in:
Joseph Doherty
2026-02-23 11:04:43 -05:00
parent 53585012f3
commit 8bce096f55
61 changed files with 2655 additions and 129 deletions

View File

@@ -48,8 +48,14 @@ public sealed class StreamManager
var handle = _streams.AddOrUpdate(
normalized.Name,
_ => new StreamHandle(normalized, new MemStore()),
(_, existing) => existing with { Config = normalized });
_ => new StreamHandle(normalized, CreateStore(normalized)),
(_, existing) =>
{
if (existing.Config.Storage == normalized.Storage)
return existing with { Config = normalized };
return new StreamHandle(normalized, CreateStore(normalized));
});
_replicaGroups.AddOrUpdate(
normalized.Name,
_ => new StreamReplicaGroup(normalized.Name, normalized.Replicas),
@@ -150,6 +156,25 @@ public sealed class StreamManager
if (stream == null)
return null;
var stateBefore = stream.Store.GetStateAsync(default).GetAwaiter().GetResult();
if (stream.Config.MaxBytes > 0 && (long)stateBefore.Bytes + payload.Length > stream.Config.MaxBytes)
{
if (stream.Config.Discard == DiscardPolicy.New)
{
return new PubAck
{
Stream = stream.Config.Name,
ErrorCode = 10054,
};
}
while ((long)stateBefore.Bytes + payload.Length > stream.Config.MaxBytes && stateBefore.FirstSeq > 0)
{
stream.Store.RemoveAsync(stateBefore.FirstSeq, default).GetAwaiter().GetResult();
stateBefore = stream.Store.GetStateAsync(default).GetAwaiter().GetResult();
}
}
if (_replicaGroups.TryGetValue(stream.Config.Name, out var replicaGroup))
_ = replicaGroup.ProposeAsync($"PUB {subject}", default).GetAwaiter().GetResult();
@@ -181,12 +206,17 @@ public sealed class StreamManager
Name = config.Name,
Subjects = config.Subjects.Count == 0 ? [] : [.. config.Subjects],
MaxMsgs = config.MaxMsgs,
MaxBytes = config.MaxBytes,
MaxMsgsPer = config.MaxMsgsPer,
MaxAgeMs = config.MaxAgeMs,
MaxConsumers = config.MaxConsumers,
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 })],
};
return copy;
@@ -241,6 +271,18 @@ public sealed class StreamManager
var list = _sourcesByOrigin.GetOrAdd(stream.Config.Source, _ => []);
list.Add(new SourceCoordinator(stream.Store));
}
if (stream.Config.Sources.Count > 0)
{
foreach (var source in stream.Config.Sources)
{
if (string.IsNullOrWhiteSpace(source.Name) || !_streams.TryGetValue(source.Name, out _))
continue;
var list = _sourcesByOrigin.GetOrAdd(source.Name, _ => []);
list.Add(new SourceCoordinator(stream.Store));
}
}
}
}
@@ -258,6 +300,30 @@ public sealed class StreamManager
source.OnOriginAppendAsync(stored, default).GetAwaiter().GetResult();
}
}
public string GetStoreBackendType(string streamName)
{
if (!_streams.TryGetValue(streamName, out var stream))
return "missing";
return stream.Store switch
{
FileStore => "file",
_ => "memory",
};
}
private static IStreamStore CreateStore(StreamConfig config)
{
return config.Storage switch
{
StorageType.File => new FileStore(new FileStoreOptions
{
Directory = Path.Combine(Path.GetTempPath(), "natsdotnet-js-store", config.Name),
}),
_ => new MemStore(),
};
}
}
public sealed record StreamHandle(StreamConfig Config, IStreamStore Store);