Ejemplo n.º 1
0
        private async Task <MySqlSession> CreateSessionAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            var connectTimeout = m_connectionSettings.ConnectionTimeout == 0 ? Timeout.InfiniteTimeSpan : TimeSpan.FromSeconds(checked ((int)m_connectionSettings.ConnectionTimeout));

            using (var timeoutSource = new CancellationTokenSource(connectTimeout))
                using (var linkedSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutSource.Token))
                {
                    try
                    {
                        // get existing session from the pool if possible
                        if (m_connectionSettings.Pooling)
                        {
                            var pool = ConnectionPool.GetPool(m_connectionSettings);
                            // this returns an open session
                            return(await pool.GetSessionAsync(ioBehavior, linkedSource.Token).ConfigureAwait(false));
                        }
                        else
                        {
                            var session = new MySqlSession();
                            await session.ConnectAsync(m_connectionSettings, ioBehavior, linkedSource.Token).ConfigureAwait(false);

                            return(session);
                        }
                    }
                    catch (OperationCanceledException ex) when(timeoutSource.IsCancellationRequested)
                    {
                        throw new MySqlException("Connect Timeout expired.", ex);
                    }
                }
        }
Ejemplo n.º 2
0
        internal static DbTable <T> MockTempTable <T>(this MySqlSession mySqlSession, IList <MySqlCommand> commands, T fromModel = null, Action <T> initializer = null)
            where T : Model, new()
        {
            var result = mySqlSession.MockTempTable <T>(fromModel, initializer);

            commands.Add(mySqlSession.GetCreateTableCommand(result.Model, true));
            return(result);
        }
Ejemplo n.º 3
0
 public bool Return(MySqlSession session)
 {
     lock (m_lock)
     {
         // TODO: Dispose oldest connections in the pool first?
         if (m_sessions.Count >= m_maximumSize)
             return false;
         m_sessions.Enqueue(session);
         return true;
     }
 }
Ejemplo n.º 4
0
 public bool Return(MySqlSession session)
 {
     lock (m_lock)
     {
         // TODO: Dispose oldest connections in the pool first?
         if (m_sessions.Count >= m_maximumSize)
         {
             return(false);
         }
         m_sessions.Enqueue(session);
         return(true);
     }
 }
Ejemplo n.º 5
0
        public async Task <MySqlSession> GetSessionAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            // wait for an open slot
            if (ioBehavior == IOBehavior.Asynchronous)
            {
                await m_session_semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
            }
            else
            {
                m_session_semaphore.Wait(cancellationToken);
            }

            try
            {
                MySqlSession session;
                // check for a pooled session
                if (m_sessions.TryDequeue(out session))
                {
                    if (session.PoolGeneration != m_generation || !await session.TryPingAsync(ioBehavior, cancellationToken).ConfigureAwait(false))
                    {
                        // session is either old or cannot communicate with the server
                        await session.DisposeAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
                    }
                    else
                    {
                        // session is valid, reset if supported
                        if (m_resetConnections)
                        {
                            await session.ResetConnectionAsync(m_userId, m_password, m_database, ioBehavior, cancellationToken).ConfigureAwait(false);
                        }
                        // pooled session is ready to be used; return it
                        return(session);
                    }
                }

                session = new MySqlSession(this, m_generation);
                await session.ConnectAsync(m_servers, m_port, m_userId, m_password, m_database, ioBehavior, cancellationToken).ConfigureAwait(false);

                return(session);
            }
            catch
            {
                m_session_semaphore.Release();
                throw;
            }
        }
Ejemplo n.º 6
0
 public void Return(MySqlSession session)
 {
     try
     {
         if (session.PoolGeneration == m_generation)
         {
             m_sessions.Enqueue(session);
         }
         else
         {
             session.DisposeAsync(IOBehavior.Synchronous, CancellationToken.None).ConfigureAwait(false);
         }
     }
     finally
     {
         m_session_semaphore.Release();
     }
 }
        public bool Return(MySqlSession session)
        {
            try
            {
                m_semaphore.Wait();

                // TODO: Dispose oldest connections in the pool first?
                if (m_sessions.Count >= m_maximumSize)
                {
                    return(false);
                }
                m_sessions.Enqueue(session);
                return(true);
            }
            finally
            {
                m_semaphore.Release();
            }
        }
        private static async Task <bool> TryPingAsync(MySqlSession session, CancellationToken cancellationToken)
        {
            await session.SendAsync(PingPayload.Create(), cancellationToken).ConfigureAwait(false);

            try
            {
                var payload = await session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false);

                OkPayload.Create(payload);
                return(true);
            }
            catch (EndOfStreamException)
            {
            }
            catch (SocketException)
            {
            }

            return(false);
        }
