Skip to content

Commit d9158cd

Browse files
committed
2 parents dee9dba + a64ca14 commit d9158cd

File tree

8 files changed

+298
-43
lines changed

8 files changed

+298
-43
lines changed
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Net;
4+
5+
namespace StackExchange.Redis;
6+
7+
/// <summary>
8+
/// Filter determining which Redis clients to kill.
9+
/// </summary>
10+
/// <seealso href="https://redis.io/docs/latest/commands/client-kill/"/>
11+
public class ClientKillFilter
12+
{
13+
/// <summary>
14+
/// Filter arguments builder for `CLIENT KILL`.
15+
/// </summary>
16+
public ClientKillFilter() { }
17+
18+
/// <summary>
19+
/// The ID of the client to kill.
20+
/// </summary>
21+
public long? Id { get; private set; }
22+
23+
/// <summary>
24+
/// The type of client.
25+
/// </summary>
26+
public ClientType? ClientType { get; private set; }
27+
28+
/// <summary>
29+
/// The authenticated ACL username.
30+
/// </summary>
31+
public string? Username { get; private set; }
32+
33+
/// <summary>
34+
/// The endpoint to kill.
35+
/// </summary>
36+
public EndPoint? Endpoint { get; private set; }
37+
38+
/// <summary>
39+
/// The server endpoint to kill.
40+
/// </summary>
41+
public EndPoint? ServerEndpoint { get; private set; }
42+
43+
/// <summary>
44+
/// Whether to skip the current connection.
45+
/// </summary>
46+
public bool? SkipMe { get; private set; }
47+
48+
/// <summary>
49+
/// Age of connection in seconds.
50+
/// </summary>
51+
public long? MaxAgeInSeconds { get; private set; }
52+
53+
/// <summary>
54+
/// Sets client id filter.
55+
/// </summary>
56+
/// <param name="id">Id of the client to kill.</param>
57+
public ClientKillFilter WithId(long? id)
58+
{
59+
Id = id;
60+
return this;
61+
}
62+
63+
/// <summary>
64+
/// Sets client type filter.
65+
/// </summary>
66+
/// <param name="clientType">The type of the client.</param>
67+
public ClientKillFilter WithClientType(ClientType? clientType)
68+
{
69+
ClientType = clientType;
70+
return this;
71+
}
72+
73+
/// <summary>
74+
/// Sets the username filter.
75+
/// </summary>
76+
/// <param name="username">Authenticated ACL username.</param>
77+
public ClientKillFilter WithUsername(string? username)
78+
{
79+
Username = username;
80+
return this;
81+
}
82+
83+
/// <summary>
84+
/// Set the endpoint filter.
85+
/// </summary>
86+
/// <param name="endpoint">The endpoint to kill.</param>
87+
public ClientKillFilter WithEndpoint(EndPoint? endpoint)
88+
{
89+
Endpoint = endpoint;
90+
return this;
91+
}
92+
93+
/// <summary>
94+
/// Set the server endpoint filter.
95+
/// </summary>
96+
/// <param name="serverEndpoint">The server endpoint to kill.</param>
97+
public ClientKillFilter WithServerEndpoint(EndPoint? serverEndpoint)
98+
{
99+
ServerEndpoint = serverEndpoint;
100+
return this;
101+
}
102+
103+
/// <summary>
104+
/// Set the skipMe filter (whether to skip the current connection).
105+
/// </summary>
106+
/// <param name="skipMe">Whether to skip the current connection.</param>
107+
public ClientKillFilter WithSkipMe(bool? skipMe)
108+
{
109+
SkipMe = skipMe;
110+
return this;
111+
}
112+
113+
/// <summary>
114+
/// Set the MaxAgeInSeconds filter.
115+
/// </summary>
116+
/// <param name="maxAgeInSeconds">Age of connection in seconds</param>
117+
public ClientKillFilter WithMaxAgeInSeconds(long? maxAgeInSeconds)
118+
{
119+
MaxAgeInSeconds = maxAgeInSeconds;
120+
return this;
121+
}
122+
123+
internal List<RedisValue> ToList(bool withReplicaCommands)
124+
{
125+
var parts = new List<RedisValue>(15)
126+
{
127+
RedisLiterals.KILL
128+
};
129+
if (Id != null)
130+
{
131+
parts.Add(RedisLiterals.ID);
132+
parts.Add(Id.Value);
133+
}
134+
if (ClientType != null)
135+
{
136+
parts.Add(RedisLiterals.TYPE);
137+
switch (ClientType.Value)
138+
{
139+
case Redis.ClientType.Normal:
140+
parts.Add(RedisLiterals.normal);
141+
break;
142+
case Redis.ClientType.Replica:
143+
parts.Add(withReplicaCommands ? RedisLiterals.replica : RedisLiterals.slave);
144+
break;
145+
case Redis.ClientType.PubSub:
146+
parts.Add(RedisLiterals.pubsub);
147+
break;
148+
default:
149+
throw new ArgumentOutOfRangeException(nameof(ClientType));
150+
}
151+
}
152+
if (Username != null)
153+
{
154+
parts.Add(RedisLiterals.USERNAME);
155+
parts.Add(Username);
156+
}
157+
if (Endpoint != null)
158+
{
159+
parts.Add(RedisLiterals.ADDR);
160+
parts.Add((RedisValue)Format.ToString(Endpoint));
161+
}
162+
if (ServerEndpoint != null)
163+
{
164+
parts.Add(RedisLiterals.LADDR);
165+
parts.Add((RedisValue)Format.ToString(ServerEndpoint));
166+
}
167+
if (SkipMe != null)
168+
{
169+
parts.Add(RedisLiterals.SKIPME);
170+
parts.Add(SkipMe.Value ? RedisLiterals.yes : RedisLiterals.no);
171+
}
172+
if (MaxAgeInSeconds != null)
173+
{
174+
parts.Add(RedisLiterals.MAXAGE);
175+
parts.Add(MaxAgeInSeconds);
176+
}
177+
return parts;
178+
}
179+
}

