Esempio n. 1
0
async def test_init__defaults(mocker):
    mocked_pooler = mocker.patch(Cluster.__module__ + ".Pooler")
    mocked_manager = mocker.patch(Cluster.__module__ + ".ClusterManager")

    cl = Cluster(["addr1", "addr2"])

    assert cl._retry_min_delay == cl.RETRY_MIN_DELAY
    assert cl._retry_max_delay == cl.RETRY_MAX_DELAY
    assert cl._max_attempts == cl.MAX_ATTEMPTS
    assert cl._password is None
    assert cl._encoding is None
    assert cl._pool_minsize == cl.POOL_MINSIZE
    assert cl._pool_maxsize == cl.POOL_MAXSIZE
    assert cl._commands_factory is RedisCluster
    assert cl._connect_timeout == cl.CONNECT_TIMEOUT
    assert cl._pool_cls is ConnectionsPool
    assert cl._pooler is mocked_pooler.return_value
    assert cl._manager is mocked_manager.return_value
    assert cl._attempt_timeout == cl.ATTEMPT_TIMEOUT
    mocked_pooler.assert_called_once_with(cl._create_default_pool,
                                          reap_frequency=None)
    mocked_manager.assert_called_once_with(
        ["addr1", "addr2"],
        cl._pooler,
        state_reload_interval=None,
        follow_cluster=None,
        execute_timeout=5.0,
    )
Esempio n. 2
0
async def test_execute__with_attempt_timeout__idempotent__success(mocker):
    cl = Cluster(["addr1"], max_attempts=4)

    mocked_pooler = mocker.patch.object(cl, "_pooler", new=get_pooler_mock())
    pool = mocked_pooler._pool
    pool.execute.side_effect = [
        asyncio.TimeoutError(),
        asyncio.TimeoutError(),
        asyncio.TimeoutError(),
        b"VALUE",
    ]

    mocked_manager = mocker.patch.object(cl,
                                         "_manager",
                                         new=get_manager_mock())
    state = mocked_manager.get_state.return_value
    state.slot_master.return_value.addr = Address("1.2.3.4", 9999)
    state.random_node.return_value.addr = Address("6.6.6.6", 9999)

    mocked_execute_retry_slowdown = mocker.patch.object(
        cl, "_execute_retry_slowdown", new=create_async_mock())

    result = await cl.execute("get", "key")

    assert result == b"VALUE"
    assert mocked_manager.get_state.call_count == 4
    assert mocked_pooler.ensure_pool.call_count == 4
    assert mocked_manager.require_reload_state.call_count == 3
    assert mocked_execute_retry_slowdown.call_count == 3
Esempio n. 3
0
async def test_create_pool(mocker):
    mocked_create_pool = mocker.patch(Cluster.__module__ + ".create_pool",
                                      new=create_async_mock())

    pool_cls = mock.Mock()
    cl = Cluster(
        ["addr1"],
        password="******",
        encoding="utf-8",
        pool_minsize=5,
        pool_maxsize=15,
        connect_timeout=1.2,
        pool_cls=pool_cls,
    )

    addr = "addr2"
    pool = await cl._create_pool(addr)

    assert pool is mocked_create_pool.return_value
    mocked_create_pool.assert_called_once_with(
        "addr2",
        pool_cls=pool_cls,
        password="******",
        encoding="utf-8",
        minsize=5,
        maxsize=15,
        create_connection_timeout=1.2,
    )