Ejemplo n.º 9
0
 public void Return(MySqlSession session)
 {
     try
     {
         lock (m_leasedSessions)
             m_leasedSessions.Remove(session.Id);
         if (SessionIsHealthy(session))
         {
             lock (m_sessions)
                 m_sessions.AddFirst(session);
         }
         else
         {
             session.DisposeAsync(IOBehavior.Synchronous, CancellationToken.None).ConfigureAwait(false);
         }
     }
     finally
     {
         m_sessionSemaphore.Release();
     }
 }
Ejemplo n.º 10
0
        private bool SessionIsHealthy(MySqlSession session)
        {
            if (!session.IsConnected)
            {
                return(false);
            }
            if (session.PoolGeneration != m_generation)
            {
                return(false);
            }
            if (session.DatabaseOverride != null)
            {
                return(false);
            }
            if (m_connectionSettings.ConnectionLifeTime > 0 &&
                (DateTime.UtcNow - session.CreatedUtc).TotalSeconds >= m_connectionSettings.ConnectionLifeTime)
            {
                return(false);
            }

            return(true);
        }
Ejemplo n.º 11
0
        private async Task CleanPoolAsync(IOBehavior ioBehavior, Func <MySqlSession, bool> shouldCleanFn, bool respectMinPoolSize, CancellationToken cancellationToken)
        {
            // synchronize access to this method as only one clean routine should be run at a time
            if (ioBehavior == IOBehavior.Asynchronous)
            {
                await m_cleanSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
            }
            else
            {
                m_cleanSemaphore.Wait(cancellationToken);
            }

            try
            {
                var waitTimeout = TimeSpan.FromMilliseconds(10);
                while (true)
                {
                    // if respectMinPoolSize is true, return if (leased sessions + waiting sessions <= minPoolSize)
                    if (respectMinPoolSize)
                    {
                        lock (m_sessions)
                            if (m_connectionSettings.MaximumPoolSize - m_sessionSemaphore.CurrentCount + m_sessions.Count <= m_connectionSettings.MinimumPoolSize)
                            {
                                return;
                            }
                    }

                    // try to get an open slot; if this fails, connection pool is full and sessions will be disposed when returned to pool
                    if (ioBehavior == IOBehavior.Asynchronous)
                    {
                        if (!await m_sessionSemaphore.WaitAsync(waitTimeout, cancellationToken).ConfigureAwait(false))
                        {
                            return;
                        }
                    }
                    else
                    {
                        if (!m_sessionSemaphore.Wait(waitTimeout, cancellationToken))
                        {
                            return;
                        }
                    }

                    try
                    {
                        // check for a waiting session
                        MySqlSession session = null;
                        lock (m_sessions)
                        {
                            if (m_sessions.Count > 0)
                            {
                                session = m_sessions.Last.Value;
                                m_sessions.RemoveLast();
                            }
                        }
                        if (session == null)
                        {
                            return;
                        }

                        if (shouldCleanFn(session))
                        {
                            // session should be cleaned; dispose it and keep iterating
                            await session.DisposeAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
                        }
                        else
                        {
                            // session should not be cleaned; put it back in the queue and stop iterating
                            lock (m_sessions)
                                m_sessions.AddLast(session);
                            return;
                        }
                    }
                    finally
                    {
                        m_sessionSemaphore.Release();
                    }
                }
            }
            finally
            {
                m_cleanSemaphore.Release();
            }
        }
