From: Date: February 22 2007 9:52pm Subject: Connector/NET commit: r606 - in branches/1.0: . TestSuite mysqlclient List-Archive: http://lists.mysql.com/commits/20420 X-Bug: 24373 Message-Id: <200702222052.l1MKqSsc029747@bk-internal.mysql.com> Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modified: branches/1.0/CHANGES branches/1.0/TestSuite/PoolingTests.cs branches/1.0/mysqlclient/MySql.Data.csproj branches/1.0/mysqlclient/MySqlPool.cs branches/1.0/mysqlclient/Resources.Designer.cs branches/1.0/mysqlclient/Resources.resx Log: Bug #24373 High CPU utilization when no idle connection Fixed this and improved the overall pooling code by using a semaphore to control access to the pooled connections. Modified: branches/1.0/CHANGES =================================================================== --- branches/1.0/CHANGES 2007-02-22 20:06:20 UTC (rev 605) +++ branches/1.0/CHANGES 2007-02-22 20:52:27 UTC (rev 606) @@ -2,12 +2,12 @@ Bugs ---- Bug #25605 BINARY and VARBINARY is returned as a string + Bug #24373 High CPU utilization when no idle connection Other ----- Added "Use Procedure Bodies" connection string option to allow calling procedures without using procedure metadata (if possible). - Version 1.0.9 Modified: branches/1.0/TestSuite/PoolingTests.cs =================================================================== --- branches/1.0/TestSuite/PoolingTests.cs 2007-02-22 20:06:20 UTC (rev 605) +++ branches/1.0/TestSuite/PoolingTests.cs 2007-02-22 20:52:27 UTC (rev 606) @@ -224,6 +224,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file Modified: branches/1.0/mysqlclient/MySql.Data.csproj =================================================================== --- branches/1.0/mysqlclient/MySql.Data.csproj 2007-02-22 20:06:20 UTC (rev 605) +++ branches/1.0/mysqlclient/MySql.Data.csproj 2007-02-22 20:52:27 UTC (rev 606) @@ -273,6 +273,11 @@ BuildAction = "Compile" /> + = 0; i--) - { - Driver d = (inUsePool[i] as Driver); - if (! d.Ping()) - { - inUsePool.RemoveAt(i); - freed++; - } - } - } - return freed; - } - */ - private Driver CheckoutConnection() + /// + /// It is assumed that this property will only be used from inside an active + /// lock. + /// + private bool HasIdleConnections { - lock (idlePool.SyncRoot) + get { return idlePool.Count > 0; } + } + + /// + /// It is assumed that this property will only be used from inside an active + /// lock. + /// + private bool HasRoomForConnections + { + get { - if (idlePool.Count == 0) return null; - Driver driver = (Driver)idlePool.Dequeue(); + if ((inUsePool.Count + idlePool.Count) == maxSize) + return false; + return true; + } + } - // if the user asks us to ping/reset pooled connections - // do so now - if (settings.ResetPooledConnections) - { - if (!driver.Ping()) - { - driver.Close(); - return null; - } - driver.Reset(); - } + #endregion - lock (inUsePool.SyncRoot) + private Driver CheckoutConnection() + { + Driver driver = (Driver)idlePool.Dequeue(); + + // if the user asks us to ping/reset pooled connections + // do so now + if (settings.ResetPooledConnections) + { + if (!driver.Ping()) { - inUsePool.Add(driver); + driver.Close(); + return null; } - return driver; + driver.Reset(); } + + inUsePool.Add(driver); + + return driver; } + /// + /// It is assumed that this method is only called from inside an active lock. + /// private Driver GetPooledConnection() { - while (true) - { - if (idlePool.Count > 0) - return CheckoutConnection(); - - // if idlepool == 0 and inusepool == max, then we can't create a new one - if (inUsePool.Count == maxSize) - return null; - + // if we don't have an idle connection but we have room for a new + // one, then create it here. + if (!HasIdleConnections) CreateNewPooledConnection(); - } + + return CheckoutConnection(); } + /// + /// It is assumed that this method is only called from inside an active lock. + /// private void CreateNewPooledConnection() { - lock (idlePool.SyncRoot) - lock (inUsePool.SyncRoot) - { - // first we check if we are allowed to create another - if ((inUsePool.Count + idlePool.Count) == maxSize) - return; - - Driver driver = Driver.Create(settings); - - idlePool.Enqueue(driver); - } + Driver driver = Driver.Create(settings); + idlePool.Enqueue(driver); } public void ReleaseConnection(Driver driver) { - lock (idlePool.SyncRoot) - lock (inUsePool.SyncRoot) - { - inUsePool.Remove(driver); + lock (lockObject) + { + if (!inUsePool.Contains(driver)) + return; - if (driver.IsTooOld && driver.IsOpen) - driver.Close(); + inUsePool.Remove(driver); + if (driver.IsTooOld) + driver.Close(); + else + idlePool.Enqueue(driver); - if (!NeedConnections) return; - - if (!driver.IsOpen) - driver.Open(); - idlePool.Enqueue(driver); - } + // we now either have a connection available or have room to make + // one so we release one slot in our semaphore + poolGate.Release(); + } } - public Driver GetConnection() + public Driver GetConnection() { - Driver driver = null; + int ticks = (int)settings.ConnectionTimeout * 1000; - int start = Environment.TickCount; - int ticks = settings.ConnectionTimeout * 1000; + // wait till we are allowed in + bool allowed = poolGate.WaitOne(ticks, false); + if (! allowed) + throw new MySqlException(Resources.TimeoutGettingConnection); - // wait timeOut seconds at most to get a connection - while (driver == null && (Environment.TickCount - start) < ticks) - driver = GetPooledConnection(); - - // if pool size is at maximum, then we must have reached our timeout so we simply - // throw our exception - if (driver == null) - throw new MySqlException("error connecting: Timeout expired. The timeout period elapsed " + - "prior to obtaining a connection from the pool. This may have occurred because all " + - "pooled connections were in use and max pool size was reached."); - - return driver; + // if we get here, then it means that we either have an idle connection + // or room to make a new connection + lock (lockObject) + { + return GetPooledConnection(); + } } - } } Modified: branches/1.0/mysqlclient/Resources.Designer.cs =================================================================== --- branches/1.0/mysqlclient/Resources.Designer.cs 2007-02-22 20:06:20 UTC (rev 605) +++ branches/1.0/mysqlclient/Resources.Designer.cs 2007-02-22 20:52:27 UTC (rev 606) @@ -404,6 +404,14 @@ } } + internal static string TimeoutGettingConnection + { + get + { + return ResourceManager.GetString("TimeoutGettingConnection", resourceCulture); + } + } + internal static string UnableToConnectToHost { get Modified: branches/1.0/mysqlclient/Resources.resx =================================================================== --- branches/1.0/mysqlclient/Resources.resx 2007-02-22 20:06:20 UTC (rev 605) +++ branches/1.0/mysqlclient/Resources.resx 2007-02-22 20:52:27 UTC (rev 606) @@ -255,4 +255,7 @@ When calling stored procedures and 'Use Procedure Bodies' is false, all parameters must have their type explicitly set. + + error connecting: Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached. + \ No newline at end of file