src/StackExchange.Redis/Interfaces/IServer.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,18 @@ public partial interface IServer : IRedis
107107
/// <inheritdoc cref="ClientKill(long?, ClientType?, EndPoint?, bool, CommandFlags)"/>
108108
Task<long> ClientKillAsync(long? id = null, ClientType? clientType = null, EndPoint? endpoint = null, bool skipMe = true, CommandFlags flags = CommandFlags.None);
109109

110+
/// <summary>
111+
/// The CLIENT KILL command closes multiple connections that match the specified filters.
112+
/// </summary>
113+
/// <param name="filter"></param>
114+
/// <param name="flags"></param>
115+
/// <returns></returns>
116+
long ClientKill(ClientKillFilter filter, CommandFlags flags = CommandFlags.None);
117+
118+
/// <inheritdoc cref="ClientKill(ClientKillFilter, CommandFlags)"/>
119+
Task<long> ClientKillAsync(ClientKillFilter filter, CommandFlags flags = CommandFlags.None);
120+
121+
110122
/// <summary>
111123
/// The <c>CLIENT LIST</c> command returns information and statistics about the client connections server in a mostly human readable format.
112124
/// </summary>

src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,22 @@ StackExchange.Redis.ClientInfo.ProtocolVersion.get -> string?
126126
StackExchange.Redis.ClientInfo.Raw.get -> string?
127127
StackExchange.Redis.ClientInfo.SubscriptionCount.get -> int
128128
StackExchange.Redis.ClientInfo.TransactionCommandLength.get -> int
129+
StackExchange.Redis.ClientKillFilter
130+
StackExchange.Redis.ClientKillFilter.ClientKillFilter() -> void
131+
StackExchange.Redis.ClientKillFilter.ClientType.get -> StackExchange.Redis.ClientType?
132+
StackExchange.Redis.ClientKillFilter.Endpoint.get -> System.Net.EndPoint?
133+
StackExchange.Redis.ClientKillFilter.Id.get -> long?
134+
StackExchange.Redis.ClientKillFilter.MaxAgeInSeconds.get -> long?
135+
StackExchange.Redis.ClientKillFilter.ServerEndpoint.get -> System.Net.EndPoint?
136+
StackExchange.Redis.ClientKillFilter.SkipMe.get -> bool?
137+
StackExchange.Redis.ClientKillFilter.Username.get -> string?
138+
StackExchange.Redis.ClientKillFilter.WithClientType(StackExchange.Redis.ClientType? clientType) -> StackExchange.Redis.ClientKillFilter!
139+
StackExchange.Redis.ClientKillFilter.WithEndpoint(System.Net.EndPoint? endpoint) -> StackExchange.Redis.ClientKillFilter!
140+
StackExchange.Redis.ClientKillFilter.WithId(long? id) -> StackExchange.Redis.ClientKillFilter!
141+
StackExchange.Redis.ClientKillFilter.WithMaxAgeInSeconds(long? maxAgeInSeconds) -> StackExchange.Redis.ClientKillFilter!
142+
StackExchange.Redis.ClientKillFilter.WithServerEndpoint(System.Net.EndPoint? serverEndpoint) -> StackExchange.Redis.ClientKillFilter!
143+
StackExchange.Redis.ClientKillFilter.WithSkipMe(bool? skipMe) -> StackExchange.Redis.ClientKillFilter!
144+
StackExchange.Redis.ClientKillFilter.WithUsername(string? username) -> StackExchange.Redis.ClientKillFilter!
129145
StackExchange.Redis.ClientType
130146
StackExchange.Redis.ClientType.Normal = 0 -> StackExchange.Redis.ClientType
131147
StackExchange.Redis.ClientType.PubSub = 2 -> StackExchange.Redis.ClientType
@@ -1006,8 +1022,10 @@ StackExchange.Redis.IServer.AllowSlaveWrites.get -> bool
10061022
StackExchange.Redis.IServer.AllowSlaveWrites.set -> void
10071023
StackExchange.Redis.IServer.ClientKill(long? id = null, StackExchange.Redis.ClientType? clientType = null, System.Net.EndPoint? endpoint = null, bool skipMe = true, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long
10081024
StackExchange.Redis.IServer.ClientKill(System.Net.EndPoint! endpoint, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> void
1025+
StackExchange.Redis.IServer.ClientKill(StackExchange.Redis.ClientKillFilter! filter, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long
10091026
StackExchange.Redis.IServer.ClientKillAsync(long? id = null, StackExchange.Redis.ClientType? clientType = null, System.Net.EndPoint? endpoint = null, bool skipMe = true, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<long>!
10101027
StackExchange.Redis.IServer.ClientKillAsync(System.Net.EndPoint! endpoint, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task!
1028+
StackExchange.Redis.IServer.ClientKillAsync(StackExchange.Redis.ClientKillFilter! filter, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<long>!
10111029
StackExchange.Redis.IServer.ClientList(StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.ClientInfo![]!
10121030
StackExchange.Redis.IServer.ClientListAsync(StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<StackExchange.Redis.ClientInfo![]!>!
10131031
StackExchange.Redis.IServer.ClusterConfiguration.get -> StackExchange.Redis.ClusterConfiguration?
@@ -1851,4 +1869,5 @@ static StackExchange.Redis.RedisResult.Create(StackExchange.Redis.RedisValue[]!
18511869
virtual StackExchange.Redis.RedisResult.Length.get -> int
18521870
virtual StackExchange.Redis.RedisResult.this[int index].get -> StackExchange.Redis.RedisResult!
18531871
StackExchange.Redis.ConnectionMultiplexer.AddLibraryNameSuffix(string! suffix) -> void
1854-
StackExchange.Redis.IConnectionMultiplexer.AddLibraryNameSuffix(string! suffix) -> void
1872+
StackExchange.Redis.IConnectionMultiplexer.AddLibraryNameSuffix(string! suffix) -> void
1873+

src/StackExchange.Redis/RedisFeatures.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ namespace StackExchange.Redis
4141
v6_2_0 = new Version(6, 2, 0),
4242
v7_0_0_rc1 = new Version(6, 9, 240), // 7.0 RC1 is version 6.9.240
4343
v7_2_0_rc1 = new Version(7, 1, 240), // 7.2 RC1 is version 7.1.240
44-
v7_4_0_rc1 = new Version(7, 4, 240); // 7.4 RC1 is version 7.4.240
44+
v7_4_0_rc1 = new Version(7, 3, 240); // 7.4 RC1 is version 7.3.240
4545

4646
private readonly Version version;
4747

src/StackExchange.Redis/RedisLiterals.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ public static readonly RedisValue
8989
IDLETIME = "IDLETIME",
9090
KEEPTTL = "KEEPTTL",
9191
KILL = "KILL",
92+
LADDR = "LADDR",
9293
LATEST = "LATEST",
9394
LEFT = "LEFT",
9495
LEN = "LEN",
@@ -101,6 +102,7 @@ public static readonly RedisValue
101102
MATCH = "MATCH",
102103
MALLOC_STATS = "MALLOC-STATS",
103104
MAX = "MAX",
105+
MAXAGE = "MAXAGE",
104106
MAXLEN = "MAXLEN",
105107
MIN = "MIN",
106108
MINMATCHLEN = "MINMATCHLEN",
@@ -137,6 +139,7 @@ public static readonly RedisValue
137139
STATS = "STATS",
138140
STORE = "STORE",
139141
TYPE = "TYPE",
142+
USERNAME = "USERNAME",
140143
WEIGHTS = "WEIGHTS",
141144
WITHMATCHLEN = "WITHMATCHLEN",
142145
WITHSCORES = "WITHSCORES",

src/StackExchange.Redis/RedisServer.cs

Lines changed: 16 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -75,46 +75,22 @@ public Task<long> ClientKillAsync(long? id = null, ClientType? clientType = null
7575
return ExecuteAsync(msg, ResultProcessor.Int64);
7676
}
7777

78-
private Message GetClientKillMessage(EndPoint? endpoint, long? id, ClientType? clientType, bool skipMe, CommandFlags flags)
78+
public long ClientKill(ClientKillFilter filter, CommandFlags flags = CommandFlags.None)
7979
{
80-
var parts = new List<RedisValue>(9)
81-
{
82-
RedisLiterals.KILL
83-
};
84-
if (id != null)
85-
{
86-
parts.Add(RedisLiterals.ID);
87-
parts.Add(id.Value);
88-
}
89-
if (clientType != null)
90-
{
91-
parts.Add(RedisLiterals.TYPE);
92-
switch (clientType.Value)
93-
{
94-
case ClientType.Normal:
95-
parts.Add(RedisLiterals.normal);
96-
break;
97-
case ClientType.Replica:
98-
parts.Add(Features.ReplicaCommands ? RedisLiterals.replica : RedisLiterals.slave);
99-
break;
100-
case ClientType.PubSub:
101-
parts.Add(RedisLiterals.pubsub);
102-
break;
103-
default:
104-
throw new ArgumentOutOfRangeException(nameof(clientType));
105-
}
106-
}
107-
if (endpoint != null)
108-
{
109-
parts.Add(RedisLiterals.ADDR);
110-
parts.Add((RedisValue)Format.ToString(endpoint));
111-
}
112-
if (!skipMe)
113-
{
114-
parts.Add(RedisLiterals.SKIPME);
115-
parts.Add(RedisLiterals.no);
116-
}
117-
return Message.Create(-1, flags, RedisCommand.CLIENT, parts);
80+
var msg = Message.Create(-1, flags, RedisCommand.CLIENT, filter.ToList(Features.ReplicaCommands));
81+
return ExecuteSync(msg, ResultProcessor.Int64);
82+
}
83+
84+
public Task<long> ClientKillAsync(ClientKillFilter filter, CommandFlags flags = CommandFlags.None)
85+
{
86+
var msg = Message.Create(-1, flags, RedisCommand.CLIENT, filter.ToList(Features.ReplicaCommands));
87+
return ExecuteAsync(msg, ResultProcessor.Int64);
88+
}
89+
90+
private Message GetClientKillMessage(EndPoint? endpoint, long? id, ClientType? clientType, bool? skipMe, CommandFlags flags)
91+
{
92+
var args = new ClientKillFilter().WithId(id).WithClientType(clientType).WithEndpoint(endpoint).WithSkipMe(skipMe).ToList(Features.ReplicaCommands);
93+
return Message.Create(-1, flags, RedisCommand.CLIENT, args);
11894
}
11995

12096
public ClientInfo[] ClientList(CommandFlags flags = CommandFlags.None)
@@ -408,7 +384,7 @@ public Task<DateTime> LastSaveAsync(CommandFlags flags = CommandFlags.None)
408384
}
409385

410386
public void MakeMaster(ReplicationChangeOptions options, TextWriter? log = null)
411-
{
387+
{
412388
// Do you believe in magic?
413389
multiplexer.MakePrimaryAsync(server, options, log).Wait(60000);
414390
}

tests/RedisConfigs/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM redis:7.2-rc1
1+
FROM redis:7.4-rc1
22

33
COPY Basic /data/Basic/
44
COPY Failover /data/Failover/

0 commit comments

Comments
 (0)