@@ -110,6 +110,7 @@ public async Task PubSubRouted()
110110 EndPoint tokyo = new DnsEndPoint ( "tokyo" , 6379 ) ;
111111
112112 using var server0 = new InProcessTestServer ( endpoint : germany ) ;
113+ Assert . SkipUnless ( server0 . GetClientConfig ( ) . CommandMap . IsAvailable ( RedisCommand . PUBLISH ) , "PUBLISH is not available" ) ;
113114 using var server1 = new InProcessTestServer ( endpoint : canada ) ;
114115 using var server2 = new InProcessTestServer ( endpoint : tokyo ) ;
115116
@@ -194,4 +195,110 @@ void Reset()
194195 Assert . Contains ( "[conn] chan: jkl" , seen ) ; // receives the message from itself
195196 Assert . Contains ( "[pub2] chan: jkl" , seen ) ; // received the message from the multi-group
196197 }
198+
199+ [ Fact ]
200+ public async Task PubSubOrderedRouted ( )
201+ {
202+ EndPoint germany = new DnsEndPoint ( "germany" , 6379 ) ;
203+ EndPoint canada = new DnsEndPoint ( "canada" , 6379 ) ;
204+ EndPoint tokyo = new DnsEndPoint ( "tokyo" , 6379 ) ;
205+
206+ using var server0 = new InProcessTestServer ( endpoint : germany ) ;
207+ Assert . SkipUnless ( server0 . GetClientConfig ( ) . CommandMap . IsAvailable ( RedisCommand . PUBLISH ) , "PUBLISH is not available" ) ;
208+ using var server1 = new InProcessTestServer ( endpoint : canada ) ;
209+ using var server2 = new InProcessTestServer ( endpoint : tokyo ) ;
210+
211+ HashSet < string > seen = [ ] ;
212+
213+ void Seen ( string source , RedisChannel channel , RedisValue value )
214+ {
215+ string message = $ "[{ source } ] { channel } : { value } ";
216+ lock ( seen )
217+ {
218+ seen . Add ( message ) ;
219+ }
220+ }
221+
222+ void Reset ( )
223+ {
224+ lock ( seen )
225+ {
226+ seen . Clear ( ) ;
227+ }
228+ }
229+
230+ void WriteSeen ( string source , ChannelMessageQueue queue )
231+ {
232+ _ = Task . Run ( async ( ) =>
233+ {
234+ await foreach ( var msg in queue )
235+ {
236+ Seen ( source , msg . Channel , msg . Message ) ;
237+ }
238+ } ) ;
239+ }
240+
241+ RedisChannel channel = RedisChannel . Literal ( "chan" ) ;
242+ var pub0 = ( await server0 . ConnectAsync ( ) ) . GetSubscriber ( ) ;
243+ WriteSeen ( nameof ( pub0 ) , await pub0 . SubscribeAsync ( channel ) ) ;
244+ var pub1 = ( await server1 . ConnectAsync ( ) ) . GetSubscriber ( ) ;
245+ WriteSeen ( nameof ( pub1 ) , await pub1 . SubscribeAsync ( channel ) ) ;
246+ var pub2 = ( await server2 . ConnectAsync ( ) ) . GetSubscriber ( ) ;
247+ WriteSeen ( nameof ( pub2 ) , await pub2 . SubscribeAsync ( channel ) ) ;
248+
249+ ConnectionGroupMember [ ] members = [
250+ new ( server0 . GetClientConfig ( ) ) { Weight = 2 } ,
251+ new ( server1 . GetClientConfig ( ) ) { Weight = 9 } ,
252+ new ( server2 . GetClientConfig ( ) ) { Weight = 3 } ,
253+ ] ;
254+ await using var conn = await ConnectionMultiplexer . ConnectGroupAsync ( members ) ;
255+ Assert . True ( conn . IsConnected ) ;
256+ var typed = Assert . IsType < MultiGroupMultiplexer > ( conn ) ;
257+ var multi = conn . GetSubscriber ( ) ;
258+ WriteSeen ( nameof ( conn ) , await multi . SubscribeAsync ( channel ) ) ;
259+
260+ // (R.4.1) If multiple member databases are configured, then I want to failover to the one with the highest weight.
261+ var db = conn . GetDatabase ( ) ;
262+ var ep = await db . IdentifyEndpointAsync ( ) ;
263+ Assert . Equal ( canada , ep ) ;
264+
265+ // now publish via all 4 options, see what happens
266+ Reset ( ) ;
267+ await pub0 . PublishAsync ( channel , "abc" ) ;
268+ await pub1 . PublishAsync ( channel , "def" ) ;
269+ await pub2 . PublishAsync ( channel , "ghi" ) ;
270+ await multi . PublishAsync ( channel , "jkl" ) ;
271+ await multi . PingAsync ( ) ;
272+
273+ // we're expecting just canada, so:
274+ Assert . Equal ( 6 , seen . Count ) ;
275+ Assert . Contains ( "[pub0] chan: abc" , seen ) ;
276+ Assert . Contains ( "[pub1] chan: def" , seen ) ;
277+ Assert . Contains ( "[pub2] chan: ghi" , seen ) ;
278+ Assert . Contains ( "[conn] chan: def" , seen ) ; // receives the message from pub1
279+ Assert . Contains ( "[conn] chan: jkl" , seen ) ; // receives the message from itself
280+ Assert . Contains ( "[pub1] chan: jkl" , seen ) ; // received the message from the multi-group
281+
282+ // change weight and update
283+ members [ 1 ] . Weight = 1 ;
284+ typed . SelectPreferredGroup ( ) ;
285+ ep = await db . IdentifyEndpointAsync ( ) ;
286+ Assert . Equal ( tokyo , ep ) ;
287+
288+ Reset ( ) ;
289+ await pub0 . PublishAsync ( channel , "abc" ) ;
290+ await pub1 . PublishAsync ( channel , "def" ) ;
291+ await pub2 . PublishAsync ( channel , "ghi" ) ;
292+ await multi . PublishAsync ( channel , "jkl" ) ;
293+ await multi . PingAsync ( ) ;
294+
295+ // now we're expecting just tokyo, so:
296+ Assert . Equal ( 6 , seen . Count ) ;
297+ Assert . Contains ( "[pub0] chan: abc" , seen ) ;
298+ Assert . Contains ( "[pub1] chan: def" , seen ) ;
299+ Assert . Contains ( "[pub2] chan: ghi" , seen ) ;
300+ Assert . Contains ( "[conn] chan: jkl" , seen ) ; // receives the message from pub2
301+ Assert . Contains ( "[conn] chan: jkl" , seen ) ; // receives the message from itself
302+ Assert . Contains ( "[pub2] chan: jkl" , seen ) ; // received the message from the multi-group
303+ }
197304}
0 commit comments