From ebd66fdad11d69eb6957777354bcdc87f3cff4e4 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Tue, 13 Jun 2023 16:11:10 +0100 Subject: [PATCH] mark the implicit RedisChannel operators as [Obsolete] (#2481) mark the implicit RedisChannel operators as [Obsolete] --- docs/ReleaseNotes.md | 1 + .../ConfigurationOptions.cs | 2 +- .../ConnectionMultiplexer.Sentinel.cs | 12 ++-- .../ConnectionMultiplexer.cs | 4 +- .../KeyspaceIsolation/KeyPrefixed.cs | 7 ++- .../Maintenance/AzureMaintenanceEvent.cs | 2 +- src/StackExchange.Redis/PhysicalBridge.cs | 2 +- .../PublicAPI/PublicAPI.Shipped.txt | 4 ++ src/StackExchange.Redis/RedisChannel.cs | 30 +++++++++- src/StackExchange.Redis/ServerEndPoint.cs | 2 +- tests/ConsoleTest/Program.cs | 2 +- .../StackExchange.Redis.Tests/ChannelTests.cs | 41 ++++++++++++++ .../StackExchange.Redis.Tests/ConfigTests.cs | 2 +- .../Issues/Issue1101Tests.cs | 6 +- .../KeyPrefixedDatabaseTests.cs | 2 + .../KeyPrefixedTests.cs | 4 +- .../PreserveOrderTests.cs | 4 +- .../PubSubCommandTests.cs | 10 ++++ .../PubSubMultiserverTests.cs | 8 +-- .../StackExchange.Redis.Tests/PubSubTests.cs | 56 ++++++++++++++++--- .../SentinelFailoverTests.cs | 2 + tests/StackExchange.Redis.Tests/TestBase.cs | 2 +- 22 files changed, 167 insertions(+), 38 deletions(-) diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 93993e7eb..70b49d1b6 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -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 diff --git a/src/StackExchange.Redis/ConfigurationOptions.cs b/src/StackExchange.Redis/ConfigurationOptions.cs index e9f01264f..da30beb56 100644 --- a/src/StackExchange.Redis/ConfigurationOptions.cs +++ b/src/StackExchange.Redis/ConfigurationOptions.cs @@ -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; diff --git a/src/StackExchange.Redis/ConnectionMultiplexer.Sentinel.cs b/src/StackExchange.Redis/ConnectionMultiplexer.Sentinel.cs index 302bccea9..145826e3c 100644 --- a/src/StackExchange.Redis/ConnectionMultiplexer.Sentinel.cs +++ b/src/StackExchange.Redis/ConnectionMultiplexer.Sentinel.cs @@ -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; @@ -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 @@ -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]); diff --git a/src/StackExchange.Redis/ConnectionMultiplexer.cs b/src/StackExchange.Redis/ConnectionMultiplexer.cs index 37d45be09..88c6c37c2 100644 --- a/src/StackExchange.Redis/ConnectionMultiplexer.cs +++ b/src/StackExchange.Redis/ConnectionMultiplexer.cs @@ -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; /// @@ -2231,7 +2231,7 @@ ConfigurationChangedChannel is byte[] channel /// The number of instances known to have received the message (however, the actual number can be higher). public Task PublishReconfigureAsync(CommandFlags flags = CommandFlags.None) => ConfigurationChangedChannel is byte[] channel - ? GetSubscriber().PublishAsync(channel, RedisLiterals.Wildcard, flags) + ? GetSubscriber().PublishAsync(RedisChannel.Literal(channel), RedisLiterals.Wildcard, flags) : CompletedTask.Default(null); /// diff --git a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs index 290fbed59..3bad77d43 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs @@ -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? mapFunction; protected Func GetMapFunction() => diff --git a/src/StackExchange.Redis/Maintenance/AzureMaintenanceEvent.cs b/src/StackExchange.Redis/Maintenance/AzureMaintenanceEvent.cs index c965e1231..330b27683 100644 --- a/src/StackExchange.Redis/Maintenance/AzureMaintenanceEvent.cs +++ b/src/StackExchange.Redis/Maintenance/AzureMaintenanceEvent.cs @@ -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); diff --git a/src/StackExchange.Redis/PhysicalBridge.cs b/src/StackExchange.Redis/PhysicalBridge.cs index e7af56a69..b42c40a19 100644 --- a/src/StackExchange.Redis/PhysicalBridge.cs +++ b/src/StackExchange.Redis/PhysicalBridge.cs @@ -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; diff --git a/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt b/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt index 59f96637f..464af8023 100644 --- a/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt +++ b/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt @@ -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 @@ -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 diff --git a/src/StackExchange.Redis/RedisChannel.cs b/src/StackExchange.Redis/RedisChannel.cs index 40448beb1..16d4e7107 100644 --- a/src/StackExchange.Redis/RedisChannel.cs +++ b/src/StackExchange.Redis/RedisChannel.cs @@ -35,6 +35,23 @@ public static bool UseImplicitAutoPattern } private static PatternMode s_DefaultPatternMode = PatternMode.Auto; + /// + /// Creates a new that does not act as a wildcard subscription + /// + public static RedisChannel Literal(string value) => new RedisChannel(value, PatternMode.Literal); + /// + /// Creates a new that does not act as a wildcard subscription + /// + public static RedisChannel Literal(byte[] value) => new RedisChannel(value, PatternMode.Literal); + /// + /// Creates a new that acts as a wildcard subscription + /// + public static RedisChannel Pattern(string value) => new RedisChannel(value, PatternMode.Pattern); + /// + /// Creates a new that acts as a wildcard subscription + /// + public static RedisChannel Pattern(byte[] value) => new RedisChannel(value, PatternMode.Pattern); + /// /// Create a new redis channel from a buffer, explicitly controlling the pattern mode. /// @@ -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); + } /// /// The matching pattern for this channel. @@ -201,6 +227,7 @@ public enum PatternMode /// Create a channel name from a . /// /// The string to get a channel from. + [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; @@ -211,6 +238,7 @@ public static implicit operator RedisChannel(string key) /// Create a channel name from a . /// /// The byte array to get a channel from. + [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; diff --git a/src/StackExchange.Redis/ServerEndPoint.cs b/src/StackExchange.Redis/ServerEndPoint.cs index 36163578d..d3082e35c 100644 --- a/src/StackExchange.Redis/ServerEndPoint.cs +++ b/src/StackExchange.Redis/ServerEndPoint.cs @@ -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(); } diff --git a/tests/ConsoleTest/Program.cs b/tests/ConsoleTest/Program.cs index 1d33a968e..0d6fa7e13 100644 --- a/tests/ConsoleTest/Program.cs +++ b/tests/ConsoleTest/Program.cs @@ -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() diff --git a/tests/StackExchange.Redis.Tests/ChannelTests.cs b/tests/StackExchange.Redis.Tests/ChannelTests.cs index dc3fbdce1..3f11d2ef1 100644 --- a/tests/StackExchange.Redis.Tests/ChannelTests.cs +++ b/tests/StackExchange.Redis.Tests/ChannelTests.cs @@ -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 @@ -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 @@ -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; + } + } } } diff --git a/tests/StackExchange.Redis.Tests/ConfigTests.cs b/tests/StackExchange.Redis.Tests/ConfigTests.cs index c07b32c8a..b11c968cf 100644 --- a/tests/StackExchange.Redis.Tests/ConfigTests.cs +++ b/tests/StackExchange.Redis.Tests/ConfigTests.cs @@ -276,7 +276,7 @@ public void ConnectWithSubscribeDisabled() Assert.True(servers[0].IsConnected); Assert.False(servers[0].IsSubscriberConnected); - var ex = Assert.Throws(() => conn.GetSubscriber().Subscribe(Me(), (_, _) => GC.KeepAlive(this))); + var ex = Assert.Throws(() => 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); } diff --git a/tests/StackExchange.Redis.Tests/Issues/Issue1101Tests.cs b/tests/StackExchange.Redis.Tests/Issues/Issue1101Tests.cs index 440d18a8e..3f248b480 100644 --- a/tests/StackExchange.Redis.Tests/Issues/Issue1101Tests.cs +++ b/tests/StackExchange.Redis.Tests/Issues/Issue1101Tests.cs @@ -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); @@ -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); @@ -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); diff --git a/tests/StackExchange.Redis.Tests/KeyPrefixedDatabaseTests.cs b/tests/StackExchange.Redis.Tests/KeyPrefixedDatabaseTests.cs index 96f7d4c85..b4eff605a 100644 --- a/tests/StackExchange.Redis.Tests/KeyPrefixedDatabaseTests.cs +++ b/tests/StackExchange.Redis.Tests/KeyPrefixedDatabaseTests.cs @@ -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] diff --git a/tests/StackExchange.Redis.Tests/KeyPrefixedTests.cs b/tests/StackExchange.Redis.Tests/KeyPrefixedTests.cs index d5fea204c..2d6b53390 100644 --- a/tests/StackExchange.Redis.Tests/KeyPrefixedTests.cs +++ b/tests/StackExchange.Redis.Tests/KeyPrefixedTests.cs @@ -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] diff --git a/tests/StackExchange.Redis.Tests/PreserveOrderTests.cs b/tests/StackExchange.Redis.Tests/PreserveOrderTests.cs index b0f7df995..f11cda5c5 100644 --- a/tests/StackExchange.Redis.Tests/PreserveOrderTests.cs +++ b/tests/StackExchange.Redis.Tests/PreserveOrderTests.cs @@ -22,7 +22,7 @@ public void Execute() var received = new List(); Log("Subscribing..."); const int COUNT = 500; - sub.Subscribe(channel, (_, message) => + sub.Subscribe(RedisChannel.Literal(channel), (_, message) => { lock (received) { @@ -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..."); diff --git a/tests/StackExchange.Redis.Tests/PubSubCommandTests.cs b/tests/StackExchange.Redis.Tests/PubSubCommandTests.cs index 809a9968e..dd77dc106 100644 --- a/tests/StackExchange.Redis.Tests/PubSubCommandTests.cs +++ b/tests/StackExchange.Redis.Tests/PubSubCommandTests.cs @@ -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(); @@ -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); } @@ -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); @@ -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); } } diff --git a/tests/StackExchange.Redis.Tests/PubSubMultiserverTests.cs b/tests/StackExchange.Redis.Tests/PubSubMultiserverTests.cs index d3e634a47..dcf706e76 100644 --- a/tests/StackExchange.Redis.Tests/PubSubMultiserverTests.cs +++ b/tests/StackExchange.Redis.Tests/PubSubMultiserverTests.cs @@ -18,8 +18,8 @@ public void ChannelSharding() using var conn = (Create(channelPrefix: Me()) as ConnectionMultiplexer)!; var defaultSlot = conn.ServerSelectionStrategy.HashSlot(default(RedisChannel)); - var slot1 = conn.ServerSelectionStrategy.HashSlot((RedisChannel)"hey"); - var slot2 = conn.ServerSelectionStrategy.HashSlot((RedisChannel)"hey2"); + var slot1 = conn.ServerSelectionStrategy.HashSlot(RedisChannel.Literal("hey")); + var slot2 = conn.ServerSelectionStrategy.HashSlot(RedisChannel.Literal("hey2")); Assert.NotEqual(defaultSlot, slot1); Assert.NotEqual(ServerSelectionStrategy.NoSlot, slot1); @@ -34,7 +34,7 @@ public async Task ClusterNodeSubscriptionFailover() using var conn = (Create(allowAdmin: true) as ConnectionMultiplexer)!; var sub = conn.GetSubscriber(); - var channel = (RedisChannel)Me(); + var channel = RedisChannel.Literal(Me()); var count = 0; Log("Subscribing..."); @@ -108,7 +108,7 @@ public async Task PrimaryReplicaSubscriptionFailover(CommandFlags flags, bool ex using var conn = (Create(configuration: config, shared: false, allowAdmin: true) as ConnectionMultiplexer)!; var sub = conn.GetSubscriber(); - var channel = (RedisChannel)(Me() + flags.ToString()); // Individual channel per case to not overlap publishers + var channel = RedisChannel.Literal(Me() + flags.ToString()); // Individual channel per case to not overlap publishers var count = 0; Log("Subscribing..."); diff --git a/tests/StackExchange.Redis.Tests/PubSubTests.cs b/tests/StackExchange.Redis.Tests/PubSubTests.cs index 8fd23ffdd..833d888e9 100644 --- a/tests/StackExchange.Redis.Tests/PubSubTests.cs +++ b/tests/StackExchange.Redis.Tests/PubSubTests.cs @@ -27,9 +27,11 @@ public async Task ExplicitPublishMode() pub.Subscribe(new RedisChannel("*bcd", RedisChannel.PatternMode.Literal), (x, y) => Interlocked.Increment(ref a)); pub.Subscribe(new RedisChannel("a*cd", RedisChannel.PatternMode.Pattern), (x, y) => Interlocked.Increment(ref b)); pub.Subscribe(new RedisChannel("ab*d", RedisChannel.PatternMode.Auto), (x, y) => Interlocked.Increment(ref c)); +#pragma warning disable CS0618 pub.Subscribe("abc*", (x, y) => Interlocked.Increment(ref d)); pub.Publish("abcd", "efg"); +#pragma warning restore CS0618 await UntilConditionAsync(TimeSpan.FromSeconds(10), () => Thread.VolatileRead(ref b) == 1 && Thread.VolatileRead(ref c) == 1 @@ -39,7 +41,9 @@ await UntilConditionAsync(TimeSpan.FromSeconds(10), Assert.Equal(1, Thread.VolatileRead(ref c)); Assert.Equal(1, Thread.VolatileRead(ref d)); +#pragma warning disable CS0618 pub.Publish("*bcd", "efg"); +#pragma warning restore CS0618 await UntilConditionAsync(TimeSpan.FromSeconds(10), () => Thread.VolatileRead(ref a) == 1); Assert.Equal(1, Thread.VolatileRead(ref a)); } @@ -77,15 +81,19 @@ public async Task TestBasicPubSub(string channelPrefix, bool wildCard, string br } } , handler2 = (_, __) => Interlocked.Increment(ref secondHandler); +#pragma warning disable CS0618 sub.Subscribe(subChannel, handler1); sub.Subscribe(subChannel, handler2); +#pragma warning restore CS0618 lock (received) { Assert.Empty(received); } Assert.Equal(0, Thread.VolatileRead(ref secondHandler)); +#pragma warning disable CS0618 var count = sub.Publish(pubChannel, "def"); +#pragma warning restore CS0618 await PingAsync(pub, sub, 3).ForAwait(); @@ -99,8 +107,10 @@ public async Task TestBasicPubSub(string channelPrefix, bool wildCard, string br Assert.Equal(1, Thread.VolatileRead(ref secondHandler)); // unsubscribe from first; should still see second +#pragma warning disable CS0618 sub.Unsubscribe(subChannel, handler1); count = sub.Publish(pubChannel, "ghi"); +#pragma warning restore CS0618 await PingAsync(pub, sub).ForAwait(); lock (received) { @@ -115,8 +125,10 @@ public async Task TestBasicPubSub(string channelPrefix, bool wildCard, string br Assert.Equal(1, count); // unsubscribe from second; should see nothing this time +#pragma warning disable CS0618 sub.Unsubscribe(subChannel, handler2); count = sub.Publish(pubChannel, "ghi"); +#pragma warning restore CS0618 await PingAsync(pub, sub).ForAwait(); lock (received) { @@ -137,7 +149,7 @@ public async Task TestBasicPubSubFireAndForget() var pub = GetAnyPrimary(conn); var sub = conn.GetSubscriber(); - RedisChannel key = Me() + Guid.NewGuid(); + RedisChannel key = RedisChannel.Literal(Me() + Guid.NewGuid()); HashSet received = new(); int secondHandler = 0; await PingAsync(pub, sub).ForAwait(); @@ -210,7 +222,9 @@ public async Task TestPatternPubSub() HashSet received = new(); int secondHandler = 0; +#pragma warning disable CS0618 sub.Subscribe("a*c", (channel, payload) => +#pragma warning restore CS0618 { lock (received) { @@ -221,7 +235,9 @@ public async Task TestPatternPubSub() } }); +#pragma warning disable CS0618 sub.Subscribe("a*c", (_, __) => Interlocked.Increment(ref secondHandler)); +#pragma warning restore CS0618 lock (received) { Assert.Empty(received); @@ -229,7 +245,7 @@ public async Task TestPatternPubSub() Assert.Equal(0, Thread.VolatileRead(ref secondHandler)); await PingAsync(pub, sub).ForAwait(); - var count = sub.Publish("abc", "def"); + var count = sub.Publish(RedisChannel.Literal("abc"), "def"); await PingAsync(pub, sub).ForAwait(); await UntilConditionAsync(TimeSpan.FromSeconds(5), () => received.Count == 1); @@ -242,8 +258,10 @@ public async Task TestPatternPubSub() await UntilConditionAsync(TimeSpan.FromSeconds(2), () => Thread.VolatileRead(ref secondHandler) == 1); Assert.Equal(1, Thread.VolatileRead(ref secondHandler)); +#pragma warning disable CS0618 sub.Unsubscribe("a*c"); count = sub.Publish("abc", "ghi"); +#pragma warning restore CS0618 await PingAsync(pub, sub).ForAwait(); @@ -259,7 +277,9 @@ public void TestPublishWithNoSubscribers() using var conn = Create(); var sub = conn.GetSubscriber(); +#pragma warning disable CS0618 Assert.Equal(0, sub.Publish(Me() + "channel", "message")); +#pragma warning restore CS0618 } [FactLongRunning] @@ -289,14 +309,18 @@ private void TestMassivePublish(ISubscriber sub, string channel, string caption) var withFAF = Stopwatch.StartNew(); for (int i = 0; i < loop; i++) { +#pragma warning disable CS0618 sub.Publish(channel, "bar", CommandFlags.FireAndForget); +#pragma warning restore CS0618 } withFAF.Stop(); var withAsync = Stopwatch.StartNew(); for (int i = 0; i < loop; i++) { +#pragma warning disable CS0618 tasks[i] = sub.PublishAsync(channel, "bar"); +#pragma warning restore CS0618 } sub.WaitAll(tasks); withAsync.Stop(); @@ -314,7 +338,7 @@ public async Task SubscribeAsyncEnumerable() using var conn = Create(syncTimeout: 20000, shared: false, log: Writer); var sub = conn.GetSubscriber(); - RedisChannel channel = Me(); + RedisChannel channel = RedisChannel.Literal(Me()); const int TO_SEND = 5; var gotall = new TaskCompletionSource(); @@ -348,7 +372,7 @@ public async Task PubSubGetAllAnyOrder() using var sonn = Create(syncTimeout: 20000, shared: false, log: Writer); var sub = sonn.GetSubscriber(); - RedisChannel channel = Me(); + RedisChannel channel = RedisChannel.Literal(Me()); const int count = 1000; var syncLock = new object(); @@ -396,7 +420,7 @@ public async Task PubSubGetAllCorrectOrder() using (var conn = Create(configuration: TestConfig.Current.RemoteServerAndPort, syncTimeout: 20000, log: Writer)) { var sub = conn.GetSubscriber(); - RedisChannel channel = Me(); + RedisChannel channel = RedisChannel.Literal(Me()); const int count = 250; var syncLock = new object(); @@ -469,7 +493,7 @@ public async Task PubSubGetAllCorrectOrder_OnMessage_Sync() using (var conn = Create(configuration: TestConfig.Current.RemoteServerAndPort, syncTimeout: 20000, log: Writer)) { var sub = conn.GetSubscriber(); - RedisChannel channel = Me(); + RedisChannel channel = RedisChannel.Literal(Me()); const int count = 1000; var syncLock = new object(); @@ -538,7 +562,7 @@ public async Task PubSubGetAllCorrectOrder_OnMessage_Async() using (var conn = Create(configuration: TestConfig.Current.RemoteServerAndPort, syncTimeout: 20000, log: Writer)) { var sub = conn.GetSubscriber(); - RedisChannel channel = Me(); + RedisChannel channel = RedisChannel.Literal(Me()); const int count = 1000; var syncLock = new object(); @@ -616,8 +640,10 @@ public async Task TestPublishWithSubscribers() var channel = Me(); var listenA = connA.GetSubscriber(); var listenB = connB.GetSubscriber(); +#pragma warning disable CS0618 var t1 = listenA.SubscribeAsync(channel, delegate { }); var t2 = listenB.SubscribeAsync(channel, delegate { }); +#pragma warning restore CS0618 await Task.WhenAll(t1, t2).ForAwait(); @@ -625,7 +651,9 @@ public async Task TestPublishWithSubscribers() await listenA.PingAsync(); await listenB.PingAsync(); +#pragma warning disable CS0618 var pub = connPub.GetSubscriber().PublishAsync(channel, "message"); +#pragma warning restore CS0618 Assert.Equal(2, await pub); // delivery count } @@ -636,7 +664,7 @@ public async Task TestMultipleSubscribersGetMessage() using var connB = Create(shared: false, log: Writer); using var connPub = Create(); - var channel = Me(); + var channel = RedisChannel.Literal(Me()); var listenA = connA.GetSubscriber(); var listenB = connB.GetSubscriber(); connPub.GetDatabase().Ping(); @@ -668,16 +696,20 @@ public async Task Issue38() int count = 0; var prefix = Me(); void handler(RedisChannel _, RedisValue __) => Interlocked.Increment(ref count); +#pragma warning disable CS0618 var a0 = sub.SubscribeAsync(prefix + "foo", handler); var a1 = sub.SubscribeAsync(prefix + "bar", handler); var b0 = sub.SubscribeAsync(prefix + "f*o", handler); var b1 = sub.SubscribeAsync(prefix + "b*r", handler); +#pragma warning restore CS0618 await Task.WhenAll(a0, a1, b0, b1).ForAwait(); +#pragma warning disable CS0618 var c = sub.PublishAsync(prefix + "foo", "foo"); var d = sub.PublishAsync(prefix + "f@o", "f@o"); var e = sub.PublishAsync(prefix + "bar", "bar"); var f = sub.PublishAsync(prefix + "b@r", "b@r"); +#pragma warning restore CS0618 await Task.WhenAll(c, d, e, f).ForAwait(); long total = c.Result + d.Result + e.Result + f.Result; @@ -702,18 +734,22 @@ public async Task TestPartialSubscriberGetMessage() var listenB = connB.GetSubscriber(); var pub = connPub.GetSubscriber(); var prefix = Me(); +#pragma warning disable CS0618 var tA = listenA.SubscribeAsync(prefix + "channel", (s, msg) => { if (s == prefix + "channel" && msg == "message") Interlocked.Increment(ref gotA); }); var tB = listenB.SubscribeAsync(prefix + "chann*", (s, msg) => { if (s == prefix + "channel" && msg == "message") Interlocked.Increment(ref gotB); }); await Task.WhenAll(tA, tB).ForAwait(); Assert.Equal(2, pub.Publish(prefix + "channel", "message")); +#pragma warning restore CS0618 await AllowReasonableTimeToPublishAndProcess().ForAwait(); Assert.Equal(1, Interlocked.CompareExchange(ref gotA, 0, 0)); Assert.Equal(1, Interlocked.CompareExchange(ref gotB, 0, 0)); // and unsubscibe... +#pragma warning disable CS0618 tB = listenB.UnsubscribeAsync(prefix + "chann*", null); await tB; Assert.Equal(1, pub.Publish(prefix + "channel", "message")); +#pragma warning restore CS0618 await AllowReasonableTimeToPublishAndProcess().ForAwait(); Assert.Equal(2, Interlocked.CompareExchange(ref gotA, 0, 0)); Assert.Equal(1, Interlocked.CompareExchange(ref gotB, 0, 0)); @@ -729,6 +765,7 @@ public async Task TestSubscribeUnsubscribeAndSubscribeAgain() var pub = connPub.GetSubscriber(); var sub = connSub.GetSubscriber(); int x = 0, y = 0; +#pragma warning disable CS0618 var t1 = sub.SubscribeAsync(prefix + "abc", delegate { Interlocked.Increment(ref x); }); var t2 = sub.SubscribeAsync(prefix + "ab*", delegate { Interlocked.Increment(ref y); }); await Task.WhenAll(t1, t2).ForAwait(); @@ -746,6 +783,7 @@ public async Task TestSubscribeUnsubscribeAndSubscribeAgain() t2 = sub.SubscribeAsync(prefix + "ab*", delegate { Interlocked.Increment(ref y); }); await Task.WhenAll(t1, t2).ForAwait(); pub.Publish(prefix + "abc", ""); +#pragma warning restore CS0618 await AllowReasonableTimeToPublishAndProcess().ForAwait(); Assert.Equal(2, Volatile.Read(ref x)); Assert.Equal(2, Volatile.Read(ref y)); @@ -776,7 +814,7 @@ public async Task AzureRedisEventsAutomaticSubscribe() }; var pubSub = connection.GetSubscriber(); - await pubSub.PublishAsync("AzureRedisEvents", "HI"); + await pubSub.PublishAsync(RedisChannel.Literal("AzureRedisEvents"), "HI"); await Task.Delay(100); Assert.True(didUpdate); diff --git a/tests/StackExchange.Redis.Tests/SentinelFailoverTests.cs b/tests/StackExchange.Redis.Tests/SentinelFailoverTests.cs index 0afdf03ec..1e4d8c28e 100644 --- a/tests/StackExchange.Redis.Tests/SentinelFailoverTests.cs +++ b/tests/StackExchange.Redis.Tests/SentinelFailoverTests.cs @@ -21,7 +21,9 @@ public async Task ManagedPrimaryConnectionEndToEndWithFailoverTest() conn.ConfigurationChanged += (s, e) => Log($"Configuration changed: {e.EndPoint}"); var sub = conn.GetSubscriber(); +#pragma warning disable CS0618 sub.Subscribe("*", (channel, message) => Log($"Sub: {channel}, message:{message}")); +#pragma warning restore CS0618 var db = conn.GetDatabase(); await db.PingAsync(); diff --git a/tests/StackExchange.Redis.Tests/TestBase.cs b/tests/StackExchange.Redis.Tests/TestBase.cs index 6738bf490..4ba21d4f5 100644 --- a/tests/StackExchange.Redis.Tests/TestBase.cs +++ b/tests/StackExchange.Redis.Tests/TestBase.cs @@ -377,7 +377,7 @@ public static ConnectionMultiplexer CreateDefault( syncTimeout = int.MaxValue; } - if (channelPrefix != null) config.ChannelPrefix = channelPrefix; + if (channelPrefix != null) config.ChannelPrefix = RedisChannel.Literal(channelPrefix); if (tieBreaker != null) config.TieBreaker = tieBreaker; if (password != null) config.Password = string.IsNullOrEmpty(password) ? null : password; if (clientName != null) config.ClientName = clientName;