|
1 | 1 | using System.Collections.Concurrent;
|
| 2 | +using System.Net; |
2 | 3 | using System.Security.Authentication;
|
3 | 4 | using MySqlConnector.Logging;
|
4 | 5 | using MySqlConnector.Protocol.Serialization;
|
5 | 6 | using MySqlConnector.Utilities;
|
6 | 7 |
|
7 | 8 | namespace MySqlConnector.Core;
|
8 | 9 |
|
| 10 | +#pragma warning disable CA1001 // Types that own disposable fields should be disposable |
9 | 11 | internal sealed class ConnectionPool
|
| 12 | +#pragma warning restore CA1001 // Types that own disposable fields should be disposable |
10 | 13 | {
|
11 | 14 | public int Id { get; }
|
12 | 15 |
|
@@ -438,6 +441,7 @@ private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection conne
|
438 | 441 | var connectionSettings = new ConnectionSettings(connectionStringBuilder);
|
439 | 442 | var pool = new ConnectionPool(connectionSettings);
|
440 | 443 | pool.StartReaperTask();
|
| 444 | + pool.StartDnsCheckTimer(); |
441 | 445 | return pool;
|
442 | 446 | }
|
443 | 447 |
|
@@ -485,6 +489,7 @@ private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection conne
|
485 | 489 | {
|
486 | 490 | s_mruCache = new(connectionString, pool);
|
487 | 491 | pool.StartReaperTask();
|
| 492 | + pool.StartDnsCheckTimer(); |
488 | 493 |
|
489 | 494 | // if we won the race to create the new pool, also store it under the original connection string
|
490 | 495 | if (connectionString != normalizedConnectionString)
|
@@ -569,6 +574,92 @@ private void StartReaperTask()
|
569 | 574 | }
|
570 | 575 | }
|
571 | 576 |
|
| 577 | + private void StartDnsCheckTimer() |
| 578 | + { |
| 579 | + if (ConnectionSettings.ConnectionProtocol != MySqlConnectionProtocol.Tcp || ConnectionSettings.DnsCheckInterval <= 0) |
| 580 | + return; |
| 581 | + |
| 582 | + var hostNames = ConnectionSettings.HostNames!; |
| 583 | + var hostAddresses = new IPAddress[hostNames.Count][]; |
| 584 | + |
| 585 | +#if NET6_0_OR_GREATER |
| 586 | + m_dnsCheckTimer = new PeriodicTimer(TimeSpan.FromSeconds(ConnectionSettings.DnsCheckInterval)); |
| 587 | + _ = RunTimer(); |
| 588 | + |
| 589 | + async Task RunTimer() |
| 590 | + { |
| 591 | + while (await m_dnsCheckTimer.WaitForNextTickAsync().ConfigureAwait(false)) |
| 592 | + { |
| 593 | + Log.Trace("Pool{0} checking for DNS changes", m_logArguments); |
| 594 | + var hostNamesChanged = false; |
| 595 | + for (var hostNameIndex = 0; hostNameIndex < hostNames.Count; hostNameIndex++) |
| 596 | + { |
| 597 | + try |
| 598 | + { |
| 599 | + var ipAddresses = await Dns.GetHostAddressesAsync(hostNames[hostNameIndex]).ConfigureAwait(false); |
| 600 | + if (hostAddresses[hostNameIndex] is null) |
| 601 | + { |
| 602 | + hostAddresses[hostNameIndex] = ipAddresses; |
| 603 | + } |
| 604 | + else if (hostAddresses[hostNameIndex].Except(ipAddresses).Any()) |
| 605 | + { |
| 606 | + Log.Debug("Pool{0} detected DNS change for HostName '{1}': {2} to {3}", m_logArguments[0], hostNames[hostNameIndex], string.Join<IPAddress>(',', hostAddresses[hostNameIndex]), string.Join<IPAddress>(',', ipAddresses)); |
| 607 | + hostAddresses[hostNameIndex] = ipAddresses; |
| 608 | + hostNamesChanged = true; |
| 609 | + } |
| 610 | + } |
| 611 | + catch (Exception ex) |
| 612 | + { |
| 613 | + // do nothing; we'll try again later |
| 614 | + Log.Debug("Pool{0} DNS check failed; ignoring HostName '{1}': {2}", m_logArguments[0], hostNames[hostNameIndex], ex.Message); |
| 615 | + } |
| 616 | + } |
| 617 | + if (hostNamesChanged) |
| 618 | + { |
| 619 | + Log.Info("Pool{0} clearing pool due to DNS changes", m_logArguments); |
| 620 | + await ClearAsync(IOBehavior.Asynchronous, CancellationToken.None).ConfigureAwait(false); |
| 621 | + } |
| 622 | + } |
| 623 | + } |
| 624 | +#else |
| 625 | + var interval = Math.Min(int.MaxValue / 1000, ConnectionSettings.DnsCheckInterval) * 1000; |
| 626 | + m_dnsCheckTimer = new Timer(t => |
| 627 | + { |
| 628 | + Log.Trace("Pool{0} checking for DNS changes", m_logArguments); |
| 629 | + var hostNamesChanged = false; |
| 630 | + for (var hostNameIndex = 0; hostNameIndex < hostNames.Count; hostNameIndex++) |
| 631 | + { |
| 632 | + try |
| 633 | + { |
| 634 | + var ipAddresses = Dns.GetHostAddresses(hostNames[hostNameIndex]); |
| 635 | + if (hostAddresses[hostNameIndex] is null) |
| 636 | + { |
| 637 | + hostAddresses[hostNameIndex] = ipAddresses; |
| 638 | + } |
| 639 | + else if (hostAddresses[hostNameIndex].Except(ipAddresses).Any()) |
| 640 | + { |
| 641 | + Log.Debug("Pool{0} detected DNS change for HostName '{1}': {2} to {3}", m_logArguments[0], hostNames[hostNameIndex], string.Join<IPAddress>(",", hostAddresses[hostNameIndex]), string.Join<IPAddress>(",", ipAddresses)); |
| 642 | + hostAddresses[hostNameIndex] = ipAddresses; |
| 643 | + hostNamesChanged = true; |
| 644 | + } |
| 645 | + } |
| 646 | + catch (Exception ex) |
| 647 | + { |
| 648 | + // do nothing; we'll try again later |
| 649 | + Log.Debug("Pool{0} DNS check failed; ignoring HostName '{1}': {2}", m_logArguments[0], hostNames[hostNameIndex], ex.Message); |
| 650 | + } |
| 651 | + } |
| 652 | + if (hostNamesChanged) |
| 653 | + { |
| 654 | + Log.Info("Pool{0} clearing pool due to DNS changes", m_logArguments); |
| 655 | + ClearAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult(); |
| 656 | + } |
| 657 | + ((Timer) t!).Change(interval, -1); |
| 658 | + }); |
| 659 | + m_dnsCheckTimer.Change(interval, -1); |
| 660 | +#endif |
| 661 | + } |
| 662 | + |
572 | 663 | private void AdjustHostConnectionCount(ServerSession session, int delta)
|
573 | 664 | {
|
574 | 665 | if (m_hostSessions is not null)
|
@@ -630,4 +721,9 @@ private static void OnAppDomainShutDown(object? sender, EventArgs e) =>
|
630 | 721 | private uint m_lastRecoveryTime;
|
631 | 722 | private int m_lastSessionId;
|
632 | 723 | private Dictionary<string, CachedProcedure?>? m_procedureCache;
|
| 724 | +#if NET6_0_OR_GREATER |
| 725 | + private PeriodicTimer? m_dnsCheckTimer; |
| 726 | +#else |
| 727 | + private Timer? m_dnsCheckTimer; |
| 728 | +#endif |
633 | 729 | }
|
0 commit comments