Ejemplo n.º 12
0
        public async Task <MySqlSession> GetSessionAsync(MySqlConnection connection, IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            // if all sessions are used, see if any have been leaked and can be recovered
            // check at most once per second (although this isn't enforced via a mutex so multiple threads might block
            // on the lock in RecoverLeakedSessions in high-concurrency situations
            if (m_sessionSemaphore.CurrentCount == 0 && unchecked (((uint)Environment.TickCount) - m_lastRecoveryTime) >= 1000u)
            {
                RecoverLeakedSessions();
            }

            // wait for an open slot (until the cancellationToken is cancelled, which is typically due to timeout)
            if (ioBehavior == IOBehavior.Asynchronous)
            {
                await m_sessionSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
            }
            else
            {
                m_sessionSemaphore.Wait(cancellationToken);
            }

            try
            {
                // check for a waiting session
                MySqlSession session = null;
                lock (m_sessions)
                {
                    if (m_sessions.Count > 0)
                    {
                        session = m_sessions.First.Value;
                        m_sessions.RemoveFirst();
                    }
                }
                if (session != null)
                {
                    bool reuseSession;

                    if (session.PoolGeneration != m_generation)
                    {
                        reuseSession = false;
                    }
                    else
                    {
                        if (m_connectionSettings.ConnectionReset)
                        {
                            reuseSession = await session.TryResetConnectionAsync(m_connectionSettings, ioBehavior, cancellationToken).ConfigureAwait(false);
                        }
                        else
                        {
                            reuseSession = await session.TryPingAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
                        }
                    }

                    if (!reuseSession)
                    {
                        // session is either old or cannot communicate with the server
                        await session.DisposeAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
                    }
                    else
                    {
                        // pooled session is ready to be used; return it
                        session.OwningConnection = new WeakReference <MySqlConnection>(connection);
                        lock (m_leasedSessions)
                            m_leasedSessions.Add(session.Id, session);
                        return(session);
                    }
                }

                // create a new session
                session = new MySqlSession(this, m_generation, Interlocked.Increment(ref m_lastId));
                await session.ConnectAsync(m_connectionSettings, ioBehavior, cancellationToken).ConfigureAwait(false);

                session.OwningConnection = new WeakReference <MySqlConnection>(connection);
                lock (m_leasedSessions)
                    m_leasedSessions.Add(session.Id, session);
                return(session);
            }
            catch
            {
                m_sessionSemaphore.Release();
                throw;
            }
        }
Ejemplo n.º 13
0
 internal static DbTable <T> MockTempTable <T>(this MySqlSession mySqlSession, T fromModel = null, Action <T> initializer = null)
     where T : Model, new()
 {
     return(mySqlSession.CreateTempTableInstance(fromModel, initializer));
 }
        public override async Task OpenAsync(CancellationToken cancellationToken)
        {
            VerifyNotDisposed();
            if (State != ConnectionState.Closed)
            {
                throw new InvalidOperationException("Cannot Open when State is {0}.".FormatInvariant(State));
            }
#if !NETSTANDARD1_3
            if (System.Transactions.Transaction.Current != null)
            {
                throw new NotSupportedException("Ambient transactions are not supported. Use BeginTransaction instead.");
            }
#endif

            SetState(ConnectionState.Connecting);

            bool success = false;
            try
            {
                // get existing session from the pool if possible
                var pool = ConnectionPool.GetPool(m_connectionStringBuilder);
                m_session = pool == null ? null : await pool.TryGetSessionAsync(cancellationToken).ConfigureAwait(false);

                if (m_session != null)
                {
                    // test that session is still valid and (optionally) reset it
                    if (!await TryPingAsync(m_session, cancellationToken).ConfigureAwait(false))
                    {
                        Utility.Dispose(ref m_session);
                    }
                    else if (m_connectionStringBuilder.ConnectionReset)
                    {
                        await ResetConnectionAsync(cancellationToken).ConfigureAwait(false);
                    }
                }

                if (m_session == null)
                {
                    m_session = new MySqlSession(pool);
                    var connected = await m_session.ConnectAsync(m_connectionStringBuilder.Server.Split(','), (int)m_connectionStringBuilder.Port).ConfigureAwait(false);

                    if (!connected)
                    {
                        SetState(ConnectionState.Closed);
                        throw new MySqlException("Unable to connect to any of the specified MySQL hosts.");
                    }

                    var payload = await m_session.ReceiveAsync(cancellationToken).ConfigureAwait(false);

                    var reader           = new ByteArrayReader(payload.ArraySegment.Array, payload.ArraySegment.Offset, payload.ArraySegment.Count);
                    var initialHandshake = new InitialHandshakePacket(reader);
                    if (initialHandshake.AuthPluginName != "mysql_native_password")
                    {
                        throw new NotSupportedException("Only 'mysql_native_password' authentication method is supported.");
                    }
                    m_session.ServerVersion  = new ServerVersion(Encoding.ASCII.GetString(initialHandshake.ServerVersion));
                    m_session.AuthPluginData = initialHandshake.AuthPluginData;

                    var response = HandshakeResponse41Packet.Create(initialHandshake, m_connectionStringBuilder.UserID, m_connectionStringBuilder.Password, m_database);
                    payload = new PayloadData(new ArraySegment <byte>(response));
                    await m_session.SendReplyAsync(payload, cancellationToken).ConfigureAwait(false);

                    await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false);

                    // TODO: Check success
                }

                m_hasBeenOpened = true;
                SetState(ConnectionState.Open);
                success = true;
            }
            catch (MySqlException)
            {
                SetState(ConnectionState.Closed);
                throw;
            }
            catch (SocketException ex)
            {
                SetState(ConnectionState.Closed);
                throw new MySqlException("Unable to connect to any of the specified MySQL hosts.", ex);
            }
            finally
            {
                if (!success)
                {
                    Utility.Dispose(ref m_session);
                }
            }
        }
