@@ -246,22 +246,35 @@ public void Dispose()
246246 /// </summary>
247247 private async Task RecoverLeakedSessionsAsync ( IOBehavior ioBehavior )
248248 {
249- var recoveredSessions = new List < ServerSession > ( ) ;
249+ var recoveredSessions = new List < ( ServerSession Session , MySqlConnection Connection ) > ( ) ;
250250 lock ( m_leasedSessions )
251251 {
252252 m_lastRecoveryTime = unchecked ( ( uint ) Environment . TickCount ) ;
253253 foreach ( var session in m_leasedSessions . Values )
254254 {
255255 if ( ! session . OwningConnection ! . TryGetTarget ( out var _ ) )
256- recoveredSessions . Add ( session ) ;
256+ {
257+ // create a dummy MySqlConnection so that any thread running RecoverLeakedSessionsAsync doesn't process this one
258+ var connection = new MySqlConnection ( ) ;
259+ session . OwningConnection = new ( connection ) ;
260+ recoveredSessions . Add ( ( session , connection ) ) ;
261+ }
257262 }
258263 }
259264 if ( recoveredSessions . Count == 0 )
260265 Log . Trace ( "Pool{0} recovered no sessions" , m_logArguments ) ;
261266 else
262267 Log . Warn ( "Pool{0}: RecoveredSessionCount={1}" , m_logArguments [ 0 ] , recoveredSessions . Count ) ;
263- foreach ( var session in recoveredSessions )
268+
269+ foreach ( var ( session , connection ) in recoveredSessions )
270+ {
271+ // bypass MySqlConnection.Dispose(Async), because it's a dummy MySqlConnection that's not set up
272+ // properly, and simply return the session to the pool directly
264273 await session . ReturnToPoolAsync ( ioBehavior , null ) . ConfigureAwait ( false ) ;
274+
275+ // be explicit about keeping the associated MySqlConnection alive until the session has been returned
276+ GC . KeepAlive ( connection ) ;
277+ }
265278 }
266279
267280 private async Task CleanPoolAsync ( IOBehavior ioBehavior , Func < ServerSession , bool > shouldCleanFn , bool respectMinPoolSize , CancellationToken cancellationToken )
0 commit comments