Skip to content

Commit 739101d

Browse files
authored
Merge pull request #228 from caleblloyd/f_connection_lifetime
Implement Connection Lifetime.
2 parents 6d3a0d2 + 308d1d2 commit 739101d

File tree

9 files changed

+71
-8
lines changed

9 files changed

+71
-8
lines changed

docs/content/connection-options.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ Connection pooling is enabled by default. These options are used to configure i
103103
<td>true</td>
104104
<td>When true, the MySqlConnection object is drawn from the appropriate pool, or if necessary, is created and added to the appropriate pool. Recognized values are true, false, yes, and no.</td>
105105
</tr>
106+
<tr>
107+
<td>Connection Lifetime, ConnectionLifeTime</td>
108+
<td>0</td>
109+
<td>When a connection is returned to the pool, its creation time is compared with the current time, and the connection is destroyed if that time span (in seconds) exceeds the value specified by Connection Lifetime. This is useful in clustered configurations to force load balancing between a running server and a server just brought online. A value of zero (0) causes pooled connections to have the maximum connection timeout.</td>
110+
</tr>
106111
<tr>
107112
<td>Connection Reset, ConnectionReset </td>
108113
<td>false</td>

src/MySqlConnector/MySqlClient/ConnectionPool.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,26 @@ public async Task<MySqlSession> GetSessionAsync(IOBehavior ioBehavior, Cancellat
5353
}
5454
}
5555

56+
private bool SessionIsHealthy(MySqlSession session)
57+
{
58+
if (!session.IsConnected)
59+
return false;
60+
if (session.PoolGeneration != m_generation)
61+
return false;
62+
if (session.DatabaseOverride != null)
63+
return false;
64+
if (m_connectionSettings.ConnectionLifeTime > 0
65+
&& (DateTime.UtcNow - session.CreatedUtc).TotalSeconds >= m_connectionSettings.ConnectionLifeTime)
66+
return false;
67+
68+
return true;
69+
}
70+
5671
public void Return(MySqlSession session)
5772
{
5873
try
5974
{
60-
if (session.IsConnected && session.PoolGeneration == m_generation && session.DatabaseOverride == null)
75+
if (SessionIsHealthy(session))
6176
m_sessions.Enqueue(session);
6277
else
6378
session.DisposeAsync(IOBehavior.Synchronous, CancellationToken.None).ConfigureAwait(false);

src/MySqlConnector/MySqlClient/MySqlConnectionStringBuilder.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ public bool Pooling
7373
set => MySqlConnectionStringOption.Pooling.SetValue(this, value);
7474
}
7575

76+
public uint ConnectionLifeTime
77+
{
78+
get => MySqlConnectionStringOption.ConnectionLifeTime.GetValue(this);
79+
set => MySqlConnectionStringOption.ConnectionLifeTime.SetValue(this, value);
80+
}
81+
7682
public bool ConnectionReset
7783
{
7884
get => MySqlConnectionStringOption.ConnectionReset.GetValue(this);
@@ -223,6 +229,7 @@ internal abstract class MySqlConnectionStringOption
223229

224230
// Connection Pooling Options
225231
public static readonly MySqlConnectionStringOption<bool> Pooling;
232+
public static readonly MySqlConnectionStringOption<uint> ConnectionLifeTime;
226233
public static readonly MySqlConnectionStringOption<bool> ConnectionReset;
227234
public static readonly MySqlConnectionStringOption<uint> MinimumPoolSize;
228235
public static readonly MySqlConnectionStringOption<uint> MaximumPoolSize;
@@ -306,6 +313,10 @@ static MySqlConnectionStringOption()
306313
keys: new[] { "Pooling" },
307314
defaultValue: true));
308315

316+
AddOption(ConnectionLifeTime = new MySqlConnectionStringOption<uint>(
317+
keys: new[] { "Connection Lifetime", "ConnectionLifeTime" },
318+
defaultValue: 0));
319+
309320
AddOption(ConnectionReset = new MySqlConnectionStringOption<bool>(
310321
keys: new[] { "Connection Reset", "ConnectionReset" },
311322
defaultValue: true));

src/MySqlConnector/Serialization/ConnectionSettings.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public ConnectionSettings(MySqlConnectionStringBuilder csb)
3636

3737
// Connection Pooling Options
3838
Pooling = csb.Pooling;
39+
ConnectionLifeTime = (int)csb.ConnectionLifeTime;
3940
ConnectionReset = csb.ConnectionReset;
4041
if (csb.MinimumPoolSize > csb.MaximumPoolSize)
4142
throw new MySqlException("MaximumPoolSize must be greater than or equal to MinimumPoolSize");
@@ -77,6 +78,7 @@ private ConnectionSettings(ConnectionSettings other, bool? useCompression)
7778