Ejemplo n.º 15
0
		public override async Task OpenAsync(CancellationToken cancellationToken)
		{
			VerifyNotDisposed();
			if (State != ConnectionState.Closed)
				throw new InvalidOperationException("Cannot Open when State is {0}.".FormatInvariant(State));
#if !NETSTANDARD1_3
			if (System.Transactions.Transaction.Current != null)
				throw new NotSupportedException("Ambient transactions are not supported. Use BeginTransaction instead.");
#endif

			if (m_connectionStringBuilder.UseCompression)
				throw new NotSupportedException("Compression not supported.");

			SetState(ConnectionState.Connecting);

			bool success = false;
			try
			{
				var pool = ConnectionPool.GetPool(m_connectionStringBuilder);
				m_session = pool?.TryGetSession();
				if (m_session == null)
				{
					m_session = new MySqlSession(pool);
					var connected = await m_session.ConnectAsync(m_connectionStringBuilder.Server.Split(','), (int) m_connectionStringBuilder.Port).ConfigureAwait(false);
					if (!connected)
					{
						SetState(ConnectionState.Closed);
						throw new MySqlException("Unable to connect to any of the specified MySQL hosts.");
					}

					var payload = await m_session.ReceiveAsync(cancellationToken).ConfigureAwait(false);
					var reader = new ByteArrayReader(payload.ArraySegment.Array, payload.ArraySegment.Offset, payload.ArraySegment.Count);
					var initialHandshake = new InitialHandshakePacket(reader);
					if (initialHandshake.AuthPluginName != "mysql_native_password")
						throw new NotSupportedException("Only 'mysql_native_password' authentication method is supported.");
					m_session.ServerVersion = new ServerVersion(Encoding.ASCII.GetString(initialHandshake.ServerVersion));
					m_session.AuthPluginData = initialHandshake.AuthPluginData;

					var response = HandshakeResponse41Packet.Create(initialHandshake, m_connectionStringBuilder.UserID, m_connectionStringBuilder.Password, m_database);
					payload = new PayloadData(new ArraySegment<byte>(response));
					await m_session.SendReplyAsync(payload, cancellationToken).ConfigureAwait(false);
					await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false);
					// TODO: Check success
				}
				else if (m_connectionStringBuilder.ConnectionReset)
				{
					if (m_session.ServerVersion.Version.CompareTo(ServerVersions.SupportsResetConnection) >= 0)
					{
						await m_session.SendAsync(ResetConnectionPayload.Create(), cancellationToken).ConfigureAwait(false);
						var payload = await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false);
						OkPayload.Create(payload);
					}
					else
					{
						// optimistically hash the password with the challenge from the initial handshake (supported by MariaDB; doesn't appear to be supported by MySQL)
						var hashedPassword = AuthenticationUtility.HashPassword(m_session.AuthPluginData, 0, m_connectionStringBuilder.Password);
						var payload = ChangeUserPayload.Create(m_connectionStringBuilder.UserID, hashedPassword, m_database);
						await m_session.SendAsync(payload, cancellationToken).ConfigureAwait(false);
						payload = await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false);
						if (payload.HeaderByte == AuthenticationMethodSwitchRequestPayload.Signature)
						{
							// if the server didn't support the hashed password; rehash with the new challenge
							var switchRequest = AuthenticationMethodSwitchRequestPayload.Create(payload);
							if (switchRequest.Name != "mysql_native_password")
								throw new NotSupportedException("Only 'mysql_native_password' authentication method is supported.");
							hashedPassword = AuthenticationUtility.HashPassword(switchRequest.Data, 0, m_connectionStringBuilder.Password);
							payload = new PayloadData(new ArraySegment<byte>(hashedPassword));
							await m_session.SendReplyAsync(payload, cancellationToken).ConfigureAwait(false);
							payload = await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false);
						}
						OkPayload.Create(payload);
					}
				}

				m_hasBeenOpened = true;
				SetState(ConnectionState.Open);
				success = true;
			}
			catch (MySqlException)
			{
				SetState(ConnectionState.Closed);
				throw;
			}
			catch (SocketException ex)
			{
				SetState(ConnectionState.Closed);
				throw new MySqlException("Unable to connect to any of the specified MySQL hosts.", ex);
			}
			finally
			{
				if (!success)
					Utility.Dispose(ref m_session);
			}
		}