Esempio n. 4
0
async def test_all_masters__with_attempt_timeout(mocker, event_loop):
    cl = Cluster(["addr1"], attempt_timeout=0.001)

    mocked_pooler = mocker.patch.object(cl, "_pooler", new=get_pooler_mock())
    pool = mocked_pooler._pool
    execute1_fut = event_loop.create_future()
    execute1_fut.set_result(None)
    execute2_fut = event_loop.create_future()

    execute_futs_iter = iter([execute1_fut, execute2_fut])

    async def execute_side_effect(*args, **kwargs):
        return await next(execute_futs_iter)

    pool.execute.side_effect = execute_side_effect

    mocked_manager = mocker.patch.object(cl,
                                         "_manager",
                                         new=get_manager_mock())
    state = mocked_manager.get_state.return_value
    state._data.masters = [
        mock.NonCallableMock(addr=Address("1.2.3.4", 6666)),
        mock.NonCallableMock(addr=Address("1.2.3.4", 9999)),
    ]

    mocked_execute_retry_slowdown = mocker.patch.object(
        cl, "_execute_retry_slowdown", new=create_async_mock())

    with pytest.raises(asyncio.TimeoutError):
        await cl.all_masters()

    assert mocked_manager.get_state.call_count == 1
    assert mocked_pooler.ensure_pool.call_count == 2
    assert mocked_manager.require_reload_state.call_count == 1
    mocked_execute_retry_slowdown.assert_not_called()
Esempio n. 5
0
async def test_execute__retryable_errors(mocker, error, retry_node_addr,
                                         require_reload_state):
    cl = Cluster(["addr1"])

    mocked_pooler = mocker.patch.object(cl, "_pooler", new=get_pooler_mock())

    pool = mocked_pooler._pool
    conn = mocked_pooler._conn

    pool_execute_count = 0

    def pool_execute_se(*args, **kwargs):
        nonlocal pool_execute_count
        if args[0] == b"ASKING":
            return b"OK"

        pool_execute_count += 1
        if pool_execute_count == 1:
            raise error

        return pool.execute.return_value

    pool.execute.side_effect = pool_execute_se
    conn.execute.side_effect = pool_execute_se

    mocked_manager = mocker.patch.object(cl,
                                         "_manager",
                                         new=get_manager_mock())
    state = mocked_manager.get_state.return_value
    state.slot_master.return_value.addr = Address("master", 6379)
    state.random_node.return_value.addr = Address("random", 6379)
    state.random_slot_replica.return_value.addr = Address(
        "random_replica", 6379)

    mocked_execute_retry_slowdown = mocker.patch.object(
        cl, "_execute_retry_slowdown", new=create_async_mock())

    result = await cl.execute("get", "key")

    assert mocked_manager.get_state.call_count == 2
    assert mocked_pooler.ensure_pool.call_count == 2
    assert mocked_manager.require_reload_state.call_count == require_reload_state
    assert result is pool.execute.return_value
    assert mocked_pooler.ensure_pool.call_args_list[0] == mock.call(
        Address("master", 6379))
    assert mocked_pooler.ensure_pool.call_args_list[1] == mock.call(
        retry_node_addr)
    # cl.determine_slot(b'key') == 12539
    state.slot_master.assert_called_once_with(12539)
    mocked_execute_retry_slowdown.assert_called_once_with(1, 10)
Esempio n. 6
0
async def test_init__customized(mocker):
    mocked_pooler = mocker.patch(Cluster.__module__ + ".Pooler")
    mocked_manager = mocker.patch(Cluster.__module__ + ".ClusterManager")

    kwargs = {
        "retry_min_delay": 1.2,
        "retry_max_delay": 3.4,
        "max_attempts": 5,
        "idle_connection_timeout": 10.0,
        "password": "******",
        "encoding": "cp1251",
        "pool_minsize": 3,
        "pool_maxsize": 7,
        "commands_factory": object(),
        "connect_timeout": 9.9,
        "pool_cls": mock.Mock(),
        "state_reload_interval": 2.2,
        "follow_cluster": True,
        "attempt_timeout": 7.8,
    }

    cl = Cluster(["addr1", "addr2"], **kwargs)

    assert cl._retry_min_delay == kwargs["retry_min_delay"]
    assert cl._retry_max_delay == kwargs["retry_max_delay"]
    assert cl._max_attempts == kwargs["max_attempts"]
    assert cl._password == kwargs["password"]
    assert cl._encoding == kwargs["encoding"]
    assert cl._pool_minsize == kwargs["pool_minsize"]
    assert cl._pool_maxsize == kwargs["pool_maxsize"]
    assert cl._connect_timeout == kwargs["connect_timeout"]
    assert cl._commands_factory is kwargs["commands_factory"]
    assert cl._pool_cls is kwargs["pool_cls"]
    assert cl._pooler is mocked_pooler.return_value
    assert cl._manager is mocked_manager.return_value
    assert cl._attempt_timeout == kwargs["attempt_timeout"]
    mocked_pooler.assert_called_once_with(
        cl._create_default_pool,
        reap_frequency=kwargs["idle_connection_timeout"])
    mocked_manager.assert_called_once_with(
        ["addr1", "addr2"],
        cl._pooler,
        state_reload_interval=kwargs["state_reload_interval"],
        follow_cluster=kwargs["follow_cluster"],
        execute_timeout=kwargs["attempt_timeout"],
    )
Esempio n. 7
0
async def test_keys_master__success(mocker):
    commands_factory = mock.Mock()
    cl = Cluster(["addr1"], commands_factory=commands_factory)

    mocked_pooler = mocker.patch.object(cl, "_pooler", new=get_pooler_mock())
    pool = mocked_pooler.ensure_pool.return_value
    mocked_manager = mocker.patch.object(cl,
                                         "_manager",
                                         new=get_manager_mock())
    state = mocked_manager.get_state.return_value

    result = await cl.keys_master("key")

    assert result is commands_factory.return_value
    state.slot_master.assert_called_once_with(12539)
    mocked_pooler.ensure_pool.assert_called_once_with(
        state.slot_master.return_value.addr)
    commands_factory.assert_called_once_with(pool)
Esempio n. 8
0
async def test_execute__success_random_master(mocker):
    cl = Cluster(["addr1"])

    mocked_pooler = mocker.patch.object(cl, "_pooler", new=get_pooler_mock())
    pool = mocked_pooler._pool
    mocked_manager = mocker.patch.object(cl,
                                         "_manager",
                                         new=get_manager_mock())
    state = mocked_manager.get_state.return_value

    result = await cl.execute("INFO")

    assert mocked_manager.get_state.call_count == 1
    assert mocked_pooler.ensure_pool.call_count == 1
    assert mocked_manager.require_reload_state.call_count == 0
    assert result is pool.execute.return_value
    mocked_pooler.ensure_pool.assert_called_with(
        state.random_master.return_value.addr)
    state.random_master.assert_called_once_with()
Esempio n. 9
0
async def test_execute__cancelled_error(mocker):
    cl = Cluster(["addr1"])

    mocked_pooler = mocker.patch.object(cl, "_pooler", new=get_pooler_mock())
    mocked_pooler.ensure_pool.side_effect = asyncio.CancelledError()

    mocked_manager = mocker.patch.object(cl,
                                         "_manager",
                                         new=get_manager_mock())

    mocked_execute_retry_slowdown = mocker.patch.object(
        cl, "_execute_retry_slowdown", new=create_async_mock())

    with pytest.raises(asyncio.CancelledError):
        await cl.execute("get", "key")

    assert mocked_manager.get_state.call_count == 1
    assert mocked_pooler.ensure_pool.call_count == 1
    assert mocked_manager.require_reload_state.call_count == 0
    mocked_execute_retry_slowdown.assert_not_called()
Esempio n. 10
0
async def test_execute__success_slot_master(mocker):
    cl = Cluster(["addr1"])

    mocked_pooler = mocker.patch.object(cl, "_pooler", new=get_pooler_mock())
    pool = mocked_pooler._pool
    mocked_manager = mocker.patch.object(cl,
                                         "_manager",
                                         new=get_manager_mock())
    state = mocked_manager.get_state.return_value

    result = await cl.execute("mset", "key", "value")

    assert mocked_manager.get_state.call_count == 1
    assert mocked_pooler.ensure_pool.call_count == 1
    assert mocked_manager.require_reload_state.call_count == 0
    assert result is pool.execute.return_value
    mocked_pooler.ensure_pool.assert_called_with(
        state.slot_master.return_value.addr)
    # cl.determine_slot(b'key') == 12539
    state.slot_master.assert_called_once_with(12539)
Esempio n. 11
0
async def test_auth(mocker):
    cl = Cluster(["addr1"])

    mocked_pooler = mocker.patch.object(cl, "_pooler", new=get_pooler_mock())
    pool_mock = mocked_pooler.ensure_pool.return_value

    async def batch_op_se(fn):
        for p in [pool_mock, pool_mock]:
            await fn(p)

    mocked_pooler.batch_op = create_async_mock(side_effect=batch_op_se)

    await cl.auth("PASSWORD")

    assert cl._password == "PASSWORD"
    mocked_pooler.batch_op.assert_called_once()
    pool_mock.auth.assert_has_calls([
        mock.call("PASSWORD"),
        mock.call("PASSWORD"),
    ])
Esempio n. 12
0
async def test_execute__unexpected_error(mocker):
    cl = Cluster(["addr1"])

    mocked_pooler = mocker.patch.object(cl, "_pooler", new=get_pooler_mock())
    mocked_pooler.ensure_pool.side_effect = RuntimeError("Boom!")

    mocked_manager = mocker.patch.object(cl,
                                         "_manager",
                                         new=get_manager_mock())

    mocked_execute_retry_slowdown = mocker.patch.object(
        cl, "_execute_retry_slowdown", new=create_async_mock())

    with pytest.raises(RuntimeError, match="Boom!"):
        await cl.execute("echo", "hello")

    assert mocked_manager.get_state.call_count == 1
    assert mocked_pooler.ensure_pool.call_count == 1
    assert mocked_manager.require_reload_state.call_count == 0
    mocked_execute_retry_slowdown.assert_not_called()
Esempio n. 13
0
async def test_execute__retriable_error_in_unexpected_call(mocker):
    cl = Cluster(["addr1"])

    mocked_pooler = mocker.patch.object(cl, "_pooler", new=get_pooler_mock())

    mocked_manager = mocker.patch.object(cl,
                                         "_manager",
                                         new=get_manager_mock())
    mocked_manager.get_state.side_effect = ConnectionRefusedError()

    mocked_execute_retry_slowdown = mocker.patch.object(
        cl, "_execute_retry_slowdown", new=create_async_mock())

    with pytest.raises(ConnectionRefusedError):
        await cl.execute("ping")

    assert mocked_manager.get_state.call_count == 1
    assert mocked_pooler.ensure_pool.call_count == 0
    assert mocked_manager.require_reload_state.call_count == 0
    assert mocked_execute_retry_slowdown.call_count == 0
Esempio n. 14
0
async def test_all_masters__with_error_after_retries(mocker):
    cl = Cluster(["addr1"], max_attempts=3)

    mocked_pooler = mocker.patch.object(cl, "_pooler", new=get_pooler_mock())
    pool = mocked_pooler.ensure_pool.return_value

    pools_returns = [
        OSError(),
        pool,
        ConnectionError(),
        pool,
        pool,
        ClusterDownError("CLUSTERDOWN cluster is down"),
    ]

    pools_returns_iter = iter(pools_returns)

    def ensure_pool_se(addr):
        res = next(pools_returns_iter)
        if isinstance(res, Exception):
            raise res
        return res

    mocked_pooler.ensure_pool.side_effect = ensure_pool_se

    mocked_manager = mocker.patch.object(cl,
                                         "_manager",
                                         new=get_manager_mock())
    state = mocked_manager.get_state.return_value
    state._data.masters = [
        mock.NonCallableMock(name="master1"),
        mock.NonCallableMock(name="master2"),
        mock.NonCallableMock(name="master3"),
    ]

    mocker.patch.object(cl, "_execute_retry_slowdown", new=create_async_mock())

    with pytest.raises(ClusterDownError):
        await cl.all_masters()
Esempio n. 15
0
async def test_create_pool_by_addr__bad_addr(mocker):
    pool_cls = mock.Mock()
    cl = Cluster(
        ["addr1"],
        password="******",
        encoding="utf-8",
        pool_minsize=5,
        pool_maxsize=15,
        connect_timeout=1.2,
        pool_cls=pool_cls,
    )
    mocked_manager = mocker.patch.object(cl,
                                         "_manager",
                                         new=get_manager_mock())
    state = mocked_manager.get_state.return_value
    state.has_addr.return_value = False

    addr = Address("addr4", 8004)
    with pytest.raises(ValueError):
        await cl.create_pool_by_addr(addr, maxsize=42)

    state.has_addr.assert_called_once_with(addr)
Esempio n. 16
0
async def test_keys_master__with_retries_but_success(mocker):
    commands_factory = mock.Mock()
    cl = Cluster(["addr1"], commands_factory=commands_factory)

    mocked_pooler = mocker.patch.object(cl, "_pooler", new=get_pooler_mock())
    pool = mocked_pooler.ensure_pool.return_value

    errors = [
        ConnectionError(),
        LoadingError("LOADING loading"),
        ProtocolError(),
        ClusterDownError("CLUSTERDOWN cluster is down"),
        OSError("socket problem"),
    ]
    errors_iter = iter(errors)

    def ensure_pool_se(addr):
        try:
            raise next(errors_iter)
        except StopIteration:
            return pool

    mocked_pooler.ensure_pool.side_effect = ensure_pool_se

    mocked_manager = mocker.patch.object(cl,
                                         "_manager",
                                         new=get_manager_mock())
    state = mocked_manager.get_state.return_value

    mocker.patch.object(cl, "_execute_retry_slowdown", new=create_async_mock())

    result = await cl.keys_master("key")

    assert result is commands_factory.return_value
    state.slot_master.assert_called_with(12539)
    assert state.slot_master.call_count == 6
    mocked_pooler.ensure_pool.assert_has_calls(
        [mock.call(state.slot_master.return_value.addr)] * 6)
    commands_factory.assert_called_once_with(pool)
Esempio n. 17
0
async def test_execute__success_one_connection_retry(mocker):
    cl = Cluster(["addr1"])

    mocked_pooler = mocker.patch.object(cl, "_pooler", new=get_pooler_mock())
    pool = mocked_pooler._pool

    pool_execute_count = 0

    def pool_execute_se(*args, **kwargs):
        nonlocal pool_execute_count
        pool_execute_count += 1
        if pool_execute_count == 1:
            raise ConnectionRefusedError

        return pool.execute.return_value

    pool.execute.side_effect = pool_execute_se

    mocked_manager = mocker.patch.object(cl,
                                         "_manager",
                                         new=get_manager_mock())
    state = mocked_manager.get_state.return_value

    mocked_execute_retry_slowdown = mocker.patch.object(
        cl, "_execute_retry_slowdown", new=create_async_mock())

    result = await cl.execute("get", "key")

    assert mocked_manager.get_state.call_count == 2
    assert mocked_pooler.ensure_pool.call_count == 2
    assert mocked_manager.require_reload_state.call_count == 1
    assert result is pool.execute.return_value
    assert mocked_pooler.ensure_pool.call_args_list[0] == mock.call(
        state.slot_master.return_value.addr)
    assert mocked_pooler.ensure_pool.call_args_list[1] == mock.call(
        state.random_slot_replica.return_value.addr)
    # cl.determine_slot(b'key') == 12539
    state.slot_master.assert_called_once_with(12539)
    mocked_execute_retry_slowdown.assert_called_once_with(1, 10)
Esempio n. 18
0
async def test_keys_master__with_attempt_timeout(mocker):
    cl = Cluster(["addr1"], max_attempts=2)

    mocked_pooler = mocker.patch.object(cl, "_pooler", new=get_pooler_mock())
    pool = mocked_pooler._pool
    pool.execute.side_effect = asyncio.TimeoutError()

    mocked_manager = mocker.patch.object(cl,
                                         "_manager",
                                         new=get_manager_mock())
    state = mocked_manager.get_state.return_value
    state.slot_master.return_value.addr = Address("1.2.3.4", 9999)

    mocked_execute_retry_slowdown = mocker.patch.object(
        cl, "_execute_retry_slowdown", new=create_async_mock())

    with pytest.raises(asyncio.TimeoutError):
        await cl.keys_master("foo{key}", "bar{key}")

    assert mocked_manager.get_state.call_count == 2
    assert mocked_pooler.ensure_pool.call_count == 2
    assert mocked_manager.require_reload_state.call_count == 2
    assert mocked_execute_retry_slowdown.call_count == 1