7879
// Connection Pooling Options
7980
Pooling = other.Pooling;
81+
ConnectionLifeTime = other.ConnectionLifeTime;
8082
ConnectionReset = other.ConnectionReset;
8183
MinimumPoolSize = other.MinimumPoolSize;
8284
MaximumPoolSize = other.MaximumPoolSize;
@@ -112,6 +114,7 @@ private ConnectionSettings(ConnectionSettings other, bool? useCompression)
112114

113115
// Connection Pooling Options
114116
internal readonly bool Pooling;
117+
internal readonly int ConnectionLifeTime;
115118
internal readonly bool ConnectionReset;
116119
internal readonly int MinimumPoolSize;
117120
internal readonly int MaximumPoolSize;

src/MySqlConnector/Serialization/MySqlSession.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ public MySqlSession()
2323

2424
public MySqlSession(ConnectionPool pool, int poolGeneration)
2525
{
26+
CreatedUtc = DateTime.UtcNow;
2627
Pool = pool;
2728
PoolGeneration = poolGeneration;
2829
}
2930

3031
public ServerVersion ServerVersion { get; set; }
3132
public int ConnectionId { get; set; }
3233
public byte[] AuthPluginData { get; set; }
34+
public DateTime CreatedUtc { get; }
3335
public ConnectionPool Pool { get; }
3436
public int PoolGeneration { get; }
3537
public string DatabaseOverride { get; set; }

tests/MySqlConnector.Tests/MySqlConnectionStringBuilderTests.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public void Defaults()
1717
#if BASELINE
1818
Assert.Equal(false, csb.ConnectionReset);
1919
#else
20+
Assert.Equal(0u, csb.ConnectionLifeTime);
2021
Assert.Equal(true, csb.ConnectionReset);
2122
#endif
2223
Assert.Equal(15u, csb.ConnectionTimeout);
@@ -59,6 +60,7 @@ public void ParseConnectionString()
5960
"Character Set=latin1;" +
6061
"Compress=true;" +
6162
"connect timeout=30;" +
63+
"connection lifetime=15;" +
6264
"ConnectionReset=false;" +
6365
"Convert Zero Datetime=true;" +
6466
#if !BASELINE
@@ -82,6 +84,7 @@ public void ParseConnectionString()
8284
Assert.Equal("file.pfx", csb.CertificateFile);
8385
Assert.Equal("Pass1234", csb.CertificatePassword);
8486
Assert.Equal("latin1", csb.CharacterSet);
87+
Assert.Equal(15u, csb.ConnectionLifeTime);
8588
Assert.Equal(false, csb.ConnectionReset);
8689
Assert.Equal(30u, csb.ConnectionTimeout);
8790
Assert.Equal(true, csb.ConvertZeroDateTime);

tests/SideBySide/ConnectionPool.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,35 @@ public async Task WaitTimeout()
159159
}
160160
}
161161

162+
[Theory]
163+
[InlineData(2u, 3u, true)]
164+
[InlineData(180u, 3u, false)]
165+
public async Task ConnectionLifeTime(uint lifeTime, uint delaySeconds, bool shouldTimeout)
166+
{
167+
var csb = AppConfig.CreateConnectionStringBuilder();
168+
csb.Pooling = true;
169+
csb.MinimumPoolSize = 0;
170+
csb.MaximumPoolSize = 1;
171+
csb.ConnectionLifeTime = lifeTime;
172+
int serverThread;
173+
174+
using (var connection = new MySqlConnection(csb.ConnectionString))
175+
{
176+
await connection.OpenAsync();
177+
serverThread = connection.ServerThread;
178+
await Task.Delay(TimeSpan.FromSeconds(delaySeconds));
179+
}
180+
181+
using (var connection = new MySqlConnection(csb.ConnectionString))
182+
{
183+
await connection.OpenAsync();
184+
if (shouldTimeout)
185+
Assert.NotEqual(serverThread, connection.ServerThread);
186+
else
187+
Assert.Equal(serverThread, connection.ServerThread);
188+
}
189+
}
190+
162191
[Fact]
163192
public async Task CharacterSet()
164193
{

tests/SideBySide/SideBySide.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@
1515
<PreserveCompilationContext>true</PreserveCompilationContext>
1616
<AssemblyName>SideBySide</AssemblyName>
1717
<PackageId>SideBySide</PackageId>
18-
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
1918
<ServerGarbageCollection>true</ServerGarbageCollection>
19+
<ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
20+
<ThreadPoolMinThreads>64</ThreadPoolMinThreads>
2021
</PropertyGroup>
2122

2223
<ItemGroup>

tests/SideBySide/runtimeconfig.template.json

Lines changed: 0 additions & 6 deletions
This file was deleted.

0 commit comments

Comments
 (0)