Ejemplo n.º 16
0
        public override async Task OpenAsync(CancellationToken cancellationToken)
        {
            VerifyNotDisposed();
            if (State != ConnectionState.Closed)
            {
                throw new InvalidOperationException("Cannot Open when State is {0}.".FormatInvariant(State));
            }
#if !NETSTANDARD1_3
            if (System.Transactions.Transaction.Current != null)
            {
                throw new NotSupportedException("Ambient transactions are not supported. Use BeginTransaction instead.");
            }
#endif

            if (m_connectionStringBuilder.UseCompression)
            {
                throw new NotSupportedException("Compression not supported.");
            }

            SetState(ConnectionState.Connecting);

            bool success = false;
            try
            {
                var pool = ConnectionPool.GetPool(m_connectionStringBuilder);
                m_session = pool?.TryGetSession();
                if (m_session == null)
                {
                    m_session = new MySqlSession(pool);
                    var connected = await m_session.ConnectAsync(m_connectionStringBuilder.Server.Split(','), (int)m_connectionStringBuilder.Port).ConfigureAwait(false);

                    if (!connected)
                    {
                        SetState(ConnectionState.Closed);
                        throw new MySqlException("Unable to connect to any of the specified MySQL hosts.");
                    }

                    var payload = await m_session.ReceiveAsync(cancellationToken).ConfigureAwait(false);

                    var reader           = new ByteArrayReader(payload.ArraySegment.Array, payload.ArraySegment.Offset, payload.ArraySegment.Count);
                    var initialHandshake = new InitialHandshakePacket(reader);
                    if (initialHandshake.AuthPluginName != "mysql_native_password")
                    {
                        throw new NotSupportedException("Only 'mysql_native_password' authentication method is supported.");
                    }
                    m_session.ServerVersion = new ServerVersion(Encoding.ASCII.GetString(initialHandshake.ServerVersion));

                    var response = HandshakeResponse41Packet.Create(initialHandshake, m_connectionStringBuilder.UserID, m_connectionStringBuilder.Password, m_database);
                    payload = new PayloadData(new ArraySegment <byte>(response));
                    await m_session.SendReplyAsync(payload, cancellationToken).ConfigureAwait(false);

                    await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false);

                    // TODO: Check success
                }
                else if (m_connectionStringBuilder.ConnectionReset)
                {
                    if (m_session.ServerVersion.Version.CompareTo(ServerVersions.SupportsResetConnection) >= 0)
                    {
                        await m_session.SendAsync(ResetConnectionPayload.Create(), cancellationToken).ConfigureAwait(false);

                        var payload = await m_session.ReceiveReplyAsync(cancellationToken);

                        OkPayload.Create(payload);
                    }
                    else
                    {
                        // MySQL doesn't appear to accept a replayed hashed password (using the challenge from the initial handshake), so just send zeroes
                        // and expect to get a new challenge
                        var payload = ChangeUserPayload.Create(m_connectionStringBuilder.UserID, new byte[20], m_database);
                        await m_session.SendAsync(payload, cancellationToken).ConfigureAwait(false);

                        payload = await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false);

                        var switchRequest = AuthenticationMethodSwitchRequestPayload.Create(payload);
                        if (switchRequest.Name != "mysql_native_password")
                        {
                            throw new NotSupportedException("Only 'mysql_native_password' authentication method is supported.");
                        }
                        var hashedPassword = AuthenticationUtility.HashPassword(switchRequest.Data, 0, m_connectionStringBuilder.Password);
                        payload = new PayloadData(new ArraySegment <byte>(hashedPassword));
                        await m_session.SendReplyAsync(payload, cancellationToken).ConfigureAwait(false);

                        payload = await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false);

                        OkPayload.Create(payload);
                    }
                }

                m_hasBeenOpened = true;
                SetState(ConnectionState.Open);
                success = true;
            }
            catch (MySqlException)
            {
                SetState(ConnectionState.Closed);
                throw;
            }
            catch (SocketException ex)
            {
                SetState(ConnectionState.Closed);
                throw new MySqlException("Unable to connect to any of the specified MySQL hosts.", ex);
            }
            finally
            {
                if (!success)
                {
                    Utility.Dispose(ref m_session);
                }
            }
        }