MySQL Lists are EOL. Please join:

List:Commits« Previous MessageNext Message »
From:Vladislav Vaintroub Date:August 4 2009 8:44pm
Subject:bzr commit into connector-net-6.0 branch (vvaintroub:738) Bug#40684
View as plain text  
#At file:///H:/connector_net/6.0/ based on revid:reggie.burnett@stripped

  738 Vladislav Vaintroub	2009-08-04
      Bug #40684 Connector/NET does not obey Connect Timeout.
      The actual problem in the bug report is that pooled connections are dropped  by
      firewalls due to long inactivity and that results in longer connect times.
            
      The patch introduces new parameter: "keepaliv" (integer, number of seconds before first keepalive packet is sent). 
      One can use this parameters to prevent inactivity drops and also to detect broken
      connections faster. Note that setting non-default timeout  is currently not possible with  Compact Framework nor with in Mono. Here, if keepalive option is used, the timeout would be defined by the OS default, which is normally 2 hours.

    modified:
      CHANGES
      MySql.Data/Provider/Source/MySqlConnectionStringBuilder.cs
      MySql.Data/Provider/Source/NativeDriver.cs
      MySql.Data/Provider/Source/common/StreamCreator.cs
      MySql.Data/Tests/Source/ConnectionStringBuilder.cs
      MySql.Data/Tests/Source/ConnectionTests.cs
=== modified file 'CHANGES'
--- a/CHANGES	2009-07-31 22:42:55 +0000
+++ b/CHANGES	2009-08-04 20:44:43 +0000
@@ -1,4 +1,6 @@
 Version 6.0.5
+- added keepalive connection option that can be used to detect network outages faster 
+  and prevent firewalls from dropping idle connections (bug#40684)
 - fixed bug where our internal driver was using a batched command even if the user had added
   'allow batch=false' to their connection string (bug #45502)
 - we are now throwing an exception when DeriveParameters is unable to successfully populate

=== modified file 'MySql.Data/Provider/Source/MySqlConnectionStringBuilder.cs'
--- a/MySql.Data/Provider/Source/MySqlConnectionStringBuilder.cs	2009-07-31 22:36:40 +0000
+++ b/MySql.Data/Provider/Source/MySqlConnectionStringBuilder.cs	2009-08-04 20:44:43 +0000
@@ -57,6 +57,7 @@ namespace MySql.Data.MySqlClient
         bool interactiveSession;
         bool functionsReturnString;
         bool useAffectedRows;
+        uint keepalive;
 
         static MySqlConnectionStringBuilder()
         {
@@ -100,6 +101,7 @@ namespace MySql.Data.MySqlClient
             defaultValues.Add(Keyword.InteractiveSession, false);
             defaultValues.Add(Keyword.FunctionsReturnString, false);
             defaultValues.Add(Keyword.UseAffectedRows, false);
+            defaultValues.Add(Keyword.Keepalive,0);
         }
 
         /// <summary>
@@ -741,6 +743,23 @@ namespace MySql.Data.MySqlClient
             }
         }
 
+#if !CF
+        [Category("Advanced")]
+        [DisplayName("Keepalive")]
+        [Description("For TCP connections, idle connection time measured in seconds, before the first keepalive packet is sent." +
+            "A value of 0 indicates that keepalive is not used.")]
+        [DefaultValue(0)]
+#endif
+        public uint Keepalive
+        {
+            get { return keepalive;}
+            set 
+            {
+                SetValue("Keepalive", value);
+                keepalive = value;
+            }
+        }
+
         #endregion
 
         #region Pooling Properties
@@ -1176,6 +1195,8 @@ namespace MySql.Data.MySqlClient
                     return Keyword.FunctionsReturnString;
                 case "USE AFFECTED ROWS":
                     return Keyword.UseAffectedRows;
+                case "KEEPALIVE":
+                    return Keyword.Keepalive;
             }
             throw new ArgumentException(Resources.KeywordNotSupported, key);
         }