Esempio n. 19
0
async def test_create_pool_by_addr(mocker):
    mocked_create_pool = mocker.patch(Cluster.__module__ + ".create_pool",
                                      new=create_async_mock())

    pool_cls = mock.Mock()
    cl = Cluster(
        ["addr1"],
        password="******",
        encoding="utf-8",
        pool_minsize=5,
        pool_maxsize=15,
        connect_timeout=1.2,
        pool_cls=pool_cls,
    )
    mocked_manager = mocker.patch.object(cl,
                                         "_manager",
                                         new=get_manager_mock())
    state = mocked_manager.get_state.return_value
    state._data.masters = [
        mock.NonCallableMock(name="master1", addr=("addr1", 8001)),
        mock.NonCallableMock(name="master2", addr=("addr2", 8002)),
        mock.NonCallableMock(name="master3", addr=("addr3", 8003)),
    ]

    addr = Address("addr2", 8002)
    redis = await cl.create_pool_by_addr(addr, maxsize=42)

    assert redis.connection is mocked_create_pool.return_value
    mocked_create_pool.assert_called_once_with(
        ("addr2", 8002),
        pool_cls=pool_cls,
        password="******",
        encoding="utf-8",
        minsize=5,
        maxsize=42,
        create_connection_timeout=1.2,
    )
Esempio n. 20
0
async def test_execute__with_attempt_timeout__non_idempotent(
        mocker, event_loop):
    cl = Cluster(["addr1"], attempt_timeout=0.001)

    mocked_pooler = mocker.patch.object(cl, "_pooler", new=get_pooler_mock())
    pool = mocked_pooler._pool
    pool.execute = mock.Mock(return_value=event_loop.create_future())

    mocked_manager = mocker.patch.object(cl,
                                         "_manager",
                                         new=get_manager_mock())
    state = mocked_manager.get_state.return_value
    state.slot_master.return_value.addr = Address("1.2.3.4", 9999)

    mocked_execute_retry_slowdown = mocker.patch.object(
        cl, "_execute_retry_slowdown", new=create_async_mock())

    with pytest.raises(asyncio.TimeoutError):
        await cl.execute("set", "key", "value")

    assert mocked_manager.get_state.call_count == 1
    assert mocked_pooler.ensure_pool.call_count == 1
    assert mocked_manager.require_reload_state.call_count == 1
    mocked_execute_retry_slowdown.assert_not_called()
Esempio n. 21
0
async def test_keys_master__with_error_after_retries(mocker):
    cl = Cluster(["addr1"], max_attempts=3)

    mocked_pooler = mocker.patch.object(cl, "_pooler", new=get_pooler_mock())

    errors = [
        ConnectionError(),
        ConnectionError(),
        ClusterDownError("CLUSTERDOWN cluster is down"),
    ]

    mocked_pooler.ensure_pool.side_effect = errors

    mocked_manager = mocker.patch.object(cl,
                                         "_manager",
                                         new=get_manager_mock())
    state = mocked_manager.get_state.return_value

    mocker.patch.object(cl, "_execute_retry_slowdown", new=create_async_mock())

    with pytest.raises(ClusterDownError):
        await cl.keys_master("key")

    assert state.slot_master.call_count == 3
