feat(admin): replace dead ThreadPool counters with a network readout (#2663)
## Summary
The admin gump's **Performance** page still rendered `ThreadPool` worker / IOCP quotas — a RunUO leftover from `Socket.BeginReceive` days. Both figures are dead: IORingGroup drives RIO / io_uring / epoll / kqueue synchronously on the game loop, so the IOCP figure reads 0% forever, and the real off-loop work (serialization workers, password checks, advanced search, console input) runs on dedicated threads the worker figure never sees.
This replaces the block with a read-only network readout.
## Engine
- `NetState.GetNetworkStats()` — new public accessor returning a `NetworkStats` snapshot (ModernUO-owned record types in `Network/NetworkStats.cs`, so UOContent takes no dependency on transport types):
- live: connected / max sockets, authenticated count, per-tier growth usage, the three loop queues (throttled, flush pending, pending disconnects)
- configured: base / pre-auth / max buffer sizes, growth budget, memory ceiling, last-read available memory
- `LastSweep`: everything the once-a-minute maintenance sweep saw, including the refusal counters it resets
- The sweep already kept four of its results (`_lastTier*`, `_lastBaseCapacityBytes`) for change detection; those fold into one `SendBufferSweep` snapshot. Sweep logic and logging are unchanged.
- The accessor never calls `Maintain()`, so opening the page cannot rotate the retention window.
- Tick age is compared by subtraction; "has a sweep run" is a `bool`, not a zero-tick sentinel.
## Gump
```
Event Loop: Healthy
Connections: 123 / 4096 (117 authenticated)
Loop Queues: throttled 0, flush 3, closing 0
Send Buffers: [scrollable]
Base: 64.0 KB recv, 256.0 KB send; pre-auth 4.0 KB / 4.0 KB
Base pools: 10.2 MB
Growth tiers (live):
512.0 KB: 3 / 32 in use, floor 5
1.0 MB: 0 / 0 in use, floor 0
2.0 MB: 0 / 0 in use, floor 0
Budget: 16.0 MB of 256.0 MB, max 2.0 MB per socket
Ceiling: 80% of 7.8 GB available
Last sweep 42s ago: released 0 tier, 0 base
Refused: budget 0, at max 0, ceiling 0
```
This commit is contained in:
parent
24bcfee554
commit
29a3ab1bd4
4 changed files with 280 additions and 30 deletions
80
Projects/Server.Tests/Tests/Network/NetworkStatsTests.cs
Normal file
80
Projects/Server.Tests/Tests/Network/NetworkStatsTests.cs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
using System.Net.Sockets;
|
||||
using Server.Network;
|
||||
using Xunit;
|
||||
|
||||
namespace Server.Tests.Network;
|
||||
|
||||
[Collection("Sequential Server Tests")]
|
||||
public class NetworkStatsTests
|
||||
{
|
||||
private static byte[] Pattern(int length, int seed)
|
||||
{
|
||||
var data = new byte[length];
|
||||
new System.Random(seed).NextBytes(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MaintainSendBuffers_RecordsTheRefusalsItResets()
|
||||
{
|
||||
var previous = NetState._availableMemoryBytes;
|
||||
NetState ns = null;
|
||||
Socket client = null;
|
||||
|
||||
try
|
||||
{
|
||||
ns = PacketTestUtilities.CreateTestNetState(out client);
|
||||
ns._protocolState = NetState.ProtocolState.GameServer_AwaitingGameServerLogin;
|
||||
ns.Account = new MockAccount();
|
||||
var baseSize = ns._socket.SendBuffer.PhysicalSize;
|
||||
|
||||
NetState.MaintainSendBuffers(); // drain whatever earlier tests refused
|
||||
NetState._availableMemoryBytes = 1; // any working set is above 80% of one byte
|
||||
|
||||
for (var i = 0; i < 6; i++)
|
||||
{
|
||||
ns.Send(Pattern(baseSize / 4, i));
|
||||
}
|
||||
|
||||
// The sweep refreshes _availableMemoryBytes, so refusals must happen before it
|
||||
NetState.MaintainSendBuffers();
|
||||
var stats = NetState.GetNetworkStats();
|
||||
|
||||
Assert.True(stats.LastSweep.Ran);
|
||||
Assert.True(stats.LastSweep.CeilingRefusals >= 1);
|
||||
|
||||
// Counters are per sweep, not cumulative
|
||||
NetState.MaintainSendBuffers();
|
||||
Assert.Equal(0, NetState.GetNetworkStats().LastSweep.CeilingRefusals);
|
||||
}
|
||||
finally
|
||||
{
|
||||
NetState._availableMemoryBytes = previous;
|
||||
ns?.Dispose();
|
||||
client?.Close();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetNetworkStats_CountsAuthenticatedConnectionsSeparately()
|
||||
{
|
||||
var ns = PacketTestUtilities.CreateTestNetState(out var client);
|
||||
|
||||
try
|
||||
{
|
||||
var before = NetState.GetNetworkStats();
|
||||
ns._protocolState = NetState.ProtocolState.GameServer_AwaitingGameServerLogin;
|
||||
ns.Account = new MockAccount();
|
||||
var after = NetState.GetNetworkStats();
|
||||
|
||||
Assert.Equal(before.Connected, after.Connected);
|
||||
Assert.Equal(before.Authenticated + 1, after.Authenticated);
|
||||
Assert.True(after.Connected >= after.Authenticated);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ns.Dispose();
|
||||
client.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue