Skip to content

Commit b774d07

Browse files
vazoisCopilot
andauthored
Fix INFO all/default/everything returning empty response (#1645)
* Fix INFO all/default/everything returning empty response Implement support for INFO all, default, and everything options: - all: returns all DefaultInfo sections excluding module-generated ones - default: returns the default set of sections (same as no-arg INFO) - everything: returns all DefaultInfo sections including modules Added pre-declared HashSet collections (AllInfoSet, EverythingInfoSet) in GarnetInfoMetrics.cs derived from DefaultInfo to support these options. Updated InfoCommand.cs to use UnionWith with the new HashSets instead of silently skipping the ALL keyword. Added tests for all three options, verifying correct section inclusion/exclusion. Fixes #1643 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * make everything option use DefaultInfo * Add null/empty guard in GetSectionHeaders test helper Adds explicit asserts before splitting INFO output so test failures surface a clear message instead of a NullReferenceException. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * wip; restructuring cluster tests to reduce CI duration * separate dispose from close and configure socket to allow rapid connect * ensure socket is disposed succesfully * fix failing test * update global.json --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent e7891d5 commit b774d07

File tree

19 files changed

+799
-634
lines changed

19 files changed

+799
-634
lines changed

benchmark/BDN.benchmark/Embedded/GarnetServerEmbedded.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ public override void Start()
110110
{
111111
}
112112

113+
/// <inheritdoc />
114+
public override void Close()
115+
{
116+
}
117+
113118
public bool TryCreateMessageConsumer(Span<byte> bytes, INetworkSender networkSender, out IMessageConsumer session)
114119
{
115120
session = null;

global.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"sdk": {
3-
"version": "10.0.103",
3+
"version": "10.0.201",
44
"rollForward": "latestMajor",
55
"allowPrerelease": false
66
}

libs/host/GarnetServer.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,14 @@ public void Dispose(bool deleteDir = true)
478478

479479
private void InternalDispose()
480480
{
481+
// Phase 1: Stop listening on all servers to free ports immediately.
482+
for (var i = 0; i < servers.Length; i++)
483+
servers[i]?.Close();
484+
485+
// Phase 2: Dispose the provider (storage engine shutdown — may take time).
481486
Provider?.Dispose();
487+
488+
// Phase 3: Drain active handlers and clean up remaining resources.
482489
for (var i = 0; i < servers.Length; i++)
483490
servers[i]?.Dispose();
484491
subscribeBroker?.Dispose();

libs/server/Lua/LuaTimeoutManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ internal Registration RegisterForTimeout(SessionScriptCache cache)
263263
goto tryAgain;
264264
}
265265

266-
// Other threads might update registrations, so check that before returning
266+
// Other threads might update registrations, so check that before returning
267267
checkUnmodified:
268268
if ((updatedRegistrations = Interlocked.CompareExchange(ref registrations, curRegistrations, curRegistrations)) != curRegistrations)
269269
{

libs/server/Metrics/Info/GarnetInfoMetrics.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ class GarnetInfoMetrics
2525
_ => true
2626
})];
2727

28+
/// <summary>
29+
/// All info sections excluding module-generated ones.
30+
/// </summary>
31+
public static readonly HashSet<InfoMetricsType> AllInfoSet = [.. DefaultInfo.Where(e => e != InfoMetricsType.MODULES)];
32+
2833
MetricsItem[] serverInfo = null;
2934
MetricsItem[] memoryInfo = null;
3035
MetricsItem[] clusterInfo = null;

libs/server/Metrics/Info/InfoCommand.cs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,20 @@ private bool NetworkINFO()
2828
reset = true;
2929
else if (sbSection.EqualsUpperCaseSpanIgnoringCase("HELP"u8))
3030
help = true;
31-
else if (!sbSection.EqualsUpperCaseSpanIgnoringCase("ALL"u8))
31+
else if (sbSection.EqualsUpperCaseSpanIgnoringCase("ALL"u8))
32+
sections.UnionWith(GarnetInfoMetrics.AllInfoSet);
33+
else if (sbSection.EqualsUpperCaseSpanIgnoringCase("DEFAULT"u8))
34+
sections.UnionWith(GarnetInfoMetrics.DefaultInfo);
35+
else if (sbSection.EqualsUpperCaseSpanIgnoringCase("EVERYTHING"u8))
36+
sections.UnionWith(GarnetInfoMetrics.DefaultInfo);
37+
else if (parseState.TryGetInfoMetricsType(i, out var sectionType))
3238
{
33-
if (parseState.TryGetInfoMetricsType(i, out var sectionType))
34-
{
35-
sections.Add(sectionType);
36-
}
37-
else
38-
{
39-
invalid = true;
40-
invalidSection = parseState.GetString(i);
41-
}
39+
sections.Add(sectionType);
40+
}
41+
else
42+
{
43+
invalid = true;
44+
invalidSection = parseState.GetString(i);
4245
}
4346
}
4447
}

