示例#1
0
    async def test_connection_context_create_connection_if_context_returns_with_exception(
            self, event_loop, mocker, not_closed_connection):
        # Checks that if a connection returns with an exception, if there are waiters a new connection is created.
        create_protocol = mocker.patch(
            "emcache.connection_pool.create_protocol",
            CoroutineMock(
                side_effect=[not_closed_connection, not_closed_connection]),
        )

        connection_pool = ConnectionPool("localhost", 11211, 1, 1, None, None,
                                         lambda _: _, False, False, None)

        async def coro(connection_context, raise_exception=False):
            async with connection_context as _:
                if raise_exception:
                    raise Exception()

        # Try to get a connection many times. The first one will
        # get and use the connection_closed, when this connection is
        # returned back to the pool, a new one will be created
        connection_context1 = connection_pool.create_connection_context()
        connection_context2 = connection_pool.create_connection_context()
        connection_context3 = connection_pool.create_connection_context()

        task1 = event_loop.create_task(coro(connection_context1))
        task2 = event_loop.create_task(coro(connection_context2))

        # Remember that due to the LIFO nature, we have to raise the exception using the last
        # pushed connection context
        task3 = event_loop.create_task(
            coro(connection_context3, raise_exception=True))

        await asyncio.gather(task1, task2, task3, return_exceptions=True)

        # check that we have called the create_protocol twice
        create_protocol.assert_has_calls([
            call("localhost",
                 11211,
                 ssl=False,
                 ssl_verify=False,
                 ssl_extra_ca=None,
                 timeout=None),
            call("localhost",
                 11211,
                 ssl=False,
                 ssl_verify=False,
                 ssl_extra_ca=None,
                 timeout=None),
        ])
        assert connection_pool.total_connections == 1

        # test specific atributes of the metrics
        assert connection_pool.metrics().connections_closed == 1
        assert connection_pool.metrics().operations_executed_with_error == 1
        assert connection_pool.metrics().operations_executed == 2
示例#2
0
    async def test_metrics_create_connection_latencies(self):
        # Check that the latencies measured are well calculated when metrics
        # method is called.
        connection_pool = ConnectionPool("localhost", 11211, 1, 1, None, None,
                                         lambda _: _, False, False, None)
        connection_pool._create_connection_latencies = [
            float(i) for i in range(0, 101)
        ]

        assert connection_pool.metrics().create_connection_avg == 50.0
        assert connection_pool.metrics().create_connection_p50 == 50.0
        assert connection_pool.metrics().create_connection_p99 == 99.0
        assert connection_pool.metrics().create_connection_upper == 100.0

        await connection_pool.close()
示例#3
0
    async def test_create_connection_tries_again_if_error(
            self, event_loop, mocker, not_closed_connection, exc):
        # If there is an error trying to acquire a connection and there are waiters waiting we will keep
        # trying to create a connection

        # Avoid the annoying sleep added by the backoff
        mocker.patch("emcache.connection_pool.asyncio.sleep", CoroutineMock())

        # First we return a Timeout and second try returns an usable connection
        create_protocol = mocker.patch(
            "emcache.connection_pool.create_protocol",
            CoroutineMock(side_effect=[exc, not_closed_connection]),
        )

        connection_pool = ConnectionPool("localhost", 11211, 1, 1, None, 1.0,
                                         lambda _: _, False, False, None)

        async def coro(connection_context):
            async with connection_context as _:
                pass

        connection_context = connection_pool.create_connection_context()

        # wait for a new connection available in another task
        task = event_loop.create_task(coro(connection_context))

        await task

        # check that we have called twice the create_protocol
        create_protocol.assert_has_calls([
            call("localhost",
                 11211,
                 ssl=False,
                 ssl_verify=False,
                 ssl_extra_ca=None,
                 timeout=1.0),
            call("localhost",
                 11211,
                 ssl=False,
                 ssl_verify=False,
                 ssl_extra_ca=None,
                 timeout=1.0),
        ])

        # test specific atributes of the metrics
        assert connection_pool.metrics().cur_connections == 1
        assert connection_pool.metrics().connections_created == 1
        assert connection_pool.metrics().connections_created_with_error == 1
示例#4
0
    async def test_metrics(self):
        # Just test that the type returned is the right one and there is a method
        # published. Specific attribute value are tested in the other tests which are
        # not specific to metrics but they are simulating most of the situations that
        # are needed for incrementing the counters.
        connection_pool = ConnectionPool("localhost", 11211, 1, 1, None, None,
                                         lambda _: _, False, False, None)
        assert isinstance(connection_pool.metrics(), ConnectionPoolMetrics)

        # checks that without latencies create connection times are None values
        assert connection_pool.metrics().create_connection_avg is None
        assert connection_pool.metrics().create_connection_p50 is None
        assert connection_pool.metrics().create_connection_p99 is None
        assert connection_pool.metrics().create_connection_upper is None

        await connection_pool.close()
