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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user