File: async_permanent_session.rst

package info (click to toggle)
python-gql 3.6.0~b4-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 1,824 kB
  • sloc: python: 20,567; makefile: 52
file content (126 lines) | stat: -rw-r--r-- 4,264 bytes parent folder | download | duplicates (2)
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