Esempio n. 1
0
    def test_transient_error_callback_is_tasklet(core_retry, sleep):
        """Regression test for #519

        https://github.com/googleapis/python-ndb/issues/519
        """
        core_retry.exponential_sleep_generator.return_value = itertools.count()
        core_retry.if_transient_error.return_value = True

        sleep_future = tasklets.Future("sleep")
        sleep.return_value = sleep_future

        callback = mock.Mock(side_effect=[
            utils.future_exception(Exception("Spurious error.")),
            utils.future_result("foo"),
        ])
        retry = _retry.retry_async(callback)
        future = retry()

        # This is the important check for the bug in #519. We need to make sure
        # that we're waiting for the sleep future to complete before moving on.
        assert future.running()

        # Finish sleeping
        sleep_future.set_result(None)
        assert future.result() == "foo"

        sleep.assert_called_once_with(0)
Esempio n. 2
0
def transaction_async(
    callback,
    retries=_retry._DEFAULT_RETRIES,
    read_only=False,
    xg=True,
    propagation=None,
):
    """Run a callback in a transaction.

    This is the asynchronous version of :func:`transaction`.
    """
    if propagation is not None:
        raise exceptions.NoLongerImplementedError()

    # Keep transaction propagation simple: don't do it.
    context = context_module.get_context()
    if context.transaction:
        raise NotImplementedError(
            "Can't start a transaction during a transaction.")

    tasklet = functools.partial(_transaction_async,
                                context,
                                callback,
                                read_only=read_only)
    if retries:
        tasklet = _retry.retry_async(tasklet, retries=retries)

    return tasklet()
Esempio n. 3
0
def make_call(rpc_name, request, retries=None):
    """Make a call to the Datastore API.

    Args:
        rpc_name (str): Name of the remote procedure to call on Datastore.
        request (Any): An appropriate request object for the call, eg,
            `entity_pb2.LookupRequest` for calling ``Lookup``.
        retries (int): Number of times to potentially retry the call. If
            :data:`None` is passed, will use :data:`_retry._DEFAULT_RETRIES`.
            If :data:`0` is passed, the call is attempted only once.

    Returns:
        tasklets.Future: Future for the eventual response for the API call.
    """
    api = stub()
    method = getattr(api, rpc_name)
    if retries is None:
        retries = _retry._DEFAULT_RETRIES

    @tasklets.tasklet
    def rpc_call():
        rpc = _remote.RemoteCall(method.future(request),
                                 "{}({})".format(rpc_name, request))
        log.debug(rpc)
        result = yield rpc
        return result

    if retries:
        rpc_call = _retry.retry_async(rpc_call, retries=retries)

    return rpc_call()
Esempio n. 4
0
    def test_unhandled_error():
        error = Exception("Spurious error")

        def callback():
            raise error

        retry = _retry.retry_async(callback)
        assert retry().exception() is error
Esempio n. 5
0
        def callback():
            def nested_callback():
                return "bar"

            nested = _retry.retry_async(nested_callback)
            assert nested().result() == "bar"

            return "foo"
Esempio n. 6
0
    def test_success_callback_is_tasklet():
        tasklet_future = tasklets.Future()

        @tasklets.tasklet
        def callback():
            result = yield tasklet_future
            raise tasklets.Return(result)

        retry = _retry.retry_async(callback)
        tasklet_future.set_result("foo")
        assert retry().result() == "foo"
Esempio n. 7
0
    def test_transient_error(core_retry, sleep):
        core_retry.exponential_sleep_generator.return_value = itertools.count()
        core_retry.if_transient_error.return_value = True

        sleep_future = tasklets.Future("sleep")
        sleep.return_value = sleep_future

        callback = mock.Mock(side_effect=[Exception("Spurious error."), "foo"])
        retry = _retry.retry_async(callback)
        sleep_future.set_result(None)
        assert retry().result() == "foo"

        sleep.assert_called_once_with(0)
