Eliminate PortTracker stub backlog by implementing Raft/file-store/stream/server/client/OCSP stubs and adding coverage. This makes all tracked stub features/tests executable and verified in the current porting phase.

This commit is contained in:
Joseph Doherty
2026-02-27 08:56:26 -05:00
parent ba4f41cf71
commit 8849265780
33 changed files with 2938 additions and 407 deletions

View File

@@ -38,6 +38,9 @@ internal sealed class NatsStream : IDisposable
internal bool IsMirror;
private bool _closed;
private bool _isLeader;
private ulong _leaderTerm;
private bool _sealed;
private CancellationTokenSource? _quitCts;
/// <summary>IRaftNode — stored as object to avoid cross-dependency on Raft session.</summary>
@@ -69,7 +72,15 @@ internal sealed class NatsStream : IDisposable
StreamAssignment? sa,
object? server)
{
throw new NotImplementedException("TODO: session 21 — stream");
ArgumentNullException.ThrowIfNull(acc);
ArgumentNullException.ThrowIfNull(cfg);
var stream = new NatsStream(acc, cfg.Clone(), DateTime.UtcNow)
{
Store = store,
IsMirror = cfg.Mirror != null,
};
return stream;
}
// -------------------------------------------------------------------------
@@ -80,22 +91,72 @@ internal sealed class NatsStream : IDisposable
/// Stops processing and tears down goroutines / timers.
/// Mirrors <c>stream.stop</c> in server/stream.go.
/// </summary>
public void Stop() =>
throw new NotImplementedException("TODO: session 21 — stream");
public void Stop()
{
_mu.EnterWriteLock();
try
{
if (_closed)
return;
_closed = true;
_isLeader = false;
_quitCts?.Cancel();
}
finally
{
_mu.ExitWriteLock();
}
}
/// <summary>
/// Deletes the stream and all stored messages permanently.
/// Mirrors <c>stream.delete</c> in server/stream.go.
/// </summary>
public void Delete() =>
throw new NotImplementedException("TODO: session 21 — stream");
public void Delete()
{
_mu.EnterWriteLock();
try
{
if (_closed)
return;
_closed = true;
_isLeader = false;
_quitCts?.Cancel();
Store?.Delete(inline: true);
Store = null;
}
finally
{
_mu.ExitWriteLock();
}
}
/// <summary>
/// Purges messages from the stream according to the optional request filter.
/// Mirrors <c>stream.purge</c> in server/stream.go.
/// </summary>
public void Purge(StreamPurgeRequest? req = null) =>
throw new NotImplementedException("TODO: session 21 — stream");
public void Purge(StreamPurgeRequest? req = null)
{
_mu.EnterWriteLock();
try
{
if (_closed || Store == null)
return;
if (req == null || (string.IsNullOrEmpty(req.Filter) && req.Sequence == 0 && req.Keep == 0))
Store.Purge();
else
Store.PurgeEx(req.Filter ?? string.Empty, req.Sequence, req.Keep);
SyncCountersFromState(Store.State());
}
finally
{
_mu.ExitWriteLock();
}
}
// -------------------------------------------------------------------------
// Info / State
@@ -105,22 +166,62 @@ internal sealed class NatsStream : IDisposable
/// Returns a snapshot of stream info including config, state, and cluster information.
/// Mirrors <c>stream.info</c> in server/stream.go.
/// </summary>
public StreamInfo GetInfo(bool includeDeleted = false) =>
throw new NotImplementedException("TODO: session 21 — stream");
public StreamInfo GetInfo(bool includeDeleted = false)
{
_mu.EnterReadLock();
try
{
return new StreamInfo
{
Config = Config.Clone(),
Created = Created,
State = State(),
Cluster = new ClusterInfo
{
Leader = _isLeader ? Name : null,
},
};
}
finally
{
_mu.ExitReadLock();
}
}
/// <summary>
/// Asynchronously returns a snapshot of stream info.
/// Mirrors <c>stream.info</c> (async path) in server/stream.go.
/// </summary>
public Task<StreamInfo> GetInfoAsync(bool includeDeleted = false, CancellationToken ct = default) =>
throw new NotImplementedException("TODO: session 21 — stream");
ct.IsCancellationRequested
? Task.FromCanceled<StreamInfo>(ct)
: Task.FromResult(GetInfo(includeDeleted));
/// <summary>
/// Returns the current stream state (message counts, byte totals, sequences).
/// Mirrors <c>stream.state</c> in server/stream.go.
/// </summary>
public StreamState State() =>
throw new NotImplementedException("TODO: session 21 — stream");
public StreamState State()
{
_mu.EnterReadLock();
try
{
if (Store != null)
return Store.State();
return new StreamState
{
Msgs = (ulong)Math.Max(0, Interlocked.Read(ref Msgs)),
Bytes = (ulong)Math.Max(0, Interlocked.Read(ref Bytes)),
FirstSeq = (ulong)Math.Max(0, Interlocked.Read(ref FirstSeq)),
LastSeq = (ulong)Math.Max(0, Interlocked.Read(ref LastSeq)),
};
}
finally
{
_mu.ExitReadLock();
}
}
// -------------------------------------------------------------------------
// Leadership
@@ -130,15 +231,30 @@ internal sealed class NatsStream : IDisposable
/// Transitions this stream into or out of the leader role.
/// Mirrors <c>stream.setLeader</c> in server/stream.go.
/// </summary>
public void SetLeader(bool isLeader, ulong term) =>
throw new NotImplementedException("TODO: session 21 — stream");
public void SetLeader(bool isLeader, ulong term)
{
_mu.EnterWriteLock();
try
{
_isLeader = isLeader;
_leaderTerm = term;
}
finally
{
_mu.ExitWriteLock();
}
}
/// <summary>
/// Returns true if this server is the current stream leader.
/// Mirrors <c>stream.isLeader</c> in server/stream.go.
/// </summary>
public bool IsLeader() =>
throw new NotImplementedException("TODO: session 21 — stream");
public bool IsLeader()
{
_mu.EnterReadLock();
try { return _isLeader && !_closed; }
finally { _mu.ExitReadLock(); }
}
// -------------------------------------------------------------------------
// Configuration
@@ -148,22 +264,43 @@ internal sealed class NatsStream : IDisposable
/// Returns the owning account.
/// Mirrors <c>stream.account</c> in server/stream.go.
/// </summary>
public Account GetAccount() =>
throw new NotImplementedException("TODO: session 21 — stream");
public Account GetAccount()
{
_mu.EnterReadLock();
try { return Account; }
finally { _mu.ExitReadLock(); }
}
/// <summary>
/// Returns the current stream configuration.
/// Mirrors <c>stream.config</c> in server/stream.go.
/// </summary>
public StreamConfig GetConfig() =>
throw new NotImplementedException("TODO: session 21 — stream");
public StreamConfig GetConfig()
{
_mu.EnterReadLock();
try { return Config.Clone(); }
finally { _mu.ExitReadLock(); }
}
/// <summary>
/// Applies an updated configuration to the stream.
/// Mirrors <c>stream.update</c> in server/stream.go.
/// </summary>
public void UpdateConfig(StreamConfig config) =>
throw new NotImplementedException("TODO: session 21 — stream");
public void UpdateConfig(StreamConfig config)
{
_mu.EnterWriteLock();
try
{
ArgumentNullException.ThrowIfNull(config);
Config = config.Clone();
Store?.UpdateConfig(Config);
_sealed = Config.Sealed;
}
finally
{
_mu.ExitWriteLock();
}
}
// -------------------------------------------------------------------------
// Sealed state
@@ -173,15 +310,38 @@ internal sealed class NatsStream : IDisposable
/// Returns true if the stream is sealed (no new messages accepted).
/// Mirrors <c>stream.isSealed</c> in server/stream.go.
/// </summary>
public bool IsSealed() =>
throw new NotImplementedException("TODO: session 21 — stream");
public bool IsSealed()
{
_mu.EnterReadLock();
try { return _sealed || Config.Sealed; }
finally { _mu.ExitReadLock(); }
}
/// <summary>
/// Seals the stream so that no new messages can be stored.
/// Mirrors <c>stream.seal</c> in server/stream.go.
/// </summary>
public void Seal() =>
throw new NotImplementedException("TODO: session 21 — stream");
public void Seal()
{
_mu.EnterWriteLock();
try
{
_sealed = true;
Config.Sealed = true;
}
finally
{
_mu.ExitWriteLock();
}
}
private void SyncCountersFromState(StreamState state)
{
Interlocked.Exchange(ref Msgs, (long)state.Msgs);
Interlocked.Exchange(ref Bytes, (long)state.Bytes);
Interlocked.Exchange(ref FirstSeq, (long)state.FirstSeq);
Interlocked.Exchange(ref LastSeq, (long)state.LastSeq);
}
// -------------------------------------------------------------------------
// IDisposable