Esempio n. 22
0
async def create_cluster(
    startup_nodes: Sequence[AioredisAddress],
    *,
    # failover options
    retry_min_delay: float = None,
    retry_max_delay: float = None,
    max_attempts: int = None,
    attempt_timeout: float = None,
    # manager options
    state_reload_frequency: float = None,
    state_reload_interval: float = None,
    follow_cluster: bool = None,
    # pool options
    idle_connection_timeout: float = None,
    # node client options
    password: str = None,
    encoding: str = None,
    pool_minsize: int = None,
    pool_maxsize: int = None,
    connect_timeout: float = None,
) -> AbcCluster:
    corrected_nodes: List[Address] = []
    for mixed_addr in startup_nodes:
        if isinstance(mixed_addr, str):
            parsed_addr, options = parse_url(mixed_addr)
            addr = Address(parsed_addr[0], parsed_addr[1])
        else:
            addr = Address(mixed_addr[0], mixed_addr[1])
        corrected_nodes.append(addr)

    # shuffle startup nodes for every instance
    # for distribute initial load to cluster
    if len(corrected_nodes) > 1:
        random.shuffle(corrected_nodes)

    if state_reload_frequency is not None:
        warnings.warn(
            "`state_reload_frequency` is deprecated and is no affect anything",
            DeprecationWarning,
        )

    cluster = Cluster(
        corrected_nodes,
        retry_min_delay=retry_min_delay,
        retry_max_delay=retry_max_delay,
        max_attempts=max_attempts,
        state_reload_interval=state_reload_interval,
        follow_cluster=follow_cluster,
        idle_connection_timeout=idle_connection_timeout,
        password=password,
        encoding=encoding,
        pool_minsize=pool_minsize,
        pool_maxsize=pool_maxsize,
        connect_timeout=connect_timeout,
        attempt_timeout=attempt_timeout,
    )
    try:
        await cluster._init()
    except Exception:
        cluster.close()
        await cluster.wait_closed()
        raise

    return cluster
Esempio n. 23
0
def test_init__no_startup_nodes():
    with pytest.raises(ValueError, match="startup_nodes must be one at least"):
        Cluster([])
Esempio n. 24
0
async def test_execute__success_several_problem_retry(mocker):
    cl = Cluster(["addr1"])

    mocked_pooler = mocker.patch.object(cl, "_pooler", new=get_pooler_mock())
    conn = mocked_pooler._conn
    pool = mocked_pooler._pool

    pool_execute_count = 0

    errors = [
        ConnectionRefusedError(),
        ConnectionError(),
        ConnectTimeoutError(("addr1", 6379)),
        AskError("ASK 12539 1.2.3.4:9999"),
        ClusterDownError("CLUSTERDOWN cluster is down"),
        TryAgainError("TRYAGAIN try again later"),
        MovedError("MOVED 12539 4.3.2.1:6666"),
        ProtocolError(),
        MovedError("MOVED 12539 4.3.2.1:6666"),
    ]

    def conn_execute_se(*args, **kwargs):
        nonlocal pool_execute_count

        if args[0] == b"ASKING":
            return conn.execute.return_value

        pool_execute_count += 1
        raise errors[pool_execute_count - 1]

    def pool_execute_se(*args, **kwargs):
        nonlocal pool_execute_count

        pool_execute_count += 1
        if pool_execute_count <= len(errors):
            err = errors[pool_execute_count - 1]
            raise err

        return ("result", pool_execute_count)

    pool.execute.side_effect = pool_execute_se
    conn.execute.side_effect = conn_execute_se

    mocked_manager = mocker.patch.object(cl,
                                         "_manager",
                                         new=get_manager_mock())
    state = mocked_manager.get_state.return_value

    mocked_execute_retry_slowdown = mocker.patch.object(
        cl, "_execute_retry_slowdown", new=create_async_mock())

    result = await cl.execute("get", "key")

    assert mocked_manager.get_state.call_count == 10
    assert mocked_pooler.ensure_pool.call_count == 10
    assert mocked_manager.require_reload_state.call_count == 8
    assert result == ("result", 10)

    ensure_pool_calls = mocked_pooler.ensure_pool.call_args_list

    # #1 attempt
    assert ensure_pool_calls[0] == mock.call(
        state.slot_master.return_value.addr)
    # #2 attempt to random replica node
    assert ensure_pool_calls[1] == mock.call(
        state.random_slot_replica.return_value.addr)
    # #3 attempt try to execute on random node in cluster
    assert ensure_pool_calls[2] == mock.call(
        state.random_node.return_value.addr)
    # #4 attempt try random node in cluster
    assert ensure_pool_calls[3] == mock.call(
        state.random_node.return_value.addr)
    # #5 attempt as ASKING to node from MovedError
    assert ensure_pool_calls[4] == mock.call(Address("1.2.3.4", 9999))
    # #6 attempt again random node because CLUSTERDOWN
    assert ensure_pool_calls[5] == mock.call(
        state.random_node.return_value.addr)
    # #7 attempt again random node because TRYAGAIN
    assert ensure_pool_calls[6] == mock.call(
        state.random_node.return_value.addr)
    # #8 attempt to node from MovedError
    assert ensure_pool_calls[7] == mock.call(Address("4.3.2.1", 6666))
    # #9 attempt again random node because ProtocolError
    assert ensure_pool_calls[8] == mock.call(
        state.random_node.return_value.addr)
    # #10 attempt to node from MovedError, finally
    assert ensure_pool_calls[9] == mock.call(Address("4.3.2.1", 6666))
    # cl.determine_slot(b'key') == 12539
    state.slot_master.assert_called_once_with(12539)
    assert state.random_slot_replica.call_count == 1
    assert state.random_slot_replica.call_args == mock.call(12539)
    assert state.random_node.call_count == 5
    assert mocked_execute_retry_slowdown.call_count == 9

    asking_calls = [
        c for c in conn.execute.call_args_list if c == mock.call(b"ASKING")
    ]
    assert len(asking_calls) == 1
