Esempio n. 1
0
def wrap_future(
    response_future: ResponseFuture,
    callback_fn: Callable[..., None],
    callback_args: Any,
    errback_fn: Callable[..., None],
    errback_args: Any,
) -> ResponseFuture:
    """Patch ResponseFuture.result() to wait for callback or errback to complete.

    The callback_fn and errback_fn are given special treatment: they must be
    complete before a result will be returned from ResponseFuture.result().
    They are not given precedence over other callbacks or errbacks, so if
    another callback triggers the response from the service (and the server
    span is closed) the special callback might not complete. The special
    callback is added first and  callbacks are executed in order, so
    generally the special callback should finish before any other callbacks.

    This fixes a race condition where the server span can complete before
    the callback has closed out the child span.

    """
    response_future._callback_event = Event()

    response_future.add_callback(callback_fn, callback_args,
                                 response_future._callback_event)
    response_future.add_errback(errback_fn, errback_args,
                                response_future._callback_event)

    def wait_for_callbacks_result(self: ResponseFuture) -> Any:
        exc = None

        try:
            result = ResponseFuture.result(self)
        except Exception as e:
            exc = e

        # wait for either _on_execute_complete or _on_execute_failed to run
        wait_result = self._callback_event.wait(timeout=0.01)
        if not wait_result:
            logger.warning(
                "Cassandra metrics callback took too long. Some metrics may be lost."
            )

        if exc:
            raise exc  # pylint: disable=E0702

        return result

    # call __get__ to turn wait_for_callbacks_result into a bound method
    bound_method = wait_for_callbacks_result.__get__(  # type: ignore
        response_future, ResponseFuture)
    # patch the ResponseFuture instance
    response_future.result = bound_method
    return response_future
Esempio n. 2
0
def _wrap_future(f: ResponseFuture) -> asyncio.Future:
    """Wrap a cassandra Future into an asyncio.Future object.

    Args:
        f: future to wrap

    Returns:
        And asyncio.Future object which can be awaited.
    """
    loop = asyncio.get_event_loop()
    aio_future = loop.create_future()

    def on_result(result):
        loop.call_soon_threadsafe(aio_future.set_result, result)

    def on_error(exception, *_):
        loop.call_soon_threadsafe(aio_future.set_exception, exception)

    f.add_callback(on_result)
    f.add_errback(on_error)
    return aio_future