示例#5
0
    async def test_waiters_LIFO(self, event_loop, mocker,
                                not_closed_connection):
        # Check that waiters queue are seen as LIFO queue, where we try to rescue the latency
        # of the last ones.
        mocker.patch("emcache.connection_pool.create_protocol",
                     CoroutineMock(return_value=not_closed_connection))

        connection_pool = ConnectionPool("localhost", 11211, 1, 1, None, None,
                                         lambda _: _, False, False, None)

        waiters_woken_up = []

        async def coro(connection_context):
            async with connection_context as _:
                waiters_woken_up.append(connection_context)

        # Try to get a connection many times.
        connection_context1 = connection_pool.create_connection_context()
        connection_context2 = connection_pool.create_connection_context()
        connection_context3 = connection_pool.create_connection_context()

        task1 = event_loop.create_task(coro(connection_context1))
        task2 = event_loop.create_task(coro(connection_context2))
        task3 = event_loop.create_task(coro(connection_context3))

        await asyncio.gather(task1, task2, task3)

        # check that where called in the right order
        assert waiters_woken_up == [
            connection_context3, connection_context2, connection_context1
        ]

        # test specific atributes of the metrics
        assert connection_pool.metrics().operations_waited == 3
示例#6
0
    async def test_connection_context_not_use_a_closed_connections(
            self, event_loop, mocker, not_closed_connection):
        # Checks that if a connection is in closed state its not used and purged.
        connection_closed = Mock()
        connection_closed.closed.return_value = True

        connection_pool = ConnectionPool("localhost", 11211, 2, 1, None, None,
                                         lambda _: _, False, False, None)

        # we add by hand a closed connection and a none closed one to the pool
        connection_pool._unused_connections.append(not_closed_connection)
        connection_pool._unused_connections.append(connection_closed)
        connection_pool._total_connections = 2

        connection_context = connection_pool.create_connection_context()

        async with connection_context as connection:
            assert connection is not_closed_connection

        # double check that the closed one has been removed and the none closed
        # one is still there.
        assert not_closed_connection in connection_pool._unused_connections
        assert connection_closed not in connection_pool._unused_connections
        assert connection_pool.total_connections == 1

        # test specific atributes of the metrics
        assert connection_pool.metrics().connections_closed == 1
示例#7
0
    async def test_initalizes_creating_min_connection(self, mocker,
                                                      not_closed_connection,
                                                      min_connections):
        ev = asyncio.Event()
        connections_created = 0

        def f(*args, **kwargs):
            nonlocal connections_created
            connections_created += 1
            if connections_created == min_connections:
                ev.set()
            return not_closed_connection

        create_protocol = mocker.patch(
            "emcache.connection_pool.create_protocol",
            CoroutineMock(side_effect=f))

        max_connections = min_connections
        connection_pool = ConnectionPool("localhost", 11211, max_connections,
                                         min_connections, None, None,
                                         lambda _: _, False, False, None)

        # wait till the min_connections have been created.
        await ev.wait()

        create_protocol.assert_called_with("localhost",
                                           11211,
                                           ssl=False,
                                           ssl_verify=False,
                                           ssl_extra_ca=None,
                                           timeout=None)
        assert connection_pool.total_connections == min_connections

        # test specific atributes of the metrics
        assert connection_pool.metrics().cur_connections == min_connections
        assert connection_pool.metrics().connections_created == min_connections
        assert connection_pool.metrics().create_connection_avg is not None
        assert connection_pool.metrics().create_connection_p50 is not None
        assert connection_pool.metrics().create_connection_p99 is not None
        assert connection_pool.metrics().create_connection_upper is not None
示例#8
0
    async def test_connection_context_create_connection_if_closed(
            self, event_loop, mocker, not_closed_connection):
        # Checks that if a connection is returned in closed state, if there are waiters a new connection is created.

        connection_closed = Mock()
        connection_closed.closed.return_value = True
        create_protocol = mocker.patch(
            "emcache.connection_pool.create_protocol",
            CoroutineMock(
                side_effect=[connection_closed, not_closed_connection]),
        )

        connection_pool = ConnectionPool("localhost", 11211, 1, 1, None, None,
                                         lambda _: _, False, False, None)

        async def coro(connection_context):
            async with connection_context as _:
                pass

        # Try to get a connection many times. The first one will
        # get and use the connection_closed, when this connection is
        # returned back to the pool, a new one will be created
        connection_context1 = connection_pool.create_connection_context()
        connection_context2 = connection_pool.create_connection_context()
        connection_context3 = connection_pool.create_connection_context()

        task1 = event_loop.create_task(coro(connection_context1))
        task2 = event_loop.create_task(coro(connection_context2))
        task3 = event_loop.create_task(coro(connection_context3))

        await asyncio.gather(task1, task2, task3)

        # check that we have called the create_protocol twice
        create_protocol.assert_has_calls([
            call("localhost",
                 11211,
                 ssl=False,
                 ssl_verify=False,
                 ssl_extra_ca=None,
                 timeout=None),
            call("localhost",
                 11211,
                 ssl=False,
                 ssl_verify=False,
                 ssl_extra_ca=None,
                 timeout=None),
        ])
        assert connection_pool.total_connections == 1

        # test specific atributes of the metrics
        assert connection_pool.metrics().connections_closed == 1
示例#9
0
    async def test_purge_not_the_beyond_min_connections(
            self, event_loop, mocker, min_connections):

        # Mock everything, we will be calling it by hand
        get_running_loop = Mock()
        call_later = Mock()
        asyncio_patched = mocker.patch("emcache.connection_pool.asyncio")
        asyncio_patched.get_running_loop = get_running_loop
        get_running_loop.return_value.call_later = call_later

        max_connections = min_connections
        connection_pool = ConnectionPool("localhost", 11211, max_connections,
                                         min_connections, 60, None,
                                         lambda _: _, False, False, None)

        # we add N min expired connections that must not be removed since they are the minimum ones
        expired_connections = [Mock() for _ in range(min_connections)]
        for expired_connection in expired_connections:
            connection_pool._unused_connections.append(expired_connection)
            connection_pool._connections_last_time_used[
                expired_connection] = time.monotonic() - 61

        connection_pool._total_connections = min_connections

        # Run the purge
        connection_pool._purge_unused_connections()

        # Check that the expired has not been removed the minumum connections
        for expired_connection in expired_connections:
            assert expired_connection in connection_pool._unused_connections
            assert expired_connection in connection_pool._connections_last_time_used

        assert connection_pool.total_connections == min_connections

        # test specific atributes of the metrics
        assert connection_pool.metrics().cur_connections == min_connections
        assert connection_pool.metrics().connections_purged == 0
示例#10
0
    async def test_purge_connections(self, event_loop, mocker):

        # Mock everything, we will be calling it by hand
        get_running_loop = Mock()
        call_later = Mock()
        asyncio_patched = mocker.patch("emcache.connection_pool.asyncio")
        asyncio_patched.get_running_loop = get_running_loop
        get_running_loop.return_value.call_later = call_later

        connection_pool = ConnectionPool("localhost", 11211, 3, 1, 60, None,
                                         lambda _: _, False, False, None)

        # Check that the call late was done with the right parameters
        call_later.assert_called_with(
            60, connection_pool._purge_unused_connections)

        # we add three connections by hand, with different timestamps.
        none_expired_connection = Mock()
        expired_connection_1 = Mock()
        expired_connection_2 = Mock()

        connection_pool._unused_connections.append(none_expired_connection)
        connection_pool._unused_connections.append(expired_connection_1)
        connection_pool._unused_connections.append(expired_connection_2)

        connection_pool._connections_last_time_used[
            none_expired_connection] = time.monotonic()
        connection_pool._connections_last_time_used[
            expired_connection_1] = time.monotonic() - 61
        connection_pool._connections_last_time_used[
            expired_connection_2] = time.monotonic() - 61

        connection_pool._total_connections = 3

        # Run the purge
        connection_pool._purge_unused_connections()

        # Check that only one expired connection has been removed and the none expired is still there
        assert expired_connection_1 not in connection_pool._unused_connections
        assert expired_connection_2 in connection_pool._unused_connections
        assert none_expired_connection in connection_pool._unused_connections

        assert expired_connection_1 not in connection_pool._connections_last_time_used
        assert expired_connection_2 in connection_pool._connections_last_time_used
        assert none_expired_connection in connection_pool._connections_last_time_used

        assert connection_pool.total_connections == 2

        assert connection_pool.metrics().cur_connections == 2
        assert connection_pool.metrics().connections_purged == 1

        # Run the purge again, which should purge the other unused connection
        connection_pool._purge_unused_connections()

        assert expired_connection_2 not in connection_pool._unused_connections
        assert none_expired_connection in connection_pool._unused_connections

        assert expired_connection_2 not in connection_pool._connections_last_time_used
        assert none_expired_connection in connection_pool._connections_last_time_used

        assert connection_pool.total_connections == 1

        assert connection_pool.metrics().cur_connections == 1
        assert connection_pool.metrics().connections_purged == 2

        # Run the purge again, nothing should happen
        connection_pool._purge_unused_connections()

        assert none_expired_connection in connection_pool._unused_connections
        assert none_expired_connection in connection_pool._connections_last_time_used
        assert connection_pool.total_connections == 1