@@ -1264,6 +1285,8 @@ namespace MySql.Data.MySqlClient
                     return functionsReturnString;
                 case Keyword.UseAffectedRows:
                     return useAffectedRows;
+                case Keyword.Keepalive:
+                    return keepalive;
                 default:
                     return null; /* this will never happen */
             }
@@ -1375,6 +1398,8 @@ namespace MySql.Data.MySqlClient
                     functionsReturnString = ConvertToBool(value); break;
                 case Keyword.UseAffectedRows:
                     useAffectedRows = ConvertToBool(value); break;
+                case Keyword.Keepalive:
+                    keepalive = ConvertToUInt(value); break;
             }
         }
 
@@ -1557,6 +1582,7 @@ namespace MySql.Data.MySqlClient
         AllowUserVariables,
         InteractiveSession,
         FunctionsReturnString,
-        UseAffectedRows
+        UseAffectedRows,
+        Keepalive
     }
 }

=== modified file 'MySql.Data/Provider/Source/NativeDriver.cs'
--- a/MySql.Data/Provider/Source/NativeDriver.cs	2009-05-15 13:15:05 +0000
+++ b/MySql.Data/Provider/Source/NativeDriver.cs	2009-08-04 20:44:43 +0000
@@ -201,7 +201,8 @@ namespace MySql.Data.MySqlClient
                     string pipeName = Settings.PipeName;
                     if (Settings.ConnectionProtocol != MySqlConnectionProtocol.NamedPipe)
                         pipeName = null;
-                    StreamCreator sc = new StreamCreator(Settings.Server, Settings.Port, pipeName);
+                    StreamCreator sc = new StreamCreator(Settings.Server, Settings.Port, pipeName,
+                        Settings.Keepalive);
                     baseStream = sc.GetStream(Settings.ConnectionTimeout);
 #if !CF
                 }

=== modified file 'MySql.Data/Provider/Source/common/StreamCreator.cs'
--- a/MySql.Data/Provider/Source/common/StreamCreator.cs	2009-07-12 16:21:45 +0000
+++ b/MySql.Data/Provider/Source/common/StreamCreator.cs	2009-08-04 20:44:43 +0000
@@ -25,6 +25,7 @@ using System.Net.Sockets;
 using System.Reflection;
 using System.Diagnostics;
 using MySql.Data.MySqlClient.Properties;
+using System.Runtime.InteropServices;
 
 namespace MySql.Data.Common
 {
@@ -37,14 +38,16 @@ namespace MySql.Data.Common
         uint port;
         string pipeName;
         uint timeOut;
+        uint keepalive;
 
-        public StreamCreator(string hosts, uint port, string pipeName)
+        public StreamCreator(string hosts, uint port, string pipeName, uint keepalive)
         {
             hostList = hosts;
             if (hostList == null || hostList.Length == 0)
                 hostList = "localhost";
             this.port = port;
             this.pipeName = pipeName;
+            this.keepalive = keepalive;
         }
 
         public Stream GetStream(uint timeout)
@@ -181,6 +184,10 @@ namespace MySql.Data.Common
 			Socket socket = unix ?
 				new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.IP) :
 				new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+            if (keepalive > 0)
+            {
+                SetKeepAlive(socket, keepalive);
+            }
 			IAsyncResult ias = socket.BeginConnect(endPoint, null, null);
 			if (!ias.AsyncWaitHandle.WaitOne((int)timeOut * 1000, false))
 			{
@@ -202,5 +209,56 @@ namespace MySql.Data.Common
             return stream;
 		}
 
