|
3 | 3 | // See the LICENSE file in the project root for more information.
|
4 | 4 |
|
5 | 5 | using System;
|
| 6 | +using System.Collections.Generic; |
6 | 7 | using System.Data;
|
7 | 8 | using System.Threading;
|
8 | 9 | using System.Threading.Tasks;
|
|
11 | 12 |
|
12 | 13 | namespace Microsoft.Data.SqlClient.ManualTesting.Tests
|
13 | 14 | {
|
14 |
| - public class SqlCommandReliabilityTest |
| 15 | + public class SqlCommandReliabilityTest : IDisposable |
15 | 16 | {
|
| 17 | + private const int ConcurrentParallelExecutions = 3; |
| 18 | + private const string UnexpectedSqlConnectionExceptionMessage = "An unexpected SQL Connection Error occured and is not part of the test."; |
16 | 19 | private readonly string _exceedErrMsgPattern = RetryLogicTestHelper.s_exceedErrMsgPattern;
|
17 | 20 | private readonly string _cancelErrMsgPattern = RetryLogicTestHelper.s_cancelErrMsgPattern;
|
| 21 | + private readonly int _originalWorkerThreads; |
| 22 | + private readonly int _originalCompletionPortThreads; |
| 23 | + private bool _disposed = false; |
| 24 | + |
| 25 | + public SqlCommandReliabilityTest() |
| 26 | + { |
| 27 | + ThreadPool.GetMinThreads(out _originalWorkerThreads, out _originalCompletionPortThreads); |
| 28 | + ThreadPool.SetMinThreads(ConcurrentParallelExecutions * 2, ConcurrentParallelExecutions * 2); // Multiply by 2 for possible additional threads needed in functions |
| 29 | + } |
| 30 | + |
| 31 | + // Dispose Pattern |
| 32 | + public void Dispose() |
| 33 | + { |
| 34 | + Dispose(true); |
| 35 | + GC.SuppressFinalize(this); |
| 36 | + } |
| 37 | + |
| 38 | + protected virtual void Dispose(bool disposing) |
| 39 | + { |
| 40 | + if (!_disposed) |
| 41 | + { |
| 42 | + ThreadPool.SetMinThreads(_originalWorkerThreads, _originalCompletionPortThreads); |
| 43 | + _disposed = true; |
| 44 | + } |
| 45 | + } |
18 | 46 |
|
19 | 47 | #region Sync
|
20 | 48 | [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))]
|
@@ -524,160 +552,119 @@ public async void RetryExecuteAsyncCancel(string cnnString, SqlRetryLogicBasePro
|
524 | 552 | [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCommand), parameters: new object[] { 2 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)]
|
525 | 553 | public void ConcurrentExecution(string cnnString, SqlRetryLogicBaseProvider provider)
|
526 | 554 | {
|
527 |
| - int numberOfTries = provider.RetryLogic.NumberOfTries; |
528 | 555 | string query = "SELECT bad command";
|
529 |
| - int retriesCount = 0; |
530 |
| - int concurrentExecution = 3; |
531 |
| - provider.Retrying += (s, e) => Interlocked.Increment(ref retriesCount); |
| 556 | + ProcessDataInParallel(cnnString, provider, query, cmd => cmd.ExecuteScalar()); |
| 557 | + ProcessDataInParallel(cnnString, provider, query, cmd => cmd.ExecuteNonQuery()); |
| 558 | + ProcessDataInParallel(cnnString, provider, query, cmd => cmd.ExecuteReader()); |
| 559 | + ProcessDataInParallel(cnnString, provider, query, cmd => cmd.ExecuteXmlReader()); |
532 | 560 |
|
533 |
| - Parallel.For(0, concurrentExecution, |
534 |
| - i => |
| 561 | + if (DataTestUtility.IsNotAzureSynapse()) |
535 | 562 | {
|
536 |
| - using (SqlConnection cnn = new SqlConnection(cnnString)) |
537 |
| - using (SqlCommand cmd = cnn.CreateCommand()) |
538 |
| - { |
539 |
| - cnn.Open(); |
540 |
| - cmd.RetryLogicProvider = provider; |
541 |
| - cmd.CommandText = query; |
542 |
| - Assert.Throws<AggregateException>(() => cmd.ExecuteScalar()); |
543 |
| - } |
544 |
| - }); |
545 |
| - Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); |
| 563 | + query += " FOR XML AUTO"; |
| 564 | + ProcessDataInParallel(cnnString, provider, query, cmd => cmd.ExecuteXmlReader()); |
| 565 | + } |
| 566 | + } |
546 | 567 |
|
547 |
| - retriesCount = 0; |
548 |
| - Parallel.For(0, concurrentExecution, |
549 |
| - i => |
| 568 | + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] |
| 569 | + [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCommand), parameters: new object[] { 2 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] |
| 570 | + public async void ConcurrentExecutionAsync(string cnnString, SqlRetryLogicBaseProvider provider) |
| 571 | + { |
| 572 | + string query = "SELECT bad command"; |
| 573 | + await ProcessDataAsAsync(cnnString, provider, query, cmd => cmd.ExecuteScalarAsync()); |
| 574 | + await ProcessDataAsAsync(cnnString, provider, query, cmd => cmd.ExecuteNonQueryAsync()); |
| 575 | + await ProcessDataAsAsync(cnnString, provider, query, cmd => cmd.ExecuteReaderAsync()); |
| 576 | + await ProcessDataAsAsync(cnnString, provider, query, cmd => cmd.ExecuteXmlReaderAsync()); |
| 577 | + |
| 578 | + if (DataTestUtility.IsNotAzureSynapse()) |
550 | 579 | {
|
551 |
| - using (SqlConnection cnn = new SqlConnection(cnnString)) |
552 |
| - using (SqlCommand cmd = cnn.CreateCommand()) |
| 580 | + query += " FOR XML AUTO"; |
| 581 | + await ProcessDataAsAsync(cnnString, provider, query, cmd => cmd.ExecuteXmlReaderAsync()); |
| 582 | + } |
| 583 | + } |
| 584 | + #endregion |
| 585 | + |
| 586 | + #region private members |
| 587 | + private SqlCommand CreateCommand(SqlConnection cnn, SqlRetryLogicBaseProvider provider, int cancelAfterRetries) |
| 588 | + { |
| 589 | + cnn.Open(); |
| 590 | + SqlCommand cmd = cnn.CreateCommand(); |
| 591 | + cmd.RetryLogicProvider = provider; |
| 592 | + cmd.RetryLogicProvider.Retrying += (object s, SqlRetryingEventArgs e) => |
| 593 | + { |
| 594 | + Assert.Equal(e.RetryCount, e.Exceptions.Count); |
| 595 | + Assert.NotEqual(TimeSpan.Zero, e.Delay); |
| 596 | + |
| 597 | + if (e.RetryCount >= cancelAfterRetries) |
553 | 598 | {
|
554 |
| - cnn.Open(); |
555 |
| - cmd.RetryLogicProvider = provider; |
556 |
| - cmd.CommandText = query; |
557 |
| - Assert.Throws<AggregateException>(() => cmd.ExecuteNonQuery()); |
| 599 | + e.Cancel = true; |
558 | 600 | }
|
559 |
| - }); |
560 |
| - Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); |
| 601 | + }; |
| 602 | + return cmd; |
| 603 | + } |
561 | 604 |
|
562 |
| - retriesCount = 0; |
563 |
| - Parallel.For(0, concurrentExecution, |
| 605 | + private static void ProcessDataInParallel(string cnnString, SqlRetryLogicBaseProvider provider, |
| 606 | + string query, Action<SqlCommand> commandAction) |
| 607 | + { |
| 608 | + int numberOfTries = provider.RetryLogic.NumberOfTries; |
| 609 | + int retryExceptionCount = 0; |
| 610 | + |
| 611 | + Parallel.For(0, ConcurrentParallelExecutions, |
564 | 612 | i =>
|
565 | 613 | {
|
566 | 614 | using (SqlConnection cnn = new SqlConnection(cnnString))
|
567 |
| - using (SqlCommand cmd = cnn.CreateCommand()) |
568 | 615 | {
|
569 | 616 | cnn.Open();
|
570 |
| - cmd.RetryLogicProvider = provider; |
571 |
| - cmd.CommandText = query; |
572 |
| - Assert.Throws<AggregateException>(() => cmd.ExecuteReader()); |
573 |
| - } |
574 |
| - }); |
575 |
| - Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); |
| 617 | + Assert.True(cnn.State == ConnectionState.Open, UnexpectedSqlConnectionExceptionMessage); |
576 | 618 |
|
577 |
| - if(DataTestUtility.IsNotAzureSynapse()) |
578 |
| - { |
579 |
| - retriesCount = 0; |
580 |
| - Parallel.For(0, concurrentExecution, |
581 |
| - i => |
582 |
| - { |
583 |
| - using (SqlConnection cnn = new SqlConnection(cnnString)) |
584 | 619 | using (SqlCommand cmd = cnn.CreateCommand())
|
585 | 620 | {
|
586 |
| - cnn.Open(); |
587 | 621 | cmd.RetryLogicProvider = provider;
|
588 |
| - cmd.CommandText = query + " FOR XML AUTO"; |
589 |
| - Assert.Throws<AggregateException>(() => cmd.ExecuteXmlReader()); |
| 622 | + cmd.CommandText = query; |
| 623 | + |
| 624 | + AggregateException retryAggregateException = Assert.Throws<AggregateException>(() => commandAction(cmd)); |
| 625 | + Interlocked.Add(ref retryExceptionCount, retryAggregateException.InnerExceptions?.Count ?? 0); |
590 | 626 | }
|
591 |
| - }); |
592 |
| - Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); |
593 |
| - } |
594 |
| - |
595 |
| - retriesCount = 0; |
596 |
| - Parallel.For(0, concurrentExecution, |
597 |
| - i => |
598 |
| - { |
599 |
| - using (SqlConnection cnn = new SqlConnection(cnnString)) |
600 |
| - using (SqlCommand cmd = cnn.CreateCommand()) |
601 |
| - { |
602 |
| - cnn.Open(); |
603 |
| - cmd.RetryLogicProvider = provider; |
604 |
| - cmd.CommandText = query; |
605 |
| - Assert.ThrowsAsync<AggregateException>(() => cmd.ExecuteScalarAsync()).Wait(); |
606 | 627 | }
|
607 | 628 | });
|
608 |
| - Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); |
609 | 629 |
|
610 |
| - retriesCount = 0; |
611 |
| - Parallel.For(0, concurrentExecution, |
612 |
| - i => |
613 |
| - { |
614 |
| - using (SqlConnection cnn = new SqlConnection(cnnString)) |
615 |
| - using (SqlCommand cmd = cnn.CreateCommand()) |
616 |
| - { |
617 |
| - cnn.Open(); |
618 |
| - cmd.RetryLogicProvider = provider; |
619 |
| - cmd.CommandText = query; |
620 |
| - Assert.ThrowsAsync<AggregateException>(() => cmd.ExecuteNonQueryAsync()).Wait(); |
621 |
| - } |
622 |
| - }); |
623 |
| - Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); |
| 630 | + // Assertion for Retries |
| 631 | + int expectedTotalNumberOfRetries = numberOfTries * ConcurrentParallelExecutions; |
| 632 | + Assert.Equal(expectedTotalNumberOfRetries, retryExceptionCount); |
| 633 | + } |
624 | 634 |
|
625 |
| - retriesCount = 0; |
626 |
| - Parallel.For(0, concurrentExecution, |
627 |
| - i => |
628 |
| - { |
629 |
| - using (SqlConnection cnn = new SqlConnection(cnnString)) |
630 |
| - using (SqlCommand cmd = cnn.CreateCommand()) |
631 |
| - { |
632 |
| - cnn.Open(); |
633 |
| - cmd.RetryLogicProvider = provider; |
634 |
| - cmd.CommandText = query; |
635 |
| - Assert.ThrowsAsync<AggregateException>(() => cmd.ExecuteReaderAsync()).Wait(); |
636 |
| - } |
637 |
| - }); |
638 |
| - Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); |
| 635 | + private static async Task ProcessDataAsAsync(string cnnString, SqlRetryLogicBaseProvider provider, |
| 636 | + string query, Func<SqlCommand, Task> commandAction) |
| 637 | + { |
| 638 | + int numberOfTries = provider.RetryLogic.NumberOfTries; |
| 639 | + int retryExceptionCount = 0; |
639 | 640 |
|
640 |
| - // TODO: there is a known issue by ExecuteXmlReaderAsync that should be solved first- issue #44 |
641 |
| - /* |
642 |
| - |
643 |
| - if(DataTestUtility.IsNotAzureSynapse()) |
| 641 | + List<Task> tasks = new List<Task>(); |
| 642 | + for (int i = 0; i < ConcurrentParallelExecutions; i++) |
644 | 643 | {
|
645 |
| - retriesCount = 0; |
646 |
| - Parallel.For(0, concurrentExecution, |
647 |
| - i => |
| 644 | + tasks.Add(Task.Run(async () => |
648 | 645 | {
|
649 | 646 | using (SqlConnection cnn = new SqlConnection(cnnString))
|
650 |
| - using (SqlCommand cmd = cnn.CreateCommand()) |
651 | 647 | {
|
652 |
| - cnn.Open(); |
653 |
| - cmd.RetryLogicProvider = provider; |
654 |
| - cmd.CommandText = query + " FOR XML AUTO"; |
655 |
| - Assert.ThrowsAsync<AggregateException>(() => cmd.ExecuteXmlReaderAsync()).Wait(); |
| 648 | + await cnn.OpenAsync(); |
| 649 | + Assert.True(cnn.State == ConnectionState.Open, UnexpectedSqlConnectionExceptionMessage); |
| 650 | + |
| 651 | + using (SqlCommand cmd = cnn.CreateCommand()) |
| 652 | + { |
| 653 | + cmd.RetryLogicProvider = provider; |
| 654 | + cmd.CommandText = query; |
| 655 | + |
| 656 | + AggregateException retryAggregateException = await Assert.ThrowsAsync<AggregateException>(async () => await commandAction(cmd)); |
| 657 | + Interlocked.Add(ref retryExceptionCount, retryAggregateException.InnerExceptions?.Count ?? 0); |
| 658 | + } |
656 | 659 | }
|
657 |
| - }); |
658 |
| - Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); |
| 660 | + })); |
659 | 661 | }
|
660 |
| - */ |
661 |
| - } |
662 |
| - #endregion |
663 | 662 |
|
664 |
| - #region private members |
665 |
| - private SqlCommand CreateCommand(SqlConnection cnn, SqlRetryLogicBaseProvider provider, int cancelAfterRetries) |
666 |
| - { |
667 |
| - cnn.Open(); |
668 |
| - SqlCommand cmd = cnn.CreateCommand(); |
669 |
| - cmd.RetryLogicProvider = provider; |
670 |
| - cmd.RetryLogicProvider.Retrying += (object s, SqlRetryingEventArgs e) => |
671 |
| - { |
672 |
| - Assert.Equal(e.RetryCount, e.Exceptions.Count); |
673 |
| - Assert.NotEqual(TimeSpan.Zero, e.Delay); |
| 663 | + await Task.WhenAll(tasks); |
674 | 664 |
|
675 |
| - if (e.RetryCount >= cancelAfterRetries) |
676 |
| - { |
677 |
| - e.Cancel = true; |
678 |
| - } |
679 |
| - }; |
680 |
| - return cmd; |
| 665 | + // Assertion for Retries |
| 666 | + int expectedTotalNumberOfRetries = numberOfTries * ConcurrentParallelExecutions; |
| 667 | + Assert.Equal(expectedTotalNumberOfRetries, retryExceptionCount); |
681 | 668 | }
|
682 | 669 | #endregion
|
683 | 670 | }
|
|
0 commit comments