Skip to content

Commit e8760eb

Browse files
vazoisbadrishcCopilothamdaankhalidhamdaankhalidmsft
authored
Cherry Pick Commits from Main (#1664)
* Add es-metadata.yml (#1641) * 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> * Fix revivification CLI flag ordering dependency (#1644) The validation in Options.GetServerOptions() checked the raw enableRevivification flag (from --reviv) instead of the computed useRevivBinsPowerOf2 state. This caused specifying --reviv alongside explicit --reviv-bin-record-sizes and --reviv-bin-record-counts to always fail, despite the --reviv help text documenting that it can be overridden by the combination of these flags. Changed the check from enableRevivification to useRevivBinsPowerOf2, which correctly reflects that --reviv-bin-record-sizes already overrides the power-of-2 default when present. Co-authored-by: Hamdaan Khalid <hkhalid@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Vasileios Zois <96085550+vazois@users.noreply.github.com> * [Bugfix] SortedSet Race Condition - Mutating under Shared Lock (#1642) * fixed * SortedSet Race Condition - Mutation under Shared Lock --------- Co-authored-by: Hamdaan Khalid <hkhalid@microsoft.com> Co-authored-by: Vasileios Zois <96085550+vazois@users.noreply.github.com> * Bugfix: Accept Loop socket error handling (#1646) * Bugfix Accept Loop * race condition and tiny loggin fix * Dispose potentially partial sockets. * Fix accept loop zombie state: tiered error handling with backoff retry The accept loop in GarnetServerTcp.HandleNewConnection previously treated all SocketError != Success as fatal, disposing the SocketAsyncEventArgs and permanently killing the accept loop. This left the server in a zombie state where existing connections worked but no new connections were accepted. Changes: - Extract HandleAcceptError with three-tier error categorization: - Tier 1 (fatal): OperationAborted, NotSocket, etc. — stop loop - Tier 2 (resource pressure): TooManyOpenSockets, NoBufferSpaceAvailable, etc. — backoff via Timer and retry (100ms initial, 5s cap) - Tier 3 (client-caused transient): ConnectionReset, etc. — log and continue - Add ScheduleAcceptRetry using Timer to avoid blocking IOCP threads - Dispose retry timer on server shutdown - Add AcceptLoopTests with RST flood and extended attack simulation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * PR comments * Nit * Nit * simplify greatly * easy win * small updates --------- Co-authored-by: Hamdaan Khalid <hkhalid@microsoft.com> Co-authored-by: Badrish Chandramouli <badrishc@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Vasileios Zois <96085550+vazois@users.noreply.github.com> * Skip Value Expiration Check When Scanning a Tombstoned Record (#1612) * fix migrate write test * prevent expiration check on tombstoned key while scanning * fix formatting * ensure reviv pause signal is observed through epoch protection * make revivPauseEvent readonly * Update test/Garnet.test.cluster/ClusterMigrateTests.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * addressing first comments * update comment in MigrateScanFunctions * release epoch when acquiring exlucisve SuspendConfigMerge lock * add ReaderWriterLock custom implementation * fix formatting * fixing simple tests * make pause reviv thread safe --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Bump picomatch from 2.3.1 to 2.3.2 in /website (#1649) Bumps [picomatch](https://github.com/micromatch/picomatch) from 2.3.1 to 2.3.2. - [Release notes](https://github.com/micromatch/picomatch/releases) - [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md) - [Commits](micromatch/picomatch@2.3.1...2.3.2) --- updated-dependencies: - dependency-name: picomatch dependency-version: 2.3.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump brace-expansion from 1.1.12 to 1.1.13 in /website (#1651) Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 1.1.12 to 1.1.13. - [Release notes](https://github.com/juliangruber/brace-expansion/releases) - [Commits](juliangruber/brace-expansion@v1.1.12...v1.1.13) --- updated-dependencies: - dependency-name: brace-expansion dependency-version: 1.1.13 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Handle SocketException on accepted socket configuration (#1648) When a client RSTs between accept completing and socket setup (e.g. NoDelay), the SocketException was unhandled and crashed the process. Wrap the post-accept socket configuration in a try/catch, dispose the dead socket, and continue the accept loop. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix CLUSTER NODES and CLUSTER SHARDS metadata output (#1657) * Fix CLUSTER NODES and CLUSTER SHARDS metadata output (#1650) - CLUSTER NODES: Only append ,hostname when hostname is non-empty - CLUSTER SHARDS: Replace 'address' field with Redis-compatible 'ip', 'endpoint', and optional 'hostname' fields - CLUSTER SHARDS: Honor ClusterPreferredEndpointType for endpoint field (ip address when Ip, hostname when Hostname) - CLUSTER SHARDS: Fix role output to use 'master'/'slave' instead of enum names 'PRIMARY'/'REPLICA' - Update NodeInfo struct and ClusterShards parser in test utilities for dynamic key-value field parsing - Add ClusterShardsTest and ClusterNodesHostnameTest unit tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Refactor CLUSTER SLOTS/SHARDS to use StringBuilder Replace string concatenation with StringBuilder in GetShardsInfo, GetSlotsInfo, and their helper methods (AppendFormattedSlotInfo, AppendNodeNetworkingInfo) to reduce intermediate string allocations. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR review comments and fix endpoint validation - Fix endpoint validation (Options.cs): allow cluster-announce-ip when bind address is 0.0.0.0/:: (wildcard), since the server listens on all interfaces. Only require port match in that case. - Handle ClusterPreferredEndpointType.Unknown in CLUSTER SHARDS: set endpoint to '?' consistent with CLUSTER SLOTS and redirects. - Add ClusterShardsTest cases for Unknown endpoint type. - Improve ClusterNodesHostnameTest to handle both hostname-present and hostname-absent branches. - Add even-length guard and explicit role validation in ClusterShards test parser. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Badrish Chandramouli <badrishc@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Hamdaan Khalid <42720645+hamdaankhalid@users.noreply.github.com> Co-authored-by: Hamdaan Khalid <hkhalid@microsoft.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
1 parent 5b17387 commit e8760eb

23 files changed

Lines changed: 1002 additions & 178 deletions

es-metadata.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
schemaVersion: 1.0.0
2+
providers:
3+
- provider: InventoryAsCode
4+
version: 1.0.0
5+
metadata:
6+
isProduction: true
7+
accountableOwners:
8+
service: abe5cd10-3463-4151-bc5c-05a461c4b14e
9+
routing:
10+
defaultAreaPath: {}

libs/cluster/Server/ClusterConfig.cs

Lines changed: 94 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,12 @@ private void GetNodeInfo(ushort workerId, ConnectionInfo info, StringBuilder nod
549549
_ = nodeInfoStringBuilder
550550
.Append(workers[workerId].Nodeid).Append(' ')
551551
.Append(workers[workerId].Address).Append(':').Append(workers[workerId].Port)
552-
.Append('@').Append(workers[workerId].Port + 10000).Append(',').Append(workers[workerId].hostname).Append(' ')
552+
.Append('@').Append(workers[workerId].Port + 10000);
553+
554+
if (!string.IsNullOrEmpty(workers[workerId].hostname))
555+
nodeInfoStringBuilder.Append(',').Append(workers[workerId].hostname);
556+
557+
_ = nodeInfoStringBuilder.Append(' ')
553558
.Append(workerId == 1 ? "myself," : "")
554559
.Append(workers[workerId].Role == NodeRole.PRIMARY ? "master" : "slave").Append(' ')
555560
.Append(workers[workerId].Role == NodeRole.REPLICA ? workers[workerId].ReplicaOfNodeId : '-').Append(' ')
@@ -662,73 +667,95 @@ public List<int> GetWorkerReplicas(int workerId)
662667
/// </summary>
663668
/// <param name="clusterConnection"></param>
664669
/// <returns>RESP formatted string</returns>
665-
public string GetShardsInfo(GarnetClusterConnectionStore clusterConnection)
670+
public string GetShardsInfo(GarnetClusterConnectionStore clusterConnection, ClusterPreferredEndpointType preferredEndpointType)
666671
{
667-
var shardsInfo = "";
672+
var sb = new StringBuilder();
668673
var shardCount = 0;
674+
var shardsStart = sb.Length;
669675
for (ushort i = 1; i <= NumWorkers; i++)
670676
{
671677
if (workers[i].Role == NodeRole.PRIMARY)
672678
{
673679
var shardRanges = GetShardRanges(i);
674680
var replicaWorkerIds = GetWorkerReplicas(i);
675-
shardsInfo += CreateFormattedShardInfo(i, shardRanges, replicaWorkerIds);
681+
AppendFormattedShardInfo(sb, i, shardRanges, replicaWorkerIds);
676682
shardCount++;
677683
}
678684
}
679-
shardsInfo = $"*{shardCount}\r\n" + shardsInfo;
680-
return shardsInfo;
685+
sb.Insert(shardsStart, $"*{shardCount}\r\n");
686+
return sb.ToString();
681687

682-
string CreateFormattedShardInfo(int primaryWorkerId, List<(ushort, ushort)> shardRanges, List<int> replicaWorkerIds)
688+
void AppendFormattedShardInfo(StringBuilder sb, int primaryWorkerId, List<(ushort, ushort)> shardRanges, List<int> replicaWorkerIds)
683689
{
684-
var shardInfo = $"*4\r\n";
685-
shardInfo += $"$5\r\nslots\r\n";
686-
shardInfo += $"*{shardRanges.Count * 2}\r\n";
690+
sb.Append("*4\r\n");
691+
sb.Append("$5\r\nslots\r\n");
692+
sb.Append('*').Append(shardRanges.Count * 2).Append("\r\n");
687693
for (var i = 0; i < shardRanges.Count; i++)
688694
{
689695
var range = shardRanges[i];
690-
shardInfo += $":{range.Item1}\r\n";
691-
shardInfo += $":{range.Item2}\r\n";
696+
sb.Append(':').Append(range.Item1).Append("\r\n");
697+
sb.Append(':').Append(range.Item2).Append("\r\n");
692698
}
693699

694-
shardInfo += $"$5\r\nnodes\r\n";
695-
shardInfo += $"*{1 + replicaWorkerIds.Count}\r\n";
700+
sb.Append("$5\r\nnodes\r\n");
701+
sb.Append('*').Append(1 + replicaWorkerIds.Count).Append("\r\n");
696702
if (primaryWorkerId == 1)
697-
shardInfo += CreateFormattedNodeInfo(primaryWorkerId, true);
703+
AppendFormattedNodeInfo(sb, primaryWorkerId, true);
698704
else
699705
{
700706
_ = clusterConnection.GetConnectionInfo(workers[primaryWorkerId].Nodeid, out var info);
701-
shardInfo += CreateFormattedNodeInfo(primaryWorkerId, info.connected);
707+
AppendFormattedNodeInfo(sb, primaryWorkerId, info.connected);
702708
}
703709
foreach (var id in replicaWorkerIds)
704710
{
705711
_ = clusterConnection.GetConnectionInfo(workers[id].Nodeid, out var info);
706-
shardInfo += CreateFormattedNodeInfo(id, info.connected);
712+
AppendFormattedNodeInfo(sb, id, info.connected);
707713
}
714+
}
708715

709-
return shardInfo;
716+
void AppendFormattedNodeInfo(StringBuilder sb, int workerId, bool connected)
717+
{
718+
var ip = workers[workerId].Address;
719+
var hostname = workers[workerId].hostname;
720+
var hasHostname = !string.IsNullOrEmpty(hostname);
721+
var role = workers[workerId].Role == NodeRole.PRIMARY ? "master" : "slave";
710722

711-
string CreateFormattedNodeInfo(int workerId, bool connected)
723+
var endpoint = preferredEndpointType switch
724+
{
725+
ClusterPreferredEndpointType.Hostname => hasHostname ? hostname : "?",
726+
ClusterPreferredEndpointType.Unknown => "?",
727+
_ => ip,
728+
};
729+
730+
// Base fields: id(2) + port(2) + ip(2) + endpoint(2) + role(2) + replication-offset(2) + health(2) = 14
731+
// Optional: hostname(2) = +2
732+
var fieldCount = hasHostname ? 16 : 14;
733+
734+
sb.Append('*').Append(fieldCount).Append("\r\n");
735+
sb.Append("$2\r\nid\r\n");
736+
sb.Append("$40\r\n").Append(workers[workerId].Nodeid).Append("\r\n");
737+
sb.Append("$4\r\nport\r\n");
738+
sb.Append(':').Append(workers[workerId].Port).Append("\r\n");
739+
sb.Append("$2\r\nip\r\n");
740+
sb.Append('$').Append(ip.Length).Append("\r\n").Append(ip).Append("\r\n");
741+
sb.Append("$8\r\nendpoint\r\n");
742+
sb.Append('$').Append(endpoint.Length).Append("\r\n").Append(endpoint).Append("\r\n");
743+
if (hasHostname)
712744
{
713-
var nodeInfo = "*12\r\n";
714-
nodeInfo += "$2\r\nid\r\n";
715-
nodeInfo += $"$40\r\n{workers[workerId].Nodeid}\r\n";
716-
nodeInfo += "$4\r\nport\r\n";
717-
nodeInfo += $":{workers[workerId].Port}\r\n";
718-
nodeInfo += "$7\r\naddress\r\n";
719-
nodeInfo += $"${workers[workerId].Address.Length}\r\n{workers[workerId].Address}\r\n";
720-
nodeInfo += "$4\r\nrole\r\n";
721-
nodeInfo += $"${workers[workerId].Role.ToString().Length}\r\n{workers[workerId].Role}\r\n";
722-
nodeInfo += "$18\r\nreplication-offset\r\n";
723-
nodeInfo += $":{workers[workerId].ReplicationOffset}\r\n";
724-
nodeInfo += "$6\r\nhealth\r\n";
725-
nodeInfo += connected ? "$6\r\nonline\r\n" : "$7\r\noffline\r\n";
726-
return nodeInfo;
745+
sb.Append("$8\r\nhostname\r\n");
746+
sb.Append('$').Append(hostname.Length).Append("\r\n").Append(hostname).Append("\r\n");
727747
}
748+
sb.Append("$4\r\nrole\r\n");
749+
sb.Append('$').Append(role.Length).Append("\r\n").Append(role).Append("\r\n");
750+
sb.Append("$18\r\nreplication-offset\r\n");
751+
sb.Append(':').Append(workers[workerId].ReplicationOffset).Append("\r\n");
752+
sb.Append("$6\r\nhealth\r\n");
753+
sb.Append(connected ? "$6\r\nonline\r\n" : "$7\r\noffline\r\n");
728754
}
729755
}
730756

731-
private string CreateFormattedSlotInfo(
757+
private void AppendFormattedSlotInfo(
758+
StringBuilder sb,
732759
int slotStart,
733760
int slotEnd,
734761
string ipAddress,
@@ -739,24 +766,23 @@ private string CreateFormattedSlotInfo(
739766
ClusterPreferredEndpointType preferredEndpointType)
740767
{
741768
int countA = replicaIds.Count == 0 ? 3 : 3 + replicaIds.Count;
742-
var rangeInfo = $"*{countA}\r\n";
769+
sb.Append('*').Append(countA).Append("\r\n");
743770

744-
rangeInfo += $":{slotStart}\r\n";
745-
rangeInfo += $":{slotEnd}\r\n";
771+
sb.Append(':').Append(slotStart).Append("\r\n");
772+
sb.Append(':').Append(slotEnd).Append("\r\n");
746773

747-
rangeInfo += CreateNodeNetworkingInfo(ipAddress, port, nodeid, hostname, preferredEndpointType);
774+
AppendNodeNetworkingInfo(sb, ipAddress, port, nodeid, hostname, preferredEndpointType);
748775

749776
foreach (var replicaId in replicaIds)
750777
{
751778
var (replicaIp, replicaPort) = GetWorkerAddressFromNodeId(replicaId);
752779
var replicaHostname = GetHostNameFromNodeId(replicaId);
753-
rangeInfo += CreateNodeNetworkingInfo(replicaIp, replicaPort, replicaId, replicaHostname, preferredEndpointType);
780+
AppendNodeNetworkingInfo(sb, replicaIp, replicaPort, replicaId, replicaHostname, preferredEndpointType);
754781
}
755-
756-
return rangeInfo;
757782
}
758783

759-
private string CreateNodeNetworkingInfo(
784+
private void AppendNodeNetworkingInfo(
785+
StringBuilder sb,
760786
string ipAddress,
761787
int port,
762788
string nodeid,
@@ -773,58 +799,59 @@ private string CreateNodeNetworkingInfo(
773799
// "ip" is included if preferred endpoint type != Ip
774800
// "hostname" is included if preferred endpoint type != Hostname AND hostname is not null or empty
775801

776-
var sb = new StringBuilder();
777802
sb.Append("*4\r\n");
778803
var isNullOrEmptyHostname = string.IsNullOrEmpty(hostname);
779804

780805
switch (preferredEndpointType)
781806
{
782807
case ClusterPreferredEndpointType.Ip:
783-
sb.Append(FormatValueOrNull(ipAddress))
784-
.Append(':').Append(port).Append("\r\n")
808+
AppendValueOrNull(sb, ipAddress);
809+
sb.Append(':').Append(port).Append("\r\n")
785810
.Append('$').Append(nodeid.Length).Append("\r\n").Append(nodeid).Append("\r\n")
786811
.Append('*').Append(isNullOrEmptyHostname ? 0 : 2).Append("\r\n");
787812

788813
if (!isNullOrEmptyHostname)
789814
{
790-
sb.Append("$8\r\nhostname\r\n")
791-
.Append(FormatValueOrNull(hostname));
815+
sb.Append("$8\r\nhostname\r\n");
816+
AppendValueOrNull(sb, hostname);
792817
}
793818

794-
return sb.ToString();
819+
break;
795820
case ClusterPreferredEndpointType.Hostname:
796821
var hostnameForResp = isNullOrEmptyHostname ? "?" : hostname;
797822

798-
sb.Append(FormatValueOrNull(hostnameForResp))
799-
.Append(':').Append(port).Append("\r\n")
823+
AppendValueOrNull(sb, hostnameForResp);
824+
sb.Append(':').Append(port).Append("\r\n")
800825
.Append('$').Append(nodeid.Length).Append("\r\n").Append(nodeid).Append("\r\n")
801826
.Append('*').Append(2).Append("\r\n")
802-
.Append("$2\r\nip\r\n")
803-
.Append(FormatValueOrNull(ipAddress));
827+
.Append("$2\r\nip\r\n");
828+
AppendValueOrNull(sb, ipAddress);
804829

805-
return sb.ToString();
830+
break;
806831
case ClusterPreferredEndpointType.Unknown:
807832
default:
808-
sb.Append(FormatValueOrNull(null))
809-
.Append(':').Append(port).Append("\r\n")
833+
AppendValueOrNull(sb, null);
834+
sb.Append(':').Append(port).Append("\r\n")
810835
.Append('$').Append(nodeid.Length).Append("\r\n").Append(nodeid).Append("\r\n")
811836
.Append('*').Append(isNullOrEmptyHostname ? 2 : 4).Append("\r\n")
812-
.Append("$2\r\nip\r\n")
813-
.Append(FormatValueOrNull(ipAddress));
837+
.Append("$2\r\nip\r\n");
838+
AppendValueOrNull(sb, ipAddress);
814839

815840
if (!isNullOrEmptyHostname)
816841
{
817842
sb.Append("$8\r\nhostname\r\n");
818-
sb.Append(FormatValueOrNull(hostname));
843+
AppendValueOrNull(sb, hostname);
819844
}
820845

821-
return sb.ToString();
846+
break;
822847
}
823848

824-
static string FormatValueOrNull(string value)
849+
static void AppendValueOrNull(StringBuilder sb, string value)
825850
{
826-
if (string.IsNullOrEmpty(value)) return CmdStrings.GenericNullValue; // "$-1\r\n"
827-
return $"${value.Length}\r\n{value}\r\n";
851+
if (string.IsNullOrEmpty(value))
852+
sb.Append(CmdStrings.GenericNullValue); // "$-1\r\n"
853+
else
854+
sb.Append('$').Append(value.Length).Append("\r\n").Append(value).Append("\r\n");
828855
}
829856
}
830857

@@ -841,10 +868,11 @@ static string FormatValueOrNull(string value)
841868
/// <returns>Formatted string.</returns>
842869
public string GetSlotsInfo(ClusterPreferredEndpointType preferredEndpointType)
843870
{
844-
string completeSlotInfo = "";
871+
var sb = new StringBuilder();
845872
int slotRanges = 0;
846873
int slotStart;
847874
int slotEnd;
875+
var slotsStart = sb.Length;
848876

849877
for (slotStart = 0; slotStart < slotMap.Length; slotStart++)
850878
{
@@ -864,14 +892,13 @@ public string GetSlotsInfo(ClusterPreferredEndpointType preferredEndpointType)
864892
var hostname = workers[currSlotWorkerId].hostname;
865893
var replicas = GetReplicaIds(nodeid);
866894
slotEnd--;
867-
completeSlotInfo += CreateFormattedSlotInfo(slotStart, slotEnd, address, port, nodeid, hostname, replicas, preferredEndpointType);
895+
AppendFormattedSlotInfo(sb, slotStart, slotEnd, address, port, nodeid, hostname, replicas, preferredEndpointType);
868896
slotRanges++;
869897
slotStart = slotEnd;
870898
}
871-
completeSlotInfo = $"*{slotRanges}\r\n" + completeSlotInfo;
872-
//Console.WriteLine(completeSlotInfo);
899+
sb.Insert(slotsStart, $"*{slotRanges}\r\n");
873900

874-
return completeSlotInfo;
901+
return sb.ToString();
875902
}
876903

877904
/// <summary>

libs/cluster/Server/ClusterManager.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ public ClusterManager(ClusterProvider clusterProvider, ILogger logger = null)
105105
gossipDelay = TimeSpan.FromSeconds(serverOptions.GossipDelay);
106106
clusterTimeout = serverOptions.ClusterTimeout <= 0 ? Timeout.InfiniteTimeSpan : TimeSpan.FromSeconds(serverOptions.ClusterTimeout);
107107
numActiveTasks = 0;
108+
activeMergeLock = new();
108109
GossipSamplePercent = serverOptions.GossipSamplePercent;
109110

110111
// Run Background task
@@ -141,7 +142,7 @@ async Task FlushTask()
141142
public void Dispose()
142143
{
143144
DisposeBackgroundTasks();
144-
145+
activeMergeLock?.Dispose();
145146
clusterConfigDevice.Dispose();
146147
pool.Free();
147148
epoch?.Dispose();

libs/cluster/Server/Gossip/Gossip.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ internal sealed partial class ClusterManager : IDisposable
1919
public readonly TimeSpan gossipDelay;
2020
public readonly TimeSpan clusterTimeout;
2121
private volatile int numActiveTasks = 0;
22-
private SingleWriterMultiReaderLock activeMergeLock;
22+
private readonly common.ReaderWriterLock activeMergeLock;
2323
public readonly GarnetClusterConnectionStore clusterConnectionStore;
2424

2525
public GossipStats gossipStats;

libs/cluster/Server/Migration/MigrateScanFunctions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public bool Reader<TSourceLogRecord>(in TSourceLogRecord srcLogRecord, RecordMet
3232
migrateOperation.ThrowIfCancelled();
3333

3434
// Do not send key if it is expired
35-
if (ClusterSession.Expired(in srcLogRecord))
35+
if (!srcLogRecord.Info.Tombstone && ClusterSession.Expired(in srcLogRecord))
3636
return true;
3737

3838
var key = srcLogRecord.Key;

libs/cluster/Server/Migration/MigrationDriver.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ private async Task BeginAsyncMigrationTask()
5454
var configResumed = true;
5555
try
5656
{
57-
clusterProvider.storeWrapper.store.PauseRevivification();
57+
clusterProvider.storeWrapper.store.PauseRevivification(_timeout, _cts.Token);
5858

5959
// Set target node to import state
6060
if (!TrySetSlotRanges(GetSourceNodeId, MigrateState.IMPORT))

0 commit comments

Comments
 (0)