libs/server/Metrics/Info/InfoHelp.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ class InfoHelp
1010
{
1111
internal const string HELP = "HELP";
1212
internal const string ALL = "ALL";
13+
internal const string DEFAULT = "DEFAULT";
14+
internal const string EVERYTHING = "EVERYTHING";
1315
internal const string RESET = "RESET";
1416

1517
public static List<string> GetInfoTypeHelpMessage()
@@ -33,7 +35,9 @@ public static List<string> GetInfoTypeHelpMessage()
3335
$"{nameof(InfoMetricsType.KEYSPACE)}: Database related statistics.",
3436
$"{nameof(InfoMetricsType.MODULES)}: Information related to loaded modules.",
3537
$"{nameof(InfoMetricsType.HLOGSCAN)}: Distribution of records in main store's hybrid log in-memory portion.",
36-
$"{nameof(ALL)}: Return all informational sections.",
38+
$"{nameof(ALL)}: Return all informational sections (excluding module generated ones).",
39+
$"{nameof(DEFAULT)}: Return the default set of informational sections.",
40+
$"{nameof(EVERYTHING)}: Return all informational sections including module generated ones.",
3741
$"{nameof(HELP)}: Print this help message.",
3842
$"{nameof(RESET)}: Reset stats.",
3943
"\r\n",

libs/server/Servers/GarnetServerBase.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,9 @@ public bool AddSession(WireFormat protocol, ref ISessionProvider provider, INetw
154154
/// <inheritdoc />
155155
public abstract void Start();
156156

157+
/// <inheritdoc />
158+
public abstract void Close();
159+
157160
/// <inheritdoc />
158161
public virtual void Dispose()
159162
{

libs/server/Servers/GarnetServerTcp.cs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,24 +82,47 @@ public GarnetServerTcp(
8282
this.unixSocketPath = unixSocketPath;
8383
this.unixSocketPermission = unixSocketPermission;
8484

85-
listenSocket = endpoint switch
85+
if (endpoint is UnixDomainSocketEndPoint unix)
8686
{
87-
UnixDomainSocketEndPoint unix => new Socket(unix.AddressFamily, SocketType.Stream, ProtocolType.Unspecified),
87+
// UDS Initialization & Cleanup
88+
listenSocket = new Socket(unix.AddressFamily, SocketType.Stream, ProtocolType.Unspecified);
89+
var socketPath = unix.ToString();
90+
if (File.Exists(socketPath))
91+
{
92+
File.Delete(socketPath);
93+
}
94+
}
95+
else
96+
{
97+
// TCP Initialization & Port Reuse
98+
listenSocket = new Socket(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
8899

89-
_ => new Socket(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
90-
};
100+
// Set reuse BEFORE Bind to handle TIME_WAIT states
101+
listenSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
102+
}
91103

92104
acceptEventArg = new SocketAsyncEventArgs();
93105
acceptEventArg.Completed += AcceptEventArg_Completed;
94106
}
95107

108+
/// <summary>
109+
/// Stop listening for new connections. Frees the listening port
110+
/// without waiting for active connections to drain.
111+
/// </summary>
112+
public override void Close()
113+
{
114+
listenSocket.Close();
115+
}
116+
96117
/// <summary>
97118
/// Dispose
98119
/// </summary>
99120
public override void Dispose()
100121
{
101-
base.Dispose();
122+
// Close listening socket to free the port and stop accepting new connections.
123+
// This also prevents new connections from arriving while DisposeActiveHandlers drains existing ones.
102124
listenSocket.Dispose();
125+
base.Dispose();
103126
acceptEventArg.UserToken = null;
104127
acceptEventArg.Dispose();
105128
networkPool?.Dispose();

libs/server/Servers/IGarnetServer.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,11 @@ public interface IGarnetServer : IDisposable
4646
/// Start server
4747
/// </summary>
4848
public void Start();
49+
50+
/// <summary>
51+
/// Stop listening for new connections. Frees the listening port
52+
/// without waiting for active connections to drain.
53+
/// </summary>
54+
public void Close();
4955
}
5056
}

0 commit comments

Comments
 (0)