+
+
+        /// <summary>
+        /// Set keepalive + timeout on socket.
+        /// </summary>
+        /// <param name="s">socket</param>
+        /// <param name="time">keepalive timeout, in seconds</param>
+        private static void SetKeepAlive(Socket s, uint time)
+        {
+
+#if !CF
+            uint on = 1;
+            uint interval = 1000; // default interval = 1 sec
+
+            uint timeMilliseconds;
+            if (time > UInt32.MaxValue / 1000)
+                timeMilliseconds = UInt32.MaxValue;
+            else
+                timeMilliseconds = time * 1000;
+
+            // Use Socket.IOControl to implement equivalent of
+            // WSAIoctl with  SOL_KEEPALIVE_VALS 
+
+            // the native structure passed to WSAIoctl is
+            //struct tcp_keepalive {
+            //    ULONG onoff;
+            //    ULONG keepalivetime;
+            //    ULONG keepaliveinterval;
+            //};
+            // marshal the equivalent of the native structure into a byte array
+
+            byte[] inOptionValues = new byte[12];
+            BitConverter.GetBytes(on).CopyTo(inOptionValues, 0);
+            BitConverter.GetBytes(time).CopyTo(inOptionValues, 4);
+            BitConverter.GetBytes(interval).CopyTo(inOptionValues, 8);
+            try
+            {
+                // call WSAIoctl via IOControl
+                s.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
+                return;
+            }
+            catch (NotImplementedException)
+            {
+                // Mono throws not implemented currently
+            }
+#endif
+            // Fallback if Socket.IOControl is not available ( Compact Framework )
+            // or not implemented ( Mono ). Keepalive option will still be set, but
+            // with timeout is kept default.
+            s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
+        }
     }
 }

=== modified file 'MySql.Data/Tests/Source/ConnectionStringBuilder.cs'
--- a/MySql.Data/Tests/Source/ConnectionStringBuilder.cs	2009-04-21 18:02:13 +0000
+++ b/MySql.Data/Tests/Source/ConnectionStringBuilder.cs	2009-08-04 20:44:43 +0000
@@ -35,7 +35,7 @@ namespace MySql.Data.MySqlClient.Tests
             sb = new MySqlConnectionStringBuilder();
             sb.ConnectionString = "server=localhost;uid=reggie;pwd=pass;port=1111;" +
                 "connection timeout=23; pooling=true; min pool size=33; " +
-                "max pool size=66";
+                "max pool size=66;keepalive=1";
             Assert.AreEqual("localhost", sb.Server);
             Assert.AreEqual("reggie", sb.UserID);
             Assert.AreEqual("pass", sb.Password);
@@ -44,6 +44,7 @@ namespace MySql.Data.MySqlClient.Tests
             Assert.IsTrue(sb.Pooling);
             Assert.AreEqual(33, sb.MinimumPoolSize);
             Assert.AreEqual(66, sb.MaximumPoolSize);
+            Assert.AreEqual(sb.Keepalive, 1);
 
             try
             {
@@ -81,6 +82,7 @@ namespace MySql.Data.MySqlClient.Tests
             Assert.IsFalse(sb.AllowZeroDateTime);
             Assert.IsFalse(sb.UsePerformanceMonitor);
             Assert.AreEqual(25, sb.ProcedureCacheSize);
+            Assert.AreEqual(0, sb.Keepalive);
         }
 
         /// <summary>

=== modified file 'MySql.Data/Tests/Source/ConnectionTests.cs'
--- a/MySql.Data/Tests/Source/ConnectionTests.cs	2009-04-21 18:02:13 +0000
+++ b/MySql.Data/Tests/Source/ConnectionTests.cs	2009-08-04 20:44:43 +0000
@@ -449,5 +449,19 @@ namespace MySql.Data.MySqlClient.Tests
             MySqlConnection c = new MySqlConnection();
             c.ConnectionString = null;
         }
+
+        /// <summary>
+        /// Test if keepalive parameters work.
+        /// </summary>
+        [Test]
+        public void Keepalive()
+        {
+            string connstr = GetConnectionStringEx("test","test",true);
+            connstr += ";keepalive=1;pooling=true";
+            using (MySqlConnection c = new MySqlConnection(connstr))
+            {
+                c.Open();
+            }
+        }
     }
 }


Attachment: [text/bzr-bundle] bzr/vvaintroub@mysql.com-20090804204443-16hf89bqhpgxauat.bundle
Thread
bzr commit into connector-net-6.0 branch (vvaintroub:738) Bug#40684Vladislav Vaintroub4 Aug