|
30 | 30 | //---------------------------------------------------------------------------
|
31 | 31 |
|
32 | 32 | using System;
|
33 |
| -using System.Collections; |
34 | 33 | using System.Collections.Generic;
|
| 34 | +using System.ServiceModel.Channels; |
35 | 35 | using System.Text;
|
36 | 36 | using System.Threading;
|
37 | 37 |
|
@@ -1228,8 +1228,8 @@ public void TestTopologyRecoveryConsumerFilter()
|
1228 | 1228 | var exchange = "topology.recovery.exchange";
|
1229 | 1229 | var queueWithRecoveredConsumer = "topology.recovery.queue.1";
|
1230 | 1230 | var queueWithIgnoredConsumer = "topology.recovery.queue.2";
|
1231 |
| - var binding1 = "recovered.binding"; |
1232 |
| - var binding2 = "filtered.binding"; |
| 1231 | + var binding1 = "recovered.binding.1"; |
| 1232 | + var binding2 = "recovered.binding.2"; |
1233 | 1233 |
|
1234 | 1234 | ch.ExchangeDeclare(exchange, "direct");
|
1235 | 1235 | ch.QueueDeclare(queueWithRecoveredConsumer, false, false, false, null);
|
@@ -1325,6 +1325,209 @@ public void TestTopologyRecoveryDefaultFilterRecoversAllEntities()
|
1325 | 1325 | Assert.IsTrue(consumerLatch2.Wait(TimeSpan.FromSeconds(5)));
|
1326 | 1326 | }
|
1327 | 1327 |
|
| 1328 | + [Test] |
| 1329 | + public void TestTopologyRecoveryQueueExceptionHandler() |
| 1330 | + { |
| 1331 | + var changedQueueArguments = new Dictionary<string, object> |
| 1332 | + { |
| 1333 | + { Headers.XMaxPriority, 20 } |
| 1334 | + }; |
| 1335 | + var exceptionHandler = new TopologyRecoveryExceptionHandler |
| 1336 | + { |
| 1337 | + QueueRecoveryExceptionCondition = (rq, ex) => |
| 1338 | + { |
| 1339 | + return rq.Name.Contains("exception") |
| 1340 | + && ex is OperationInterruptedException operationInterruptedException |
| 1341 | + && operationInterruptedException.ShutdownReason.ReplyCode == Constants.PreconditionFailed; |
| 1342 | + }, |
| 1343 | + QueueRecoveryExceptionHandler = (rq, ex, connection) => |
| 1344 | + { |
| 1345 | + using (var model = connection.CreateModel()) |
| 1346 | + { |
| 1347 | + model.QueueDeclare(rq.Name, false, false, false, changedQueueArguments); |
| 1348 | + } |
| 1349 | + } |
| 1350 | + }; |
| 1351 | + var latch = new ManualResetEventSlim(false); |
| 1352 | + AutorecoveringConnection conn = CreateAutorecoveringConnectionWithTopologyRecoveryExceptionHandler(exceptionHandler); |
| 1353 | + conn.RecoverySucceeded += (source, ea) => latch.Set(); |
| 1354 | + IModel ch = conn.CreateModel(); |
| 1355 | + |
| 1356 | + var queueToRecoverWithException = "recovery.exception.queue"; |
| 1357 | + var queueToRecoverSuccessfully = "successfully.recovered.queue"; |
| 1358 | + ch.QueueDeclare(queueToRecoverWithException, false, false, false, null); |
| 1359 | + ch.QueueDeclare(queueToRecoverSuccessfully, false, false, false, null); |
| 1360 | + |
| 1361 | + Model.QueueDelete(queueToRecoverSuccessfully); |
| 1362 | + Model.QueueDelete(queueToRecoverWithException); |
| 1363 | + Model.QueueDeclare(queueToRecoverWithException, false, false, false, changedQueueArguments); |
| 1364 | + |
| 1365 | + CloseAndWaitForRecovery(conn); |
| 1366 | + Wait(latch); |
| 1367 | + |
| 1368 | + Assert.IsTrue(ch.IsOpen); |
| 1369 | + AssertQueueRecovery(ch, queueToRecoverSuccessfully, false); |
| 1370 | + AssertQueueRecovery(ch, queueToRecoverWithException, false, changedQueueArguments); |
| 1371 | + |
| 1372 | + //Cleanup |
| 1373 | + Model.QueueDelete(queueToRecoverWithException); |
| 1374 | + } |
| 1375 | + |
| 1376 | + [Test] |
| 1377 | + public void TestTopologyRecoveryExchangeExceptionHandler() |
| 1378 | + { |
| 1379 | + var exceptionHandler = new TopologyRecoveryExceptionHandler |
| 1380 | + { |
| 1381 | + ExchangeRecoveryExceptionCondition = (re, ex) => |
| 1382 | + { |
| 1383 | + return re.Name.Contains("exception") |
| 1384 | + && ex is OperationInterruptedException operationInterruptedException |
| 1385 | + && operationInterruptedException.ShutdownReason.ReplyCode == Constants.PreconditionFailed; |
| 1386 | + }, |
| 1387 | + ExchangeRecoveryExceptionHandler = (re, ex, connection) => |
| 1388 | + { |
| 1389 | + using (var model = connection.CreateModel()) |
| 1390 | + { |
| 1391 | + model.ExchangeDeclare(re.Name, "topic", false, false); |
| 1392 | + } |
| 1393 | + } |
| 1394 | + }; |
| 1395 | + var latch = new ManualResetEventSlim(false); |
| 1396 | + AutorecoveringConnection conn = CreateAutorecoveringConnectionWithTopologyRecoveryExceptionHandler(exceptionHandler); |
| 1397 | + conn.RecoverySucceeded += (source, ea) => latch.Set(); |
| 1398 | + IModel ch = conn.CreateModel(); |
| 1399 | + |
| 1400 | + var exchangeToRecoverWithException = "recovery.exception.exchange"; |
| 1401 | + var exchangeToRecoverSuccessfully = "successfully.recovered.exchange"; |
| 1402 | + ch.ExchangeDeclare(exchangeToRecoverWithException, "direct", false, false); |
| 1403 | + ch.ExchangeDeclare(exchangeToRecoverSuccessfully, "direct", false, false); |
| 1404 | + |
| 1405 | + Model.ExchangeDelete(exchangeToRecoverSuccessfully); |
| 1406 | + Model.ExchangeDelete(exchangeToRecoverWithException); |
| 1407 | + Model.ExchangeDeclare(exchangeToRecoverWithException, "topic", false, false); |
| 1408 | + |
| 1409 | + CloseAndWaitForRecovery(conn); |
| 1410 | + Wait(latch); |
| 1411 | + |
| 1412 | + Assert.IsTrue(ch.IsOpen); |
| 1413 | + AssertExchangeRecovery(ch, exchangeToRecoverSuccessfully); |
| 1414 | + AssertExchangeRecovery(ch, exchangeToRecoverWithException); |
| 1415 | + |
| 1416 | + //Cleanup |
| 1417 | + Model.ExchangeDelete(exchangeToRecoverWithException); |
| 1418 | + } |
| 1419 | + |
| 1420 | + [Test] |
| 1421 | + public void TestTopologyRecoveryBindingExceptionHandler() |
| 1422 | + { |
| 1423 | + var exchange = "topology.recovery.exchange"; |
| 1424 | + var queueWithExceptionBinding = "recovery.exception.queue"; |
| 1425 | + var bindingToRecoverWithException = "recovery.exception.binding"; |
| 1426 | + |
| 1427 | + var exceptionHandler = new TopologyRecoveryExceptionHandler |
| 1428 | + { |
| 1429 | + BindingRecoveryExceptionCondition = (b, ex) => |
| 1430 | + { |
| 1431 | + return b.RoutingKey.Contains("exception") |
| 1432 | + && ex is OperationInterruptedException operationInterruptedException |
| 1433 | + && operationInterruptedException.ShutdownReason.ReplyCode == Constants.NotFound; |
| 1434 | + }, |
| 1435 | + BindingRecoveryExceptionHandler = (b, ex, connection) => |
| 1436 | + { |
| 1437 | + using (var model = connection.CreateModel()) |
| 1438 | + { |
| 1439 | + model.QueueDeclare(queueWithExceptionBinding, false, false, false, null); |
| 1440 | + model.QueueBind(queueWithExceptionBinding, exchange, bindingToRecoverWithException); |
| 1441 | + } |
| 1442 | + } |
| 1443 | + }; |
| 1444 | + var latch = new ManualResetEventSlim(false); |
| 1445 | + AutorecoveringConnection conn = CreateAutorecoveringConnectionWithTopologyRecoveryExceptionHandler(exceptionHandler); |
| 1446 | + conn.RecoverySucceeded += (source, ea) => latch.Set(); |
| 1447 | + IModel ch = conn.CreateModel(); |
| 1448 | + |
| 1449 | + var queueWithRecoveredBinding = "successfully.recovered.queue"; |
| 1450 | + var bindingToRecoverSuccessfully = "successfully.recovered.binding"; |
| 1451 | + |
| 1452 | + Model.QueueDeclare(queueWithExceptionBinding, false, false, false, null); |
| 1453 | + |
| 1454 | + ch.ExchangeDeclare(exchange, "direct"); |
| 1455 | + ch.QueueDeclare(queueWithRecoveredBinding, false, false, false, null); |
| 1456 | + ch.QueueBind(queueWithRecoveredBinding, exchange, bindingToRecoverSuccessfully); |
| 1457 | + ch.QueueBind(queueWithExceptionBinding, exchange, bindingToRecoverWithException); |
| 1458 | + ch.QueuePurge(queueWithRecoveredBinding); |
| 1459 | + ch.QueuePurge(queueWithExceptionBinding); |
| 1460 | + |
| 1461 | + Model.QueueUnbind(queueWithRecoveredBinding, exchange, bindingToRecoverSuccessfully); |
| 1462 | + Model.QueueUnbind(queueWithExceptionBinding, exchange, bindingToRecoverWithException); |
| 1463 | + Model.QueueDelete(queueWithExceptionBinding); |
| 1464 | + |
| 1465 | + CloseAndWaitForRecovery(conn); |
| 1466 | + Wait(latch); |
| 1467 | + |
| 1468 | + Assert.IsTrue(ch.IsOpen); |
| 1469 | + Assert.IsTrue(SendAndConsumeMessage(queueWithRecoveredBinding, exchange, bindingToRecoverSuccessfully)); |
| 1470 | + Assert.IsFalse(SendAndConsumeMessage(queueWithExceptionBinding, exchange, bindingToRecoverWithException)); |
| 1471 | + } |
| 1472 | + |
| 1473 | + [Test] |
| 1474 | + public void TestTopologyRecoveryConsumerExceptionHandler() |
| 1475 | + { |
| 1476 | + var queueWithExceptionConsumer = "recovery.exception.queue"; |
| 1477 | + |
| 1478 | + var exceptionHandler = new TopologyRecoveryExceptionHandler |
| 1479 | + { |
| 1480 | + ConsumerRecoveryExceptionCondition = (c, ex) => |
| 1481 | + { |
| 1482 | + return c.ConsumerTag.Contains("exception") |
| 1483 | + && ex is OperationInterruptedException operationInterruptedException |
| 1484 | + && operationInterruptedException.ShutdownReason.ReplyCode == Constants.NotFound; |
| 1485 | + }, |
| 1486 | + ConsumerRecoveryExceptionHandler = (c, ex, connection) => |
| 1487 | + { |
| 1488 | + using (var model = connection.CreateModel()) |
| 1489 | + { |
| 1490 | + model.QueueDeclare(queueWithExceptionConsumer, false, false, false, null); |
| 1491 | + model.BasicConsume(queueWithExceptionConsumer, true, c.ConsumerTag, c.Consumer); |
| 1492 | + } |
| 1493 | + } |
| 1494 | + }; |
| 1495 | + var latch = new ManualResetEventSlim(false); |
| 1496 | + AutorecoveringConnection conn = CreateAutorecoveringConnectionWithTopologyRecoveryExceptionHandler(exceptionHandler); |
| 1497 | + conn.RecoverySucceeded += (source, ea) => latch.Set(); |
| 1498 | + IModel ch = conn.CreateModel(); |
| 1499 | + ch.ConfirmSelect(); |
| 1500 | + |
| 1501 | + Model.QueueDeclare(queueWithExceptionConsumer, false, false, false, null); |
| 1502 | + Model.QueuePurge(queueWithExceptionConsumer); |
| 1503 | + |
| 1504 | + var recoverLatch = new ManualResetEventSlim(false); |
| 1505 | + var consumerToRecover = new EventingBasicConsumer(ch); |
| 1506 | + consumerToRecover.Received += (source, ea) => recoverLatch.Set(); |
| 1507 | + ch.BasicConsume(queueWithExceptionConsumer, true, "exception.consumer", consumerToRecover); |
| 1508 | + |
| 1509 | + Model.QueueDelete(queueWithExceptionConsumer); |
| 1510 | + |
| 1511 | + CloseAndWaitForRecovery(conn); |
| 1512 | + Wait(latch); |
| 1513 | + |
| 1514 | + Assert.IsTrue(ch.IsOpen); |
| 1515 | + |
| 1516 | + ch.BasicPublish("", queueWithExceptionConsumer, ch.CreateBasicProperties(), Encoding.UTF8.GetBytes("test message")); |
| 1517 | + |
| 1518 | + Assert.IsTrue(recoverLatch.Wait(TimeSpan.FromSeconds(5))); |
| 1519 | + |
| 1520 | + try |
| 1521 | + { |
| 1522 | + ch.BasicConsume(queueWithExceptionConsumer, true, "exception.consumer", consumerToRecover); |
| 1523 | + Assert.Fail("Expected an exception"); |
| 1524 | + } |
| 1525 | + catch (OperationInterruptedException e) |
| 1526 | + { |
| 1527 | + AssertShutdownError(e.ShutdownReason, 530); // NOT_ALLOWED - not allowed to reuse consumer tag |
| 1528 | + } |
| 1529 | + } |
| 1530 | + |
1328 | 1531 | internal bool SendAndConsumeMessage(string queue, string exchange, string routingKey)
|
1329 | 1532 | {
|
1330 | 1533 | using (var ch = Conn.CreateModel())
|
@@ -1362,15 +1565,15 @@ internal void AssertQueueRecovery(IModel m, string q)
|
1362 | 1565 | AssertQueueRecovery(m, q, true);
|
1363 | 1566 | }
|
1364 | 1567 |
|
1365 |
| - internal void AssertQueueRecovery(IModel m, string q, bool exclusive) |
| 1568 | + internal void AssertQueueRecovery(IModel m, string q, bool exclusive, IDictionary<string, object> arguments = null) |
1366 | 1569 | {
|
1367 | 1570 | m.ConfirmSelect();
|
1368 | 1571 | m.QueueDeclarePassive(q);
|
1369 |
| - QueueDeclareOk ok1 = m.QueueDeclare(q, false, exclusive, false, null); |
| 1572 | + QueueDeclareOk ok1 = m.QueueDeclare(q, false, exclusive, false, arguments); |
1370 | 1573 | Assert.AreEqual(ok1.MessageCount, 0);
|
1371 | 1574 | m.BasicPublish("", q, null, _messageBody);
|
1372 | 1575 | Assert.IsTrue(WaitForConfirms(m));
|
1373 |
| - QueueDeclareOk ok2 = m.QueueDeclare(q, false, exclusive, false, null); |
| 1576 | + QueueDeclareOk ok2 = m.QueueDeclare(q, false, exclusive, false, arguments); |
1374 | 1577 | Assert.AreEqual(ok2.MessageCount, 1);
|
1375 | 1578 | }
|
1376 | 1579 |
|
|
0 commit comments