You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
468 lines
15 KiB
468 lines
15 KiB
// Copyright (c) 2004-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc.
|
|
//
|
|
// This program is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License version 2 as published by
|
|
// the Free Software Foundation
|
|
//
|
|
// There are special exceptions to the terms and conditions of the GPL
|
|
// as it is applied to this software. View the full text of the
|
|
// exception in file EXCEPTIONS in the directory of this software
|
|
// distribution.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
using System;
|
|
using System.Data;
|
|
using System.Threading;
|
|
using MySql.Data.MySqlClient;
|
|
using NUnit.Framework;
|
|
using System.Reflection;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
|
|
namespace MySql.Data.MySqlClient.Tests
|
|
{
|
|
/// <summary>
|
|
/// Summary description for PoolingTests.
|
|
/// </summary>
|
|
[TestFixture]
|
|
public class PoolingTests : BaseTest
|
|
{
|
|
[Test]
|
|
public void Connection()
|
|
{
|
|
string connStr = GetPoolingConnectionString();
|
|
|
|
MySqlConnection c = new MySqlConnection(connStr);
|
|
c.Open();
|
|
int serverThread = c.ServerThread;
|
|
c.Close();
|
|
|
|
// first test that only a single connection get's used
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
c = new MySqlConnection(connStr);
|
|
c.Open();
|
|
Assert.AreEqual(serverThread, c.ServerThread);
|
|
c.Close();
|
|
}
|
|
|
|
c.Open();
|
|
KillConnection(c);
|
|
c.Close();
|
|
|
|
connStr += ";Min Pool Size=10";
|
|
MySqlConnection[] connArray = new MySqlConnection[10];
|
|
for (int i = 0; i < connArray.Length; i++)
|
|
{
|
|
connArray[i] = new MySqlConnection(connStr);
|
|
connArray[i].Open();
|
|
}
|
|
|
|
// now make sure all the server ids are different
|
|
for (int i = 0; i < connArray.Length; i++)
|
|
{
|
|
for (int j = 0; j < connArray.Length; j++)
|
|
{
|
|
if (i != j)
|
|
Assert.IsTrue(connArray[i].ServerThread != connArray[j].ServerThread);
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < connArray.Length; i++)
|
|
{
|
|
KillConnection(connArray[i]);
|
|
connArray[i].Close();
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void OpenKilled()
|
|
{
|
|
string connStr = GetPoolingConnectionString() + ";min pool size=1; max pool size=1";
|
|
MySqlConnection c = new MySqlConnection(connStr);
|
|
c.Open();
|
|
int threadId = c.ServerThread;
|
|
// thread gets killed right here
|
|
KillConnection(c);
|
|
c.Close();
|
|
|
|
c.Dispose();
|
|
|
|
c = new MySqlConnection(connStr);
|
|
c.Open();
|
|
int secondThreadId = c.ServerThread;
|
|
KillConnection(c);
|
|
c.Close();
|
|
Assert.IsFalse(threadId == secondThreadId);
|
|
}
|
|
|
|
[Test]
|
|
public void ReclaimBrokenConnection()
|
|
{
|
|
// now create a new connection string only allowing 1 connection in the pool
|
|
string connStr = GetPoolingConnectionString() + ";connect timeout=2;max pool size=1";
|
|
|
|
// now use up that connection
|
|
MySqlConnection c = new MySqlConnection(connStr);
|
|
c.Open();
|
|
|
|
// now attempting to open a connection should fail
|
|
try
|
|
{
|
|
MySqlConnection c2 = new MySqlConnection(connStr);
|
|
c2.Open();
|
|
Assert.Fail("Open after using up pool should fail");
|
|
}
|
|
catch (Exception) { }
|
|
|
|
// we now kill the first connection to simulate a server stoppage
|
|
base.KillConnection(c);
|
|
|
|
// now we do something on the first connection
|
|
try
|
|
{
|
|
c.ChangeDatabase("mysql");
|
|
Assert.Fail("This change database should not work");
|
|
}
|
|
catch (Exception) { }
|
|
|
|
// Opening a connection now should work
|
|
MySqlConnection connection = new MySqlConnection(connStr);
|
|
connection.Open();
|
|
KillConnection(connection);
|
|
connection.Close();
|
|
}
|
|
|
|
[Test]
|
|
public void TestUserReset()
|
|
{
|
|
string connStr = GetPoolingConnectionString();
|
|
using (MySqlConnection c = new MySqlConnection(connStr))
|
|
{
|
|
c.Open();
|
|
MySqlCommand cmd = new MySqlCommand("SET @testvar='5'", c);
|
|
cmd.ExecuteNonQuery();
|
|
|
|
cmd.CommandText = "SELECT @testvar";
|
|
object var = cmd.ExecuteScalar();
|
|
Assert.AreEqual("5", var);
|
|
c.Close();
|
|
|
|
c.Open();
|
|
object var2 = cmd.ExecuteScalar();
|
|
Assert.AreEqual(DBNull.Value, var2);
|
|
KillConnection(c);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bug #25614 After connection is closed, and opened again UTF-8 characters are not read well
|
|
/// </summary>
|
|
[Test]
|
|
public void UTF8AfterClosing()
|
|
{
|
|
string originalValue = "??????????";
|
|
|
|
execSQL("CREATE TABLE test (id int(11) NOT NULL, " +
|
|
"value varchar(100) NOT NULL, PRIMARY KEY (`id`) " +
|
|
") ENGINE=MyISAM DEFAULT CHARSET=utf8");
|
|
|
|
string connStr = GetPoolingConnectionString() + ";charset=utf8";
|
|
using (MySqlConnection con = new MySqlConnection(connStr))
|
|
{
|
|
con.Open();
|
|
|
|
MySqlCommand cmd = new MySqlCommand("INSERT INTO test VALUES (1, '??????????')", con);
|
|
cmd.ExecuteNonQuery();
|
|
|
|
cmd = new MySqlCommand("SELECT value FROM test WHERE id = 1", con);
|
|
string firstS = cmd.ExecuteScalar().ToString();
|
|
Assert.AreEqual(originalValue, firstS);
|
|
|
|
con.Close();
|
|
con.Open();
|
|
|
|
//Does not work:
|
|
cmd = new MySqlCommand("SELECT value FROM test WHERE id = 1", con);
|
|
string secondS = cmd.ExecuteScalar().ToString();
|
|
|
|
KillConnection(con);
|
|
con.Close();
|
|
Assert.AreEqual(firstS, secondS);
|
|
}
|
|
}
|
|
|
|
#if !CF
|
|
|
|
private void PoolingWorker(object cn)
|
|
{
|
|
MySqlConnection conn = (cn as MySqlConnection);
|
|
|
|
Thread.Sleep(5000);
|
|
conn.Close();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bug #24373 High CPU utilization when no idle connection
|
|
/// </summary>
|
|
[Test]
|
|
public void MultipleThreads()
|
|
{
|
|
string connStr = GetPoolingConnectionString() + ";max pool size=1";
|
|
MySqlConnection c = new MySqlConnection(connStr);
|
|
c.Open();
|
|
|
|
ParameterizedThreadStart ts = new ParameterizedThreadStart(PoolingWorker);
|
|
Thread t = new Thread(ts);
|
|
t.Start(c);
|
|
|
|
using (MySqlConnection c2 = new MySqlConnection(connStr))
|
|
{
|
|
c2.Open();
|
|
KillConnection(c2);
|
|
}
|
|
c.Close();
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
[Test]
|
|
public void NewTest()
|
|
{
|
|
if (Version < new Version(5, 0)) return;
|
|
|
|
execSQL("CREATE TABLE Test (id INT, name VARCHAR(50))");
|
|
execSQL("CREATE PROCEDURE spTest(theid INT) BEGIN SELECT * FROM test WHERE id=theid; END");
|
|
execSQL("INSERT INTO test VALUES (1, 'First')");
|
|
execSQL("INSERT INTO test VALUES (2, 'Second')");
|
|
execSQL("INSERT INTO test VALUES (3, 'Third')");
|
|
execSQL("INSERT INTO test VALUES (4, 'Fourth')");
|
|
|
|
string connStr = GetPoolingConnectionString();
|
|
|
|
for (int i = 1; i < 5; i++)
|
|
{
|
|
using (MySqlConnection con = new MySqlConnection(connStr))
|
|
{
|
|
con.Open();
|
|
MySqlCommand reccmd = new MySqlCommand("spTest", con);
|
|
reccmd.CommandTimeout = 0;
|
|
reccmd.CommandType = CommandType.StoredProcedure;
|
|
MySqlParameter par = new MySqlParameter("@theid", MySqlDbType.String);
|
|
par.Value = i;
|
|
reccmd.Parameters.Add(par);
|
|
using (MySqlDataReader recdr = reccmd.ExecuteReader())
|
|
{
|
|
if (recdr.Read())
|
|
{
|
|
int x = recdr.GetOrdinal("name");
|
|
Assert.AreEqual(1, x);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
MySqlConnection c = new MySqlConnection(connStr);
|
|
c.Open();
|
|
KillConnection(c);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bug #29409 Bug on Open Connection with pooling=true to a MYSQL Server that is shutdown
|
|
/// </summary>
|
|
[Test]
|
|
public void ConnectAfterMaxPoolSizeTimeouts()
|
|
{
|
|
//TODO: refactor test suite to support starting/stopping services
|
|
/* string connStr = "server=localhost;uid=root;database=test;pooling=true;connect timeout=6; max pool size = 6";
|
|
MySqlConnection c = new MySqlConnection(connStr);
|
|
for (int i = 0; i < 6; i++)
|
|
{
|
|
try
|
|
{
|
|
c.Open();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
}
|
|
}
|
|
c.Open();
|
|
c.Close();*/
|
|
}
|
|
|
|
bool IsConnectionAlive(int serverThread)
|
|
{
|
|
MySqlDataAdapter da = new MySqlDataAdapter("SHOW PROCESSLIST", conn);
|
|
DataTable dt = new DataTable();
|
|
da.Fill(dt);
|
|
foreach (DataRow row in dt.Rows)
|
|
if ((long)row["Id"] == serverThread)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
[Test]
|
|
public void CleanIdleConnections()
|
|
{
|
|
string assemblyName = typeof(MySqlConnection).Assembly.FullName;
|
|
string pmName = String.Format("MySql.Data.MySqlClient.MySqlPoolManager, {0}", assemblyName);
|
|
|
|
Type poolManager = Type.GetType(pmName, false);
|
|
FieldInfo poolManagerTimerField = poolManager.GetField("timer",
|
|
BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
|
|
FieldInfo poolManagerMaxConnectionIdleTime =
|
|
poolManager.GetField ("maxConnectionIdleTime",
|
|
BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
|
|
|
|
Timer poolManagerTimer = (Timer)poolManagerTimerField.GetValue(null);
|
|
int origMaxConnectionIdleTime = (int) poolManagerMaxConnectionIdleTime.GetValue(null);
|
|
|
|
|
|
try
|
|
{
|
|
// Normally, idle connection would expire after 3 minutes and would
|
|
// be cleaned up by timer that also runs every 3 minutes.
|
|
// Since we do not want to wait that long during a unit tests,
|
|
// we use tricks.
|
|
// - temporarily reduce max.idle time for connections down to 1
|
|
// second
|
|
// - temporarily change cleanup timer to run each second.
|
|
int threadId = -1;
|
|
string connStr = GetPoolingConnectionString();
|
|
using (MySqlConnection c = new MySqlConnection(connStr))
|
|
{
|
|
c.Open();
|
|
threadId = c.ServerThread;
|
|
}
|
|
|
|
// Pooled connection should be still alive
|
|
Assert.IsTrue(IsConnectionAlive(threadId));
|
|
|
|
poolManagerMaxConnectionIdleTime.SetValue(null, 1);
|
|
poolManagerTimer.Change(1000, 1000);
|
|
|
|
// Let the idle connection expire and let cleanup timer run.
|
|
Thread.Sleep(2500);
|
|
|
|
// The connection that was pooled must be dead now
|
|
Assert.IsFalse(IsConnectionAlive(threadId));
|
|
}
|
|
finally
|
|
{
|
|
// restore values for connection idle time and timer interval
|
|
poolManagerMaxConnectionIdleTime.SetValue(null, origMaxConnectionIdleTime);
|
|
poolManagerTimer.Change(origMaxConnectionIdleTime*1000,
|
|
origMaxConnectionIdleTime*1000);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void ClearPool()
|
|
{
|
|
string connStr = GetPoolingConnectionString() + ";min pool size=10";
|
|
MySqlConnectionStringBuilder settings = new MySqlConnectionStringBuilder(connStr);
|
|
MySqlConnection[] connections = new MySqlConnection[10];
|
|
connections[0] = new MySqlConnection(connStr);
|
|
connections[0].Open();
|
|
|
|
string assemblyName = typeof(MySqlConnection).Assembly.FullName;
|
|
string pmName = String.Format("MySql.Data.MySqlClient.MySqlPoolManager, {0}", assemblyName);
|
|
|
|
Type poolManager = Type.GetType(pmName, false);
|
|
FieldInfo poolManagerHashTable = poolManager.GetField("pools",
|
|
BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
|
|
Hashtable poolHash = (Hashtable)poolManagerHashTable.GetValue(null);
|
|
FieldInfo clearingPoolsFI = poolManager.GetField("clearingPools",
|
|
BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
|
|
ICollection clearingPools = (ICollection)clearingPoolsFI.GetValue(null);
|
|
|
|
// now we need to investigate
|
|
string poolName = String.Format("MySql.Data.MySqlClient.MySqlPool, {0}", assemblyName);
|
|
Type poolType = Type.GetType(poolName, false);
|
|
|
|
FieldInfo inUsePool = poolType.GetField("inUsePool", BindingFlags.NonPublic | BindingFlags.Instance);
|
|
ICollection inUseList = (ICollection)inUsePool.GetValue(poolHash[settings.ConnectionString]);
|
|
Assert.AreEqual(1, inUseList.Count);
|
|
|
|
FieldInfo idlePool = poolType.GetField("idlePool", BindingFlags.NonPublic | BindingFlags.Instance);
|
|
ICollection idleList = (ICollection)idlePool.GetValue(poolHash[settings.ConnectionString]);
|
|
Assert.AreEqual(9, idleList.Count);
|
|
|
|
// now open 4 more of these. Now we shoudl have 5 open and five
|
|
// still in the pool
|
|
for (int i = 1; i < 5; i++)
|
|
{
|
|
connections[i] = new MySqlConnection(connStr);
|
|
connections[i].Open();
|
|
}
|
|
|
|
Assert.AreEqual(5, inUseList.Count);
|
|
Assert.AreEqual(5, idleList.Count);
|
|
|
|
Assert.AreEqual(0, clearingPools.Count);
|
|
// now tell this connection to clear its pool
|
|
MySqlConnection.ClearPool(connections[0]);
|
|
Assert.AreEqual(1, clearingPools.Count);
|
|
Assert.AreEqual(0, idleList.Count);
|
|
|
|
for (int i = 0; i < 5; i++)
|
|
connections[i].Close();
|
|
Assert.AreEqual(0, clearingPools.Count);
|
|
}
|
|
|
|
[Test]
|
|
public void TestBadConnections()
|
|
{
|
|
MySqlConnectionStringBuilder builder = new
|
|
MySqlConnectionStringBuilder();
|
|
|
|
builder.Pooling = true;
|
|
builder.Server = "xxxxxxxx"; // one that definitely does not exist.
|
|
builder.UserID = "whoever";
|
|
builder.Password = "whatever";
|
|
|
|
int numberOfConnections = 1;
|
|
|
|
for (int i = 0; i < numberOfConnections; ++i)
|
|
{
|
|
using (MySqlConnection connection = new MySqlConnection(builder.ConnectionString))
|
|
{
|
|
try
|
|
{
|
|
connection.Open();
|
|
Assert.Fail("Connection should throw an exception.");
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
}
|
|
Thread.Sleep(50);
|
|
}
|
|
MySqlConnection.ClearAllPools();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bug #42801 ClearPool .Net connector : NullReferenceException
|
|
/// </summary>
|
|
[Test]
|
|
public void DoubleClearingConnectionPool()
|
|
{
|
|
MySqlConnection c1 = new MySqlConnection(GetConnectionString(true));
|
|
MySqlConnection c2 = new MySqlConnection(GetConnectionString(true));
|
|
c1.Open();
|
|
c2.Open();
|
|
c1.Close();
|
|
c2.Close();
|
|
MySqlConnection.ClearPool(c1);
|
|
MySqlConnection.ClearPool(c2);
|
|
}
|
|
|
|
}
|
|
}
|
|
|