Skip to content

Commit

Permalink
mark the implicit RedisChannel operators as [Obsolete] (#2481)
Browse files Browse the repository at this point in the history
mark the implicit RedisChannel operators as [Obsolete]
  • Loading branch information
mgravell authored Jun 13, 2023
1 parent 3f0be76 commit ebd66fd
Show file tree
Hide file tree
Showing 22 changed files with 167 additions and 38 deletions.
1 change: 1 addition & 0 deletions docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Current package versions:
## Unreleased

- Fix [#2479](https://github.com/StackExchange/StackExchange.Redis/issues/2479): Add `RedisChannel.UseImplicitAutoPattern` (global) and `RedisChannel.IsPatternBased` ([#2480 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2480))
- Fix [#2479](https://github.com/StackExchange/StackExchange.Redis/issues/2479): Mark `RedisChannel` conversion operators as obsolete; add `RedisChannel.Literal` and `RedisChannel.Pattern` helpers ([#2481 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2481))
- Fix [#2449](https://github.com/StackExchange/StackExchange.Redis/issues/2449): Update `Pipelines.Sockets.Unofficial` to `v2.2.8` to support native AOT ([#2456 by eerhardt](https://github.com/StackExchange/StackExchange.Redis/pull/2456))

## 2.6.111
Expand Down
2 changes: 1 addition & 1 deletion src/StackExchange.Redis/ConfigurationOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -860,7 +860,7 @@ private ConfigurationOptions DoParse(string configuration, bool ignoreUnknown)
ClientName = value;
break;
case OptionKeys.ChannelPrefix:
ChannelPrefix = value;
ChannelPrefix = RedisChannel.Literal(value);
break;
case OptionKeys.ConfigChannel:
ConfigurationChannel = value;
Expand Down
12 changes: 6 additions & 6 deletions src/StackExchange.Redis/ConnectionMultiplexer.Sentinel.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
using System;
using Pipelines.Sockets.Unofficial;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Pipelines.Sockets.Unofficial;

namespace StackExchange.Redis;

Expand All @@ -30,9 +30,9 @@ internal void InitializeSentinel(LogProxy? logProxy)
// Subscribe to sentinel change events
ISubscriber sub = GetSubscriber();

if (sub.SubscribedEndpoint("+switch-master") == null)
if (sub.SubscribedEndpoint(RedisChannel.Literal("+switch-master")) == null)
{
sub.Subscribe("+switch-master", (__, message) =>
sub.Subscribe(RedisChannel.Literal("+switch-master"), (__, message) =>
{
string[] messageParts = ((string)message!).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
// We don't care about the result of this - we're just trying
Expand Down Expand Up @@ -68,9 +68,9 @@ internal void InitializeSentinel(LogProxy? logProxy)
ReconfigureAsync(first: false, reconfigureAll: true, logProxy, e.EndPoint, "Lost sentinel connection", false).Wait();

// Subscribe to new sentinels being added
if (sub.SubscribedEndpoint("+sentinel") == null)
if (sub.SubscribedEndpoint(RedisChannel.Literal("+sentinel")) == null)
{
sub.Subscribe("+sentinel", (_, message) =>
sub.Subscribe(RedisChannel.Literal("+sentinel"), (_, message) =>
{
string[] messageParts = ((string)message!).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
UpdateSentinelAddressList(messageParts[0]);
Expand Down
4 changes: 2 additions & 2 deletions src/StackExchange.Redis/ConnectionMultiplexer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2221,7 +2221,7 @@ public long PublishReconfigure(CommandFlags flags = CommandFlags.None)

private long PublishReconfigureImpl(CommandFlags flags) =>
ConfigurationChangedChannel is byte[] channel
? GetSubscriber().Publish(channel, RedisLiterals.Wildcard, flags)
? GetSubscriber().Publish(RedisChannel.Literal(channel), RedisLiterals.Wildcard, flags)
: 0;

/// <summary>
Expand All @@ -2231,7 +2231,7 @@ ConfigurationChangedChannel is byte[] channel
/// <returns>The number of instances known to have received the message (however, the actual number can be higher).</returns>
public Task<long> PublishReconfigureAsync(CommandFlags flags = CommandFlags.None) =>
ConfigurationChangedChannel is byte[] channel
? GetSubscriber().PublishAsync(channel, RedisLiterals.Wildcard, flags)
? GetSubscriber().PublishAsync(RedisChannel.Literal(channel), RedisLiterals.Wildcard, flags)
: CompletedTask<long>.Default(null);

/// <summary>
Expand Down
7 changes: 5 additions & 2 deletions src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs
Original file line number Diff line number Diff line change
Expand Up @@ -841,8 +841,11 @@ protected RedisValue SortGetToInner(RedisValue outer) =>
}
}

protected RedisChannel ToInner(RedisChannel outer) =>
RedisKey.ConcatenateBytes(Prefix, null, (byte[]?)outer);
protected RedisChannel ToInner(RedisChannel outer)
{
var combined = RedisKey.ConcatenateBytes(Prefix, null, (byte[]?)outer);
return new RedisChannel(combined, outer.IsPatternBased ? RedisChannel.PatternMode.Pattern : RedisChannel.PatternMode.Literal);
}

private Func<RedisKey, RedisKey>? mapFunction;
protected Func<RedisKey, RedisKey> GetMapFunction() =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ internal async static Task AddListenerAsync(ConnectionMultiplexer multiplexer, A
return;
}

await sub.SubscribeAsync(PubSubChannelName, async (_, message) =>
await sub.SubscribeAsync(RedisChannel.Literal(PubSubChannelName), async (_, message) =>
{
var newMessage = new AzureMaintenanceEvent(message!);
newMessage.NotifyMultiplexer(multiplexer);
Expand Down
2 changes: 1 addition & 1 deletion src/StackExchange.Redis/PhysicalBridge.cs
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ internal void KeepAlive()
else if (commandMap.IsAvailable(RedisCommand.UNSUBSCRIBE))
{
msg = Message.Create(-1, CommandFlags.FireAndForget, RedisCommand.UNSUBSCRIBE,
(RedisChannel)Multiplexer.UniqueId);
RedisChannel.Literal(Multiplexer.UniqueId));
msg.SetSource(ResultProcessor.TrackSubscriptions, null);
}
break;
Expand Down
4 changes: 4 additions & 0 deletions src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1658,6 +1658,8 @@ static StackExchange.Redis.RedisChannel.implicit operator byte[]?(StackExchange.
static StackExchange.Redis.RedisChannel.implicit operator StackExchange.Redis.RedisChannel(byte[]? key) -> StackExchange.Redis.RedisChannel
static StackExchange.Redis.RedisChannel.implicit operator StackExchange.Redis.RedisChannel(string! key) -> StackExchange.Redis.RedisChannel
static StackExchange.Redis.RedisChannel.implicit operator string?(StackExchange.Redis.RedisChannel key) -> string?
static StackExchange.Redis.RedisChannel.Literal(byte[]! value) -> StackExchange.Redis.RedisChannel
static StackExchange.Redis.RedisChannel.Literal(string! value) -> StackExchange.Redis.RedisChannel
static StackExchange.Redis.RedisChannel.operator !=(byte[]! x, StackExchange.Redis.RedisChannel y) -> bool
static StackExchange.Redis.RedisChannel.operator !=(StackExchange.Redis.RedisChannel x, byte[]! y) -> bool
static StackExchange.Redis.RedisChannel.operator !=(StackExchange.Redis.RedisChannel x, StackExchange.Redis.RedisChannel y) -> bool
Expand All @@ -1668,6 +1670,8 @@ static StackExchange.Redis.RedisChannel.operator ==(StackExchange.Redis.RedisCha
static StackExchange.Redis.RedisChannel.operator ==(StackExchange.Redis.RedisChannel x, StackExchange.Redis.RedisChannel y) -> bool
static StackExchange.Redis.RedisChannel.operator ==(StackExchange.Redis.RedisChannel x, string! y) -> bool
static StackExchange.Redis.RedisChannel.operator ==(string! x, StackExchange.Redis.RedisChannel y) -> bool
static StackExchange.Redis.RedisChannel.Pattern(byte[]! value) -> StackExchange.Redis.RedisChannel
static StackExchange.Redis.RedisChannel.Pattern(string! value) -> StackExchange.Redis.RedisChannel
static StackExchange.Redis.RedisChannel.UseImplicitAutoPattern.get -> bool
static StackExchange.Redis.RedisChannel.UseImplicitAutoPattern.set -> void
static StackExchange.Redis.RedisFeatures.operator !=(StackExchange.Redis.RedisFeatures left, StackExchange.Redis.RedisFeatures right) -> bool
Expand Down
30 changes: 29 additions & 1 deletion src/StackExchange.Redis/RedisChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,23 @@ public static bool UseImplicitAutoPattern
}
private static PatternMode s_DefaultPatternMode = PatternMode.Auto;

/// <summary>
/// Creates a new <see cref="RedisChannel"/> that does not act as a wildcard subscription
/// </summary>
public static RedisChannel Literal(string value) => new RedisChannel(value, PatternMode.Literal);
/// <summary>
/// Creates a new <see cref="RedisChannel"/> that does not act as a wildcard subscription
/// </summary>
public static RedisChannel Literal(byte[] value) => new RedisChannel(value, PatternMode.Literal);
/// <summary>
/// Creates a new <see cref="RedisChannel"/> that acts as a wildcard subscription
/// </summary>
public static RedisChannel Pattern(string value) => new RedisChannel(value, PatternMode.Pattern);
/// <summary>
/// Creates a new <see cref="RedisChannel"/> that acts as a wildcard subscription
/// </summary>
public static RedisChannel Pattern(byte[] value) => new RedisChannel(value, PatternMode.Pattern);

/// <summary>
/// Create a new redis channel from a buffer, explicitly controlling the pattern mode.
/// </summary>
Expand Down Expand Up @@ -176,7 +193,16 @@ internal void AssertNotNull()
if (IsNull) throw new ArgumentException("A null key is not valid in this context");
}

internal RedisChannel Clone() => (byte[]?)Value?.Clone() ?? default;
internal RedisChannel Clone()
{
if (Value is null || Value.Length == 0)
{
// no need to duplicate anything
return this;
}
var copy = (byte[])Value.Clone(); // defensive array copy
return new RedisChannel(copy, _isPatternBased);
}

/// <summary>
/// The matching pattern for this channel.
Expand All @@ -201,6 +227,7 @@ public enum PatternMode
/// Create a channel name from a <see cref="string"/>.
/// </summary>
/// <param name="key">The string to get a channel from.</param>
[Obsolete("It is preferable to explicitly specify a " + nameof(PatternMode) + ", or use the " + nameof(Literal) + "/" + nameof(Pattern) + " methods", error: false)]
public static implicit operator RedisChannel(string key)
{
if (key == null) return default;
Expand All @@ -211,6 +238,7 @@ public static implicit operator RedisChannel(string key)
/// Create a channel name from a <see cref="T:byte[]"/>.
/// </summary>
/// <param name="key">The byte array to get a channel from.</param>
[Obsolete("It is preferable to explicitly specify a " + nameof(PatternMode) + ", or use the " + nameof(Literal) + "/" + nameof(Pattern) + " methods", error: false)]
public static implicit operator RedisChannel(byte[]? key)
{
if (key == null) return default;
Expand Down
2 changes: 1 addition & 1 deletion src/StackExchange.Redis/ServerEndPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -979,7 +979,7 @@ private async Task HandshakeAsync(PhysicalConnection connection, LogProxy? log)
var configChannel = Multiplexer.ConfigurationChangedChannel;
if (configChannel != null)
{
msg = Message.Create(-1, CommandFlags.FireAndForget, RedisCommand.SUBSCRIBE, (RedisChannel)configChannel);
msg = Message.Create(-1, CommandFlags.FireAndForget, RedisCommand.SUBSCRIBE, RedisChannel.Literal(configChannel));
// Note: this is NOT internal, we want it to queue in a backlog for sending when ready if necessary
await WriteDirectOrQueueFireAndForgetAsync(connection, msg, ResultProcessor.TrackSubscriptions).ForAwait();
}
Expand Down
2 changes: 1 addition & 1 deletion tests/ConsoleTest/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ static void ParallelRun(int taskId, ConnectionMultiplexer connection)
static void MassPublish(ConnectionMultiplexer connection)
{
var subscriber = connection.GetSubscriber();
Parallel.For(0, 1000, _ => subscriber.Publish("cache-events:cache-testing", "hey"));
Parallel.For(0, 1000, _ => subscriber.Publish(new RedisChannel("cache-events:cache-testing", RedisChannel.PatternMode.Literal), "hey"));
}

static string GetLibVersion()
Expand Down
41 changes: 41 additions & 0 deletions tests/StackExchange.Redis.Tests/ChannelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ public void ValidateAutoPatternModeString(string name, bool useImplicitAutoPatte
try
{
RedisChannel.UseImplicitAutoPattern = useImplicitAutoPattern;
#pragma warning disable CS0618 // we need to test the operator
RedisChannel channel = name;
#pragma warning restore CS0618
Assert.Equal(isPatternBased, channel.IsPatternBased);
}
finally
Expand Down Expand Up @@ -71,7 +73,9 @@ public void ValidateAutoPatternModeBytes(string name, bool useImplicitAutoPatter
try
{
RedisChannel.UseImplicitAutoPattern = useImplicitAutoPattern;
#pragma warning disable CS0618 // we need to test the operator
RedisChannel channel = bytes;
#pragma warning restore CS0618
Assert.Equal(isPatternBased, channel.IsPatternBased);
}
finally
Expand Down Expand Up @@ -108,5 +112,42 @@ public void ValidateModeSpecifiedIgnoresGlobalSettingBytes(string name, RedisCha
RedisChannel.UseImplicitAutoPattern = oldValue;
}
}

[Theory]
[InlineData("abc*def", false)]
[InlineData("abcdef", false)]
[InlineData("abc*def", true)]
[InlineData("abcdef", true)]
public void ValidateLiteralPatternMode(string name, bool useImplicitAutoPattern)
{
bool oldValue = RedisChannel.UseImplicitAutoPattern;
try
{
RedisChannel.UseImplicitAutoPattern = useImplicitAutoPattern;
RedisChannel channel;

// literal, string
channel = RedisChannel.Literal(name);
Assert.False(channel.IsPatternBased);

// pattern, string
channel = RedisChannel.Pattern(name);
Assert.True(channel.IsPatternBased);

var bytes = Encoding.UTF8.GetBytes(name);

// literal, byte[]
channel = RedisChannel.Literal(bytes);
Assert.False(channel.IsPatternBased);

// pattern, byte[]
channel = RedisChannel.Pattern(bytes);
Assert.True(channel.IsPatternBased);
}
finally
{
RedisChannel.UseImplicitAutoPattern = oldValue;
}
}
}
}
2 changes: 1 addition & 1 deletion tests/StackExchange.Redis.Tests/ConfigTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ public void ConnectWithSubscribeDisabled()
Assert.True(servers[0].IsConnected);
Assert.False(servers[0].IsSubscriberConnected);

var ex = Assert.Throws<RedisCommandException>(() => conn.GetSubscriber().Subscribe(Me(), (_, _) => GC.KeepAlive(this)));
var ex = Assert.Throws<RedisCommandException>(() => conn.GetSubscriber().Subscribe(RedisChannel.Literal(Me()), (_, _) => GC.KeepAlive(this)));
Assert.Equal("This operation has been disabled in the command-map and cannot be used: SUBSCRIBE", ex.Message);
}

Expand Down
6 changes: 3 additions & 3 deletions tests/StackExchange.Redis.Tests/Issues/Issue1101Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public async Task ExecuteWithUnsubscribeViaChannel()
{
using var conn = Create(log: Writer);

RedisChannel name = Me();
RedisChannel name = RedisChannel.Literal(Me());
var pubsub = conn.GetSubscriber();
AssertCounts(pubsub, name, false, 0, 0);

Expand Down Expand Up @@ -93,7 +93,7 @@ public async Task ExecuteWithUnsubscribeViaSubscriber()
{
using var conn = Create(shared: false, log: Writer);

RedisChannel name = Me();
RedisChannel name = RedisChannel.Literal(Me());
var pubsub = conn.GetSubscriber();
AssertCounts(pubsub, name, false, 0, 0);

Expand Down Expand Up @@ -144,7 +144,7 @@ public async Task ExecuteWithUnsubscribeViaClearAll()
{
using var conn = Create(log: Writer);

RedisChannel name = Me();
RedisChannel name = RedisChannel.Literal(Me());
var pubsub = conn.GetSubscriber();
AssertCounts(pubsub, name, false, 0, 0);

Expand Down
2 changes: 2 additions & 0 deletions tests/StackExchange.Redis.Tests/KeyPrefixedDatabaseTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -568,8 +568,10 @@ public void LockTake()
[Fact]
public void Publish()
{
#pragma warning disable CS0618
prefixed.Publish("channel", "message", CommandFlags.None);
mock.Verify(_ => _.Publish("prefix:channel", "message", CommandFlags.None));
#pragma warning restore CS0618
}

[Fact]
Expand Down
4 changes: 2 additions & 2 deletions tests/StackExchange.Redis.Tests/KeyPrefixedTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -528,8 +528,8 @@ public void LockTakeAsync()
[Fact]
public void PublishAsync()
{
prefixed.PublishAsync("channel", "message", CommandFlags.None);
mock.Verify(_ => _.PublishAsync("prefix:channel", "message", CommandFlags.None));
prefixed.PublishAsync(RedisChannel.Literal("channel"), "message", CommandFlags.None);
mock.Verify(_ => _.PublishAsync(RedisChannel.Literal("prefix:channel"), "message", CommandFlags.None));
}

[Fact]
Expand Down
4 changes: 2 additions & 2 deletions tests/StackExchange.Redis.Tests/PreserveOrderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public void Execute()
var received = new List<int>();
Log("Subscribing...");
const int COUNT = 500;
sub.Subscribe(channel, (_, message) =>
sub.Subscribe(RedisChannel.Literal(channel), (_, message) =>
{
lock (received)
{
Expand All @@ -42,7 +42,7 @@ public void Execute()
// it all goes to the server and back
for (int i = 0; i < COUNT; i++)
{
sub.Publish(channel, i, CommandFlags.FireAndForget);
sub.Publish(RedisChannel.Literal(channel), i, CommandFlags.FireAndForget);
}

Log("Allowing time for delivery etc...");
Expand Down
10 changes: 10 additions & 0 deletions tests/StackExchange.Redis.Tests/PubSubCommandTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ public void SubscriberCount()
{
using var conn = Create();

#pragma warning disable CS0618
RedisChannel channel = Me() + Guid.NewGuid();
var server = conn.GetServer(conn.GetEndPoints()[0]);

var channels = server.SubscriptionChannels(Me() + "*");
#pragma warning restore CS0618
Assert.DoesNotContain(channel, channels);

_ = server.SubscriptionPatternCount();
Expand All @@ -30,7 +32,9 @@ public void SubscriberCount()
count = server.SubscriptionSubscriberCount(channel);
Assert.Equal(1, count);

#pragma warning disable CS0618
channels = server.SubscriptionChannels(Me() + "*");
#pragma warning restore CS0618
Assert.Contains(channel, channels);
}

Expand All @@ -39,10 +43,14 @@ public async Task SubscriberCountAsync()
{
using var conn = Create();

#pragma warning disable CS0618
RedisChannel channel = Me() + Guid.NewGuid();
#pragma warning restore CS0618
var server = conn.GetServer(conn.GetEndPoints()[0]);

#pragma warning disable CS0618
var channels = await server.SubscriptionChannelsAsync(Me() + "*").WithTimeout(2000);
#pragma warning restore CS0618
Assert.DoesNotContain(channel, channels);

_ = await server.SubscriptionPatternCountAsync().WithTimeout(2000);
Expand All @@ -52,7 +60,9 @@ public async Task SubscriberCountAsync()
count = await server.SubscriptionSubscriberCountAsync(channel).WithTimeout(2000);
Assert.Equal(1, count);

#pragma warning disable CS0618
channels = await server.SubscriptionChannelsAsync(Me() + "*").WithTimeout(2000);
#pragma warning restore CS0618
Assert.Contains(channel, channels);
}
}
Expand Down
Loading

0 comments on commit ebd66fd

Please sign in to comment.