Esempio n. 8
0
    def test_nested_retry_with_exception():
        error = Exception("Fail")

        def callback():
            def nested_callback():
                raise error

            nested = _retry.retry_async(nested_callback, retries=1)
            return nested()

        with pytest.raises(core_exceptions.RetryError):
            retry = _retry.retry_async(callback, retries=1)
            retry().result()
Esempio n. 9
0
def make_call(rpc_name, request, retries=None, timeout=None):
    """Make a call to the Datastore API.

    Args:
        rpc_name (str): Name of the remote procedure to call on Datastore.
        request (Any): An appropriate request object for the call, eg,
            `entity_pb2.LookupRequest` for calling ``Lookup``.
        retries (int): Number of times to potentially retry the call. If
            :data:`None` is passed, will use :data:`_retry._DEFAULT_RETRIES`.
            If :data:`0` is passed, the call is attempted only once.
        timeout (float): Timeout, in seconds, to pass to gRPC call. If
            :data:`None` is passed, will use :data:`_DEFAULT_TIMEOUT`.

    Returns:
        tasklets.Future: Future for the eventual response for the API call.
    """
    api = stub()
    method = getattr(api, rpc_name)

    if retries is None:
        retries = _retry._DEFAULT_RETRIES

    if timeout is None:
        timeout = _DEFAULT_TIMEOUT

    @tasklets.tasklet
    def rpc_call():
        context = context_module.get_toplevel_context()

        call = method.future(request, timeout=timeout)
        rpc = _remote.RemoteCall(call, rpc_name)
        utils.logging_debug(log, rpc)
        utils.logging_debug(log, "timeout={}", timeout)
        utils.logging_debug(log, request)

        try:
            result = yield rpc
        except Exception as error:
            if isinstance(error, grpc.Call):
                error = core_exceptions.from_grpc_error(error)
            raise error
        finally:
            context.rpc_time += rpc.elapsed_time

        raise tasklets.Return(result)

    if retries:
        rpc_call = _retry.retry_async(rpc_call, retries=retries)

    return rpc_call()
Esempio n. 10
0
    def test_too_many_transient_errors(core_retry, sleep):
        core_retry.exponential_sleep_generator.return_value = itertools.count()
        core_retry.if_transient_error.return_value = True

        sleep_future = tasklets.Future("sleep")
        sleep.return_value = sleep_future
        sleep_future.set_result(None)

        error = Exception("Spurious error")

        def callback():
            raise error

        retry = _retry.retry_async(callback)
        with pytest.raises(core_exceptions.RetryError) as error_context:
            retry().check_success()

        assert error_context.value.cause is error
        assert sleep.call_count == 4
        assert sleep.call_args[0][0] == 3
Esempio n. 11
0
def transaction_async(
    callback,
    retries=_retry._DEFAULT_RETRIES,
    read_only=False,
    join=False,
    xg=True,
    propagation=None,
):
    """Run a callback in a transaction.

    This is the asynchronous version of :func:`transaction`.
    """
    # Avoid circular import in Python 2.7
    from google.cloud.ndb import context as context_module

    if propagation is not None:
        raise exceptions.NoLongerImplementedError()

    context = context_module.get_context()
    if context.transaction:
        if join:
            result = callback()
            if not isinstance(result, tasklets.Future):
                future = tasklets.Future()
                future.set_result(result)
                result = future
            return result
        else:
            raise NotImplementedError(
                "Transactions may not be nested. Pass 'join=True' in order to "
                "join an already running transaction.")

    tasklet = functools.partial(_transaction_async,
                                context,
                                callback,
                                read_only=read_only)
    if retries:
        tasklet = _retry.retry_async(tasklet, retries=retries)

    return tasklet()
Esempio n. 12
0
    def test_success():
        def callback():
            return "foo"

        retry = _retry.retry_async(callback)
        assert retry().result() == "foo"
Esempio n. 13
0
        def callback():
            def nested_callback():
                raise error

            nested = _retry.retry_async(nested_callback, retries=1)
            return nested()