Esempio n. 25
0
async def test_execute__error_after_max_attempts(mocker):
    cl = Cluster(["addr1"])

    mocked_pooler = mocker.patch.object(cl, "_pooler", new=get_pooler_mock())
    pool = mocked_pooler._pool

    pool_execute_count = 0

    errors = [
        MovedError("MOVED 12539 1.2.3.4:1000"),
        MovedError("MOVED 12539 1.2.3.4:1001"),
        MovedError("MOVED 12539 1.2.3.4:1002"),
        MovedError("MOVED 12539 1.2.3.4:1003"),
        MovedError("MOVED 12539 1.2.3.4:1004"),
        MovedError("MOVED 12539 1.2.3.4:1005"),
        MovedError("MOVED 12539 1.2.3.4:1006"),
        MovedError("MOVED 12539 1.2.3.4:1007"),
        MovedError("MOVED 12539 1.2.3.4:1008"),
        MovedError("MOVED 12539 1.2.3.4:1009"),
        MovedError("MOVED 12539 1.2.3.4:1010"),
        MovedError("MOVED 12539 1.2.3.4:1011"),
    ]

    def pool_execute_se(*args, **kwargs):
        nonlocal pool_execute_count

        pool_execute_count += 1
        raise errors[pool_execute_count - 1]

    pool.execute.side_effect = pool_execute_se

    mocked_manager = mocker.patch.object(cl,
                                         "_manager",
                                         new=get_manager_mock())
    state = mocked_manager.get_state.return_value

    mocked_execute_retry_slowdown = mocker.patch.object(
        cl, "_execute_retry_slowdown", new=create_async_mock())

    with pytest.raises(MovedError) as ei:
        await cl.execute("time")

    assert (ei.value.info.host, ei.value.info.port) == ("1.2.3.4", 1009)
    assert mocked_manager.get_state.call_count == 10
    assert mocked_pooler.ensure_pool.call_count == 10
    assert mocked_manager.require_reload_state.call_count == 10

    ensure_pool_calls = mocked_pooler.ensure_pool.call_args_list

    assert ensure_pool_calls[0] == mock.call(
        state.random_master.return_value.addr)
    assert ensure_pool_calls[1] == mock.call(Address("1.2.3.4", 1000))
    assert ensure_pool_calls[2] == mock.call(Address("1.2.3.4", 1001))
    assert ensure_pool_calls[3] == mock.call(Address("1.2.3.4", 1002))
    assert ensure_pool_calls[4] == mock.call(Address("1.2.3.4", 1003))
    assert ensure_pool_calls[5] == mock.call(Address("1.2.3.4", 1004))
    assert ensure_pool_calls[6] == mock.call(Address("1.2.3.4", 1005))
    assert ensure_pool_calls[7] == mock.call(Address("1.2.3.4", 1006))
    assert ensure_pool_calls[8] == mock.call(Address("1.2.3.4", 1007))
    assert ensure_pool_calls[9] == mock.call(Address("1.2.3.4", 1008))
    assert state.random_node.call_count == 0
    assert mocked_execute_retry_slowdown.call_count == 9