|
31 | 31 |
|
32 | 32 | using System;
|
33 | 33 | using System.Collections.Generic;
|
| 34 | +using System.Text; |
34 | 35 | using System.Threading;
|
35 | 36 | using System.Threading.Tasks;
|
36 | 37 | using RabbitMQ.Client.Events;
|
@@ -1053,6 +1054,246 @@ public void TestUnblockedListenersRecovery()
|
1053 | 1054 | Wait(latch);
|
1054 | 1055 | }
|
1055 | 1056 |
|
| 1057 | + [Fact] |
| 1058 | + public void TestTopologyRecoveryQueueFilter() |
| 1059 | + { |
| 1060 | + var filter = new TopologyRecoveryFilter |
| 1061 | + { |
| 1062 | + QueueFilter = queue => !queue.Name.Contains("filtered") |
| 1063 | + }; |
| 1064 | + var latch = new ManualResetEventSlim(false); |
| 1065 | + AutorecoveringConnection conn = CreateAutorecoveringConnectionWithTopologyRecoveryFilter(filter); |
| 1066 | + conn.RecoverySucceeded += (source, ea) => latch.Set(); |
| 1067 | + IModel ch = conn.CreateModel(); |
| 1068 | + |
| 1069 | + var queueToRecover = "recovered.queue"; |
| 1070 | + var queueToIgnore = "filtered.queue"; |
| 1071 | + ch.QueueDeclare(queueToRecover, false, false, false, null); |
| 1072 | + ch.QueueDeclare(queueToIgnore, false, false, false, null); |
| 1073 | + |
| 1074 | + |
| 1075 | + _model.QueueDelete(queueToRecover); |
| 1076 | + _model.QueueDelete(queueToIgnore); |
| 1077 | + |
| 1078 | + CloseAndWaitForRecovery(conn); |
| 1079 | + Wait(latch); |
| 1080 | + |
| 1081 | + Assert.True(ch.IsOpen); |
| 1082 | + AssertQueueRecovery(ch, queueToRecover, false); |
| 1083 | + |
| 1084 | + try |
| 1085 | + { |
| 1086 | + ch.QueueDeclarePassive(queueToIgnore); |
| 1087 | + Assert.Fail("Expected an exception"); |
| 1088 | + } |
| 1089 | + catch (OperationInterruptedException e) |
| 1090 | + { |
| 1091 | + AssertShutdownError(e.ShutdownReason, 404); |
| 1092 | + } |
| 1093 | + } |
| 1094 | + |
| 1095 | + [Fact] |
| 1096 | + public void TestTopologyRecoveryExchangeFilter() |
| 1097 | + { |
| 1098 | + var filter = new TopologyRecoveryFilter |
| 1099 | + { |
| 1100 | + ExchangeFilter = exchange => exchange.Type == "topic" && !exchange.Name.Contains("filtered") |
| 1101 | + }; |
| 1102 | + var latch = new ManualResetEventSlim(false); |
| 1103 | + AutorecoveringConnection conn = CreateAutorecoveringConnectionWithTopologyRecoveryFilter(filter); |
| 1104 | + conn.RecoverySucceeded += (source, ea) => latch.Set(); |
| 1105 | + IModel ch = conn.CreateModel(); |
| 1106 | + |
| 1107 | + var exchangeToRecover = "recovered.exchange"; |
| 1108 | + var exchangeToIgnore = "filtered.exchange"; |
| 1109 | + ch.ExchangeDeclare(exchangeToRecover, "topic", false, true); |
| 1110 | + ch.ExchangeDeclare(exchangeToIgnore, "direct", false, true); |
| 1111 | + |
| 1112 | + _model.ExchangeDelete(exchangeToRecover); |
| 1113 | + _model.ExchangeDelete(exchangeToIgnore); |
| 1114 | + |
| 1115 | + CloseAndWaitForRecovery(conn); |
| 1116 | + Wait(latch); |
| 1117 | + |
| 1118 | + Assert.True(ch.IsOpen); |
| 1119 | + AssertExchangeRecovery(ch, exchangeToRecover); |
| 1120 | + |
| 1121 | + try |
| 1122 | + { |
| 1123 | + ch.ExchangeDeclarePassive(exchangeToIgnore); |
| 1124 | + Assert.Fail("Expected an exception"); |
| 1125 | + } |
| 1126 | + catch (OperationInterruptedException e) |
| 1127 | + { |
| 1128 | + AssertShutdownError(e.ShutdownReason, 404); |
| 1129 | + } |
| 1130 | + } |
| 1131 | + |
| 1132 | + [Fact] |
| 1133 | + public void TestTopologyRecoveryBindingFilter() |
| 1134 | + { |
| 1135 | + var filter = new TopologyRecoveryFilter |
| 1136 | + { |
| 1137 | + BindingFilter = binding => !binding.RoutingKey.Contains("filtered") |
| 1138 | + }; |
| 1139 | + var latch = new ManualResetEventSlim(false); |
| 1140 | + AutorecoveringConnection conn = CreateAutorecoveringConnectionWithTopologyRecoveryFilter(filter); |
| 1141 | + conn.RecoverySucceeded += (source, ea) => latch.Set(); |
| 1142 | + IModel ch = conn.CreateModel(); |
| 1143 | + |
| 1144 | + var exchange = "topology.recovery.exchange"; |
| 1145 | + var queueWithRecoveredBinding = "topology.recovery.queue.1"; |
| 1146 | + var queueWithIgnoredBinding = "topology.recovery.queue.2"; |
| 1147 | + var bindingToRecover = "recovered.binding"; |
| 1148 | + var bindingToIgnore = "filtered.binding"; |
| 1149 | + |
| 1150 | + ch.ExchangeDeclare(exchange, "direct"); |
| 1151 | + ch.QueueDeclare(queueWithRecoveredBinding, false, false, false, null); |
| 1152 | + ch.QueueDeclare(queueWithIgnoredBinding, false, false, false, null); |
| 1153 | + ch.QueueBind(queueWithRecoveredBinding, exchange, bindingToRecover); |
| 1154 | + ch.QueueBind(queueWithIgnoredBinding, exchange, bindingToIgnore); |
| 1155 | + ch.QueuePurge(queueWithRecoveredBinding); |
| 1156 | + ch.QueuePurge(queueWithIgnoredBinding); |
| 1157 | + |
| 1158 | + _model.QueueUnbind(queueWithRecoveredBinding, exchange, bindingToRecover); |
| 1159 | + _model.QueueUnbind(queueWithIgnoredBinding, exchange, bindingToIgnore); |
| 1160 | + |
| 1161 | + CloseAndWaitForRecovery(conn); |
| 1162 | + Wait(latch); |
| 1163 | + |
| 1164 | + Assert.True(ch.IsOpen); |
| 1165 | + Assert.True(SendAndConsumeMessage(queueWithRecoveredBinding, exchange, bindingToRecover)); |
| 1166 | + Assert.False(SendAndConsumeMessage(queueWithIgnoredBinding, exchange, bindingToIgnore)); |
| 1167 | + } |
| 1168 | + |
| 1169 | + [Fact] |
| 1170 | + public void TestTopologyRecoveryConsumerFilter() |
| 1171 | + { |
| 1172 | + var filter = new TopologyRecoveryFilter |
| 1173 | + { |
| 1174 | + ConsumerFilter = consumer => !consumer.ConsumerTag.Contains("filtered") |
| 1175 | + }; |
| 1176 | + var latch = new ManualResetEventSlim(false); |
| 1177 | + AutorecoveringConnection conn = CreateAutorecoveringConnectionWithTopologyRecoveryFilter(filter); |
| 1178 | + conn.RecoverySucceeded += (source, ea) => latch.Set(); |
| 1179 | + IModel ch = conn.CreateModel(); |
| 1180 | + ch.ConfirmSelect(); |
| 1181 | + |
| 1182 | + var exchange = "topology.recovery.exchange"; |
| 1183 | + var queueWithRecoveredConsumer = "topology.recovery.queue.1"; |
| 1184 | + var queueWithIgnoredConsumer = "topology.recovery.queue.2"; |
| 1185 | + var binding1 = "recovered.binding"; |
| 1186 | + var binding2 = "filtered.binding"; |
| 1187 | + |
| 1188 | + ch.ExchangeDeclare(exchange, "direct"); |
| 1189 | + ch.QueueDeclare(queueWithRecoveredConsumer, false, false, false, null); |
| 1190 | + ch.QueueDeclare(queueWithIgnoredConsumer, false, false, false, null); |
| 1191 | + ch.QueueBind(queueWithRecoveredConsumer, exchange, binding1); |
| 1192 | + ch.QueueBind(queueWithIgnoredConsumer, exchange, binding2); |
| 1193 | + ch.QueuePurge(queueWithRecoveredConsumer); |
| 1194 | + ch.QueuePurge(queueWithIgnoredConsumer); |
| 1195 | + |
| 1196 | + var recoverLatch = new ManualResetEventSlim(false); |
| 1197 | + var consumerToRecover = new EventingBasicConsumer(ch); |
| 1198 | + consumerToRecover.Received += (source, ea) => recoverLatch.Set(); |
| 1199 | + ch.BasicConsume(queueWithRecoveredConsumer, true, "recovered.consumer", consumerToRecover); |
| 1200 | + |
| 1201 | + var ignoredLatch = new ManualResetEventSlim(false); |
| 1202 | + var consumerToIgnore = new EventingBasicConsumer(ch); |
| 1203 | + consumerToIgnore.Received += (source, ea) => ignoredLatch.Set(); |
| 1204 | + ch.BasicConsume(queueWithIgnoredConsumer, true, "filtered.consumer", consumerToIgnore); |
| 1205 | + |
| 1206 | + CloseAndWaitForRecovery(conn); |
| 1207 | + Wait(latch); |
| 1208 | + |
| 1209 | + Assert.True(ch.IsOpen); |
| 1210 | + ch.BasicPublish(exchange, binding1, Encoding.UTF8.GetBytes("test message")); |
| 1211 | + ch.BasicPublish(exchange, binding2, Encoding.UTF8.GetBytes("test message")); |
| 1212 | + |
| 1213 | + Assert.True(recoverLatch.Wait(TimeSpan.FromSeconds(5))); |
| 1214 | + Assert.False(ignoredLatch.Wait(TimeSpan.FromSeconds(5))); |
| 1215 | + |
| 1216 | + ch.BasicConsume(queueWithIgnoredConsumer, true, "filtered.consumer", consumerToIgnore); |
| 1217 | + |
| 1218 | + try |
| 1219 | + { |
| 1220 | + ch.BasicConsume(queueWithRecoveredConsumer, true, "recovered.consumer", consumerToRecover); |
| 1221 | + Assert.Fail("Expected an exception"); |
| 1222 | + } |
| 1223 | + catch (OperationInterruptedException e) |
| 1224 | + { |
| 1225 | + AssertShutdownError(e.ShutdownReason, 530); // NOT_ALLOWED - not allowed to reuse consumer tag |
| 1226 | + } |
| 1227 | + } |
| 1228 | + |
| 1229 | + [Fact] |
| 1230 | + public void TestTopologyRecoveryDefaultFilterRecoversAllEntities() |
| 1231 | + { |
| 1232 | + var filter = new TopologyRecoveryFilter(); |
| 1233 | + var latch = new ManualResetEventSlim(false); |
| 1234 | + AutorecoveringConnection conn = CreateAutorecoveringConnectionWithTopologyRecoveryFilter(filter); |
| 1235 | + conn.RecoverySucceeded += (source, ea) => latch.Set(); |
| 1236 | + IModel ch = conn.CreateModel(); |
| 1237 | + |
| 1238 | + var exchange = "topology.recovery.exchange"; |
| 1239 | + var queue1 = "topology.recovery.queue.1"; |
| 1240 | + var queue2 = "topology.recovery.queue.2"; |
| 1241 | + var binding1 = "recovered.binding"; |
| 1242 | + var binding2 = "filtered.binding"; |
| 1243 | + |
| 1244 | + ch.ExchangeDeclare(exchange, "direct"); |
| 1245 | + ch.QueueDeclare(queue1, false, false, false, null); |
| 1246 | + ch.QueueDeclare(queue2, false, false, false, null); |
| 1247 | + ch.QueueBind(queue1, exchange, binding1); |
| 1248 | + ch.QueueBind(queue2, exchange, binding2); |
| 1249 | + ch.QueuePurge(queue1); |
| 1250 | + ch.QueuePurge(queue2); |
| 1251 | + |
| 1252 | + var consumerLatch1 = new ManualResetEventSlim(false); |
| 1253 | + var consumer1 = new EventingBasicConsumer(ch); |
| 1254 | + consumer1.Received += (source, ea) => consumerLatch1.Set(); |
| 1255 | + ch.BasicConsume(queue1, true, "recovered.consumer", consumer1); |
| 1256 | + |
| 1257 | + var consumerLatch2 = new ManualResetEventSlim(false); |
| 1258 | + var consumer2 = new EventingBasicConsumer(ch); |
| 1259 | + consumer2.Received += (source, ea) => consumerLatch2.Set(); |
| 1260 | + ch.BasicConsume(queue2, true, "filtered.consumer", consumer2); |
| 1261 | + |
| 1262 | + _model.ExchangeDelete(exchange); |
| 1263 | + _model.QueueDelete(queue1); |
| 1264 | + _model.QueueDelete(queue2); |
| 1265 | + |
| 1266 | + CloseAndWaitForRecovery(conn); |
| 1267 | + Wait(latch); |
| 1268 | + |
| 1269 | + Assert.True(ch.IsOpen); |
| 1270 | + AssertExchangeRecovery(ch, exchange); |
| 1271 | + ch.QueueDeclarePassive(queue1); |
| 1272 | + ch.QueueDeclarePassive(queue2); |
| 1273 | + |
| 1274 | + ch.BasicPublish(exchange, binding1, Encoding.UTF8.GetBytes("test message")); |
| 1275 | + ch.BasicPublish(exchange, binding2, Encoding.UTF8.GetBytes("test message")); |
| 1276 | + |
| 1277 | + Assert.True(consumerLatch1.Wait(TimeSpan.FromSeconds(5))); |
| 1278 | + Assert.True(consumerLatch2.Wait(TimeSpan.FromSeconds(5))); |
| 1279 | + } |
| 1280 | + |
| 1281 | + internal bool SendAndConsumeMessage(string queue, string exchange, string routingKey) |
| 1282 | + { |
| 1283 | + using (var ch = _conn.CreateModel()) |
| 1284 | + { |
| 1285 | + var latch = new ManualResetEventSlim(false); |
| 1286 | + |
| 1287 | + var consumer = new AckingBasicConsumer(ch, 1, latch); |
| 1288 | + |
| 1289 | + ch.BasicConsume(queue, true, consumer); |
| 1290 | + |
| 1291 | + ch.BasicPublish(exchange, routingKey, Encoding.UTF8.GetBytes("test message")); |
| 1292 | + |
| 1293 | + return latch.Wait(TimeSpan.FromSeconds(5)); |
| 1294 | + } |
| 1295 | + } |
| 1296 | + |
1056 | 1297 | internal void AssertExchangeRecovery(IChannel m, string x)
|
1057 | 1298 | {
|
1058 | 1299 | m.ConfirmSelect();
|
|
0 commit comments