1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
|
.. _async_permanent_session:
Async permanent session
=======================
Sometimes you want to have a single permanent reconnecting async session to a GraphQL backend,
and that can be `difficult to manage`_ manually with the :code:`async with client as session` syntax.
It is now possible to have a single reconnecting session using the
:meth:`connect_async <gql.Client.connect_async>` method of Client
with a :code:`reconnecting=True` argument.
.. code-block:: python
# Create a session from the client which will reconnect automatically.
# This session can be kept in a class for example to provide a way
# to execute GraphQL queries from many different places
session = await client.connect_async(reconnecting=True)
# You can run execute or subscribe method on this session
result = await session.execute(query)
# When you want the connection to close (for cleanup),
# you call close_async
await client.close_async()
When you use :code:`reconnecting=True`, gql will watch the exceptions generated
during the execute and subscribe calls and, if it detects a TransportClosed exception
(indicating that the link to the underlying transport is broken),
it will try to reconnect to the backend again.
Retries
-------
Connection retries
^^^^^^^^^^^^^^^^^^
With :code:`reconnecting=True`, gql will use the `backoff`_ module to repeatedly try to connect with
exponential backoff and jitter with a maximum delay of 60 seconds by default.
You can change the default reconnecting profile by providing your own
backoff decorator to the :code:`retry_connect` argument.
.. code-block:: python
# Here wait maximum 5 minutes between connection retries
retry_connect = backoff.on_exception(
backoff.expo, # wait generator (here: exponential backoff)
Exception, # which exceptions should cause a retry (here: everything)
max_value=300, # max wait time in seconds
)
session = await client.connect_async(
reconnecting=True,
retry_connect=retry_connect,
)
Execution retries
^^^^^^^^^^^^^^^^^
With :code:`reconnecting=True`, by default we will also retry up to 5 times
when an exception happens during an execute call (to manage a possible loss in the connection
to the transport).
There is no retry in case of a :code:`TransportQueryError` exception as it indicates that
the connection to the backend is working correctly.
You can change the default execute retry profile by providing your own
backoff decorator to the :code:`retry_execute` argument.
.. code-block:: python
# Here Only 3 tries for execute calls
retry_execute = backoff.on_exception(
backoff.expo,
Exception,
max_tries=3,
)
session = await client.connect_async(
reconnecting=True,
retry_execute=retry_execute,
)
If you don't want any retry on the execute calls, you can disable the retries with :code:`retry_execute=False`
.. note::
If you want to retry even with :code:`TransportQueryError` exceptions,
then you need to make your own backoff decorator on your own method:
.. code-block:: python
@backoff.on_exception(backoff.expo,
Exception,
max_tries=3)
async def execute_with_retry(session, query):
return await session.execute(query)
Subscription retries
^^^^^^^^^^^^^^^^^^^^
There is no :code:`retry_subscribe` as it is not feasible with async generators.
If you want retries for your subscriptions, then you can do it yourself
with backoff decorators on your methods.
.. code-block:: python
@backoff.on_exception(backoff.expo,
Exception,
max_tries=3,
giveup=lambda e: isinstance(e, TransportQueryError))
async def execute_subscription1(session):
async for result in session.subscribe(subscription1):
print(result)
FastAPI example
---------------
.. literalinclude:: ../code_examples/fastapi_async.py
Console example
---------------
.. literalinclude:: ../code_examples/console_async.py
.. _difficult to manage: https://github.com/graphql-python/gql/issues/179
.. _backoff: https://github.com/litl/backoff
|