#At file:///H:/connector_net/6.1/ based on revid:reggie.burnett@stripped
723 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 2 new parameters: "keepalive" (boolean, sets keepalive option on socket) and "keepalive timeout" (integer, number of seconds before first keepalive packet is sent). "keepalive timeout" does not automatically switch "keepalive" on, both options need to be specified to set the timeout.
One can use this parameters to prevent inactivity drops and also to detect broken connections faster.
Note that "Keepalive timeout" is not currently available in Compact Framework nor in Mono ( will be silently ignored there and connection will use the default timeout what is normally 2 hours)
modified:
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 'MySql.Data/Provider/Source/MySqlConnectionStringBuilder.cs'
--- a/MySql.Data/Provider/Source/MySqlConnectionStringBuilder.cs 2009-07-31 22:45:32 +0000
+++ b/MySql.Data/Provider/Source/MySqlConnectionStringBuilder.cs 2009-08-03 22:48:52 +0000
@@ -59,6 +59,8 @@ namespace MySql.Data.MySqlClient
bool functionsReturnString;
bool useAffectedRows;
bool oldGuids;
+ bool keepalive;
+ uint keepaliveTimeout;
static MySqlConnectionStringBuilder()
{
@@ -104,6 +106,8 @@ namespace MySql.Data.MySqlClient
defaultValues.Add(Keyword.UseAffectedRows, false);
defaultValues.Add(Keyword.SslMode, MySqlSslMode.None);
defaultValues.Add(Keyword.OldGuids, false);
+ defaultValues.Add(Keyword.Keepalive, false);
+ defaultValues.Add(Keyword.KeepaliveTimeout, 7200);
}
/// <summary>
@@ -761,6 +765,37 @@ namespace MySql.Data.MySqlClient
}
}
+#if !CF
+ [Category("Advanced")]
+ [DisplayName("Keepalive")]
+ [Description("Set keepalive option on socket, useful to check for network outage and to prevent disconnect due to long inactivity")]
+ [DefaultValue(false)]
+#endif
+ public bool Keepalive
+ {
+ get { return keepalive;}
+ set
+ {
+ SetValue("Keepalive", value);
+ keepalive = value;
+ }
+ }
+
+#if !CF
+ [Category("Advanced")]
+ [DisplayName("Keepalive timeout")]
+ [Description("Timeout, in seconds, with no activity until the first keep-alive packet is sent.")]
+ [DefaultValue(false)]
+#endif
+ public uint KeepaliveTimeout
+ {
+ get { return keepaliveTimeout; }
+ set
+ {
+ SetValue("Keepalive timeout", value);
+ keepaliveTimeout = value;
+ }
+ }
#endregion
#region Pooling Properties
@@ -1225,6 +1260,10 @@ namespace MySql.Data.MySqlClient
return Keyword.SslMode;
case "OLD GUIDS":
return Keyword.OldGuids;
+ case "KEEPALIVE":
+ return Keyword.Keepalive;
+ case "KEEPALIVE TIMEOUT":
+ return Keyword.KeepaliveTimeout;
}
throw new ArgumentException(Resources.KeywordNotSupported, key);
}
@@ -1317,6 +1356,10 @@ namespace MySql.Data.MySqlClient
return sslMode;
case Keyword.OldGuids:
return oldGuids;
+ case Keyword.Keepalive:
+ return keepalive;
+ case Keyword.KeepaliveTimeout:
+ return keepaliveTimeout;
default:
return null; /* this will never happen */
}
@@ -1435,6 +1478,10 @@ namespace MySql.Data.MySqlClient
sslMode = ConvertToSslMode(value); break;
case Keyword.OldGuids:
oldGuids = ConvertToBool(value); break;
+ case Keyword.Keepalive:
+ keepalive = ConvertToBool(value); break;
+ case Keyword.KeepaliveTimeout:
+ keepaliveTimeout = ConvertToUInt(value); break;
}
}
@@ -1619,6 +1666,8 @@ namespace MySql.Data.MySqlClient
FunctionsReturnString,
UseAffectedRows,
SslMode,
- OldGuids
+ OldGuids,
+ Keepalive,
+ KeepaliveTimeout
}
}
=== modified file 'MySql.Data/Provider/Source/NativeDriver.cs'
--- a/MySql.Data/Provider/Source/NativeDriver.cs 2009-07-28 20:40:35 +0000
+++ b/MySql.Data/Provider/Source/NativeDriver.cs 2009-08-03 22:48:52 +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, Settings.KeepaliveTimeout);
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-28 20:40:35 +0000
+++ b/MySql.Data/Provider/Source/common/StreamCreator.cs 2009-08-03 22:48:52 +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,18 @@ namespace MySql.Data.Common
uint port;
string pipeName;
uint timeOut;
+ uint keepaliveTimeout;
+ bool keepalive;
- public StreamCreator(string hosts, uint port, string pipeName)
+ public StreamCreator(string hosts, uint port, string pipeName, bool keepalive, uint keepaliveTimeout)
{
hostList = hosts;
if (hostList == null || hostList.Length == 0)
hostList = "localhost";
this.port = port;
this.pipeName = pipeName;
+ this.keepalive = keepalive;
+ this.keepaliveTimeout = keepaliveTimeout;
}
public Stream GetStream(uint timeout)
@@ -181,6 +186,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)
+ {
+ SetKeepAlive(socket, keepaliveTimeout);
+ }
IAsyncResult ias = socket.BeginConnect(endPoint, null, null);
if (!ias.AsyncWaitHandle.WaitOne((int)timeOut * 1000, false))
{
@@ -202,5 +211,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.Tcp, 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-03 22:48:52 +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=true;keepalive timeout=1";
Assert.AreEqual("localhost", sb.Server);
Assert.AreEqual("reggie", sb.UserID);
Assert.AreEqual("pass", sb.Password);
@@ -44,6 +44,10 @@ namespace MySql.Data.MySqlClient.Tests
Assert.IsTrue(sb.Pooling);
Assert.AreEqual(33, sb.MinimumPoolSize);
Assert.AreEqual(66, sb.MaximumPoolSize);
+ Assert.AreEqual(sb.Keepalive, true);
+ Assert.AreEqual(sb.KeepaliveTimeout, 1);
+
+
try
{
@@ -81,6 +85,8 @@ namespace MySql.Data.MySqlClient.Tests
Assert.IsFalse(sb.AllowZeroDateTime);
Assert.IsFalse(sb.UsePerformanceMonitor);
Assert.AreEqual(25, sb.ProcedureCacheSize);
+ Assert.IsFalse(sb.Keepalive);
+ Assert.AreEqual(sb.KeepaliveTimeout, 7200);
}
/// <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-03 22:48:52 +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=true;keepalive timeout=10;pooling=true";
+ using (MySqlConnection c = new MySqlConnection(connstr))
+ {
+ c.Open();
+ }
+ }
}
}
Attachment: [text/bzr-bundle] bzr/vvaintroub@mysql.com-20090803224852-2utrc3bzpay72jhq.bundle
| Thread |
|---|
| • bzr commit into connector-net-6.1 branch (vvaintroub:723) Bug#40684 | Vladislav Vaintroub | 4 Aug |