#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#40684 | Vladislav Vaintroub | 4 Aug |