@@ -4020,6 +4020,126 @@ mod cluster_async {
40204020 . unwrap ( ) ;
40214021 }
40224022
4023+ #[ test]
4024+ fn test_async_cluster_dont_route_to_a_random_on_non_key_based_cmd ( ) {
4025+ // This test verifies that non-key-based commands do not get routed to a random node
4026+ // when no connection is found for the given route. Instead, the appropriate error
4027+ // should be raised.
4028+ let name = "test_async_cluster_dont_route_to_a_random_on_non_key_based_cmd" ;
4029+ let request_counter = Arc :: new ( AtomicU32 :: new ( 0 ) ) ;
4030+ let cloned_req_counter = request_counter. clone ( ) ;
4031+ let MockEnv {
4032+ runtime,
4033+ async_connection : mut connection,
4034+ handler : _handler,
4035+ ..
4036+ } = MockEnv :: with_client_builder (
4037+ ClusterClient :: builder ( vec ! [ & * format!( "redis://{name}" ) ] ) ,
4038+ name,
4039+ move |received_cmd : & [ u8 ] , _| {
4040+ let slots_config_vec = vec ! [
4041+ MockSlotRange {
4042+ primary_port: 6379 ,
4043+ replica_ports: vec![ ] ,
4044+ slot_range: ( 0_u16 ..8000_u16 ) ,
4045+ } ,
4046+ MockSlotRange {
4047+ primary_port: 6380 ,
4048+ replica_ports: vec![ ] ,
4049+ // Don't cover all slots
4050+ slot_range: ( 8001_u16 ..12000_u16 ) ,
4051+ } ,
4052+ ] ;
4053+ respond_startup_with_config ( name, received_cmd, Some ( slots_config_vec) , false ) ?;
4054+ // If requests are sent to random nodes, they will be caught and counted here.
4055+ request_counter. fetch_add ( 1 , Ordering :: Relaxed ) ;
4056+ Err ( Ok ( Value :: Nil ) )
4057+ } ,
4058+ ) ;
4059+
4060+ runtime
4061+ . block_on ( async move {
4062+ let uncovered_slot = 16000 ;
4063+ let route = redis:: cluster_routing:: Route :: new (
4064+ uncovered_slot,
4065+ redis:: cluster_routing:: SlotAddr :: Master ,
4066+ ) ;
4067+ let single_node_route =
4068+ redis:: cluster_routing:: SingleNodeRoutingInfo :: SpecificNode ( route) ;
4069+ let routing = RoutingInfo :: SingleNode ( single_node_route) ;
4070+ let res = connection
4071+ . route_command ( & redis:: cmd ( "FLUSHALL" ) , routing)
4072+ . await ;
4073+ assert ! ( res. is_err( ) ) ;
4074+ let res_err = res. unwrap_err ( ) ;
4075+ assert_eq ! (
4076+ res_err. kind( ) ,
4077+ ErrorKind :: ClusterConnectionNotFound ,
4078+ "{:?}" ,
4079+ res_err
4080+ ) ;
4081+ assert_eq ! ( cloned_req_counter. load( Ordering :: Relaxed ) , 0 ) ;
4082+ Ok :: < _ , RedisError > ( ( ) )
4083+ } )
4084+ . unwrap ( ) ;
4085+ }
4086+
4087+ #[ test]
4088+ fn test_async_cluster_route_to_random_on_key_based_cmd ( ) {
4089+ // This test verifies that key-based commands get routed to a random node
4090+ // when no connection is found for the given route. The command should
4091+ // then be redirected correctly by the server's MOVED error.
4092+ let name = "test_async_cluster_route_to_random_on_key_based_cmd" ;
4093+ let request_counter = Arc :: new ( AtomicU32 :: new ( 0 ) ) ;
4094+ let cloned_req_counter = request_counter. clone ( ) ;
4095+ let MockEnv {
4096+ runtime,
4097+ async_connection : mut connection,
4098+ handler : _handler,
4099+ ..
4100+ } = MockEnv :: with_client_builder (
4101+ ClusterClient :: builder ( vec ! [ & * format!( "redis://{name}" ) ] ) ,
4102+ name,
4103+ move |received_cmd : & [ u8 ] , _| {
4104+ let slots_config_vec = vec ! [
4105+ MockSlotRange {
4106+ primary_port: 6379 ,
4107+ replica_ports: vec![ ] ,
4108+ slot_range: ( 0_u16 ..8000_u16 ) ,
4109+ } ,
4110+ MockSlotRange {
4111+ primary_port: 6380 ,
4112+ replica_ports: vec![ ] ,
4113+ // Don't cover all slots
4114+ slot_range: ( 8001_u16 ..12000_u16 ) ,
4115+ } ,
4116+ ] ;
4117+ respond_startup_with_config ( name, received_cmd, Some ( slots_config_vec) , false ) ?;
4118+ if contains_slice ( received_cmd, b"GET" ) {
4119+ if request_counter. fetch_add ( 1 , Ordering :: Relaxed ) == 0 {
4120+ return Err ( parse_redis_value (
4121+ format ! ( "-MOVED 12182 {name}:6380\r \n " ) . as_bytes ( ) ,
4122+ ) ) ;
4123+ } else {
4124+ return Err ( Ok ( Value :: SimpleString ( "bar" . into ( ) ) ) ) ;
4125+ }
4126+ }
4127+ panic ! ( "unexpected command {:?}" , received_cmd) ;
4128+ } ,
4129+ ) ;
4130+
4131+ runtime
4132+ . block_on ( async move {
4133+ // The keyslot of "foo" is 12182 and it isn't covered by any node, so we expect the
4134+ // request to be routed to a random node and then to be redirected to the MOVED node (2 requests in total)
4135+ let res: String = connection. get ( "foo" ) . await . unwrap ( ) ;
4136+ assert_eq ! ( res, "bar" . to_string( ) ) ;
4137+ assert_eq ! ( cloned_req_counter. load( Ordering :: Relaxed ) , 2 ) ;
4138+ Ok :: < _ , RedisError > ( ( ) )
4139+ } )
4140+ . unwrap ( ) ;
4141+ }
4142+
40234143 #[ cfg( feature = "tls-rustls" ) ]
40244144 mod mtls_test {
40254145 use crate :: support:: mtls_test:: create_cluster_client_from_cluster;
0 commit comments