Skip to content

Commit 7f7a3e7

Browse files
authored
Docs for PreferRepeatableRead (#9065)
Ref #8832 Since this is a *virtual*, client side option, it has to be explained for each client separately.
1 parent 7bc9dc1 commit 7f7a3e7

File tree

3 files changed

+103
-3
lines changed

3 files changed

+103
-3
lines changed

docs/reference/reference/edgeql/tx_start.rst

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ Start transaction
3838

3939
# where <transaction-mode> is one of:
4040

41+
isolation repeatable read
4142
isolation serializable
4243
read write | read only
4344
deferrable | not deferrable
@@ -54,6 +55,7 @@ committed if the command was executed successfully, or automatically
5455
rollbacked if there was an error. This behavior is often called
5556
"autocommit".
5657

58+
When isolation is not specified, it defaults to ``serializable``.
5759

5860
Parameters
5961
----------
@@ -67,7 +69,44 @@ The :eql:synopsis:`<transaction-mode>` can be one of the following:
6769
If a pattern of reads and writes among concurrent serializable
6870
transactions creates a situation that could not have occurred
6971
in any serial (one-at-a-time) execution of those transactions,
70-
one of them will be rolled back with a serialization_failure error.
72+
one of them will be rolled back with a serialization failure.
73+
74+
This level is the default isolation level.
75+
76+
Note that, compared to ``repeatable read``, serializable level has a
77+
significantly higher probability of resulting in serialization failures,
78+
requires the whole transaction to be retried. If acceptable, consider using
79+
``repeatable read`` or
80+
:ref:`prefer repeatable read <prefer_repeatable_read>`.
81+
82+
:eql:synopsis:`isolation repeatable read`
83+
All statements in the current transaction can only see data
84+
changes that were committed before the first query or data
85+
modification statement was executed within this transaction.
86+
87+
Compared to ``serializable``, this level is less likely to result in
88+
serialization failures.
89+
90+
It is however possible for this level to allow serialization anomalies.
91+
This constitutes a series of transactions that would not be allowed if they
92+
were executed serially instead of concurrently.
93+
94+
For example, assume ``type X { is_selected: bool }`` and following query:
95+
96+
.. code-block:: edgeql
97+
98+
# transaction A: unselect all selected
99+
update X filter .is_selected set { is_selected := false };
100+
101+
# transaction B: select all unselected
102+
update X filter not .is_selected set { is_selected := true };
103+
104+
Running these two transactions serially would result in either all ``X``
105+
being select or none being selected. But if executed concurrently, even with
106+
the ``repeatable read`` isolation level, we can end up with some ``X`` being
107+
selected and some not.
108+
109+
To avoid this, we can use the ``serializable`` isolation level.
71110

72111
:eql:synopsis:`read write`
73112
Sets the transaction access mode to read/write.
@@ -109,6 +148,23 @@ Start a serializable deferrable transaction:
109148
start transaction isolation serializable, read only, deferrable;
110149
111150
151+
.. _prefer_repeatable_read:
152+
153+
Prefer repeatable read
154+
----------------------
155+
156+
In addition to the isolation levels above, some client libraries also support
157+
``PreferRepeatableRead`` as a transaction isolation level.
158+
In this mode, the server will analyze the query and use ``repeatable read``
159+
isolation level if it can. When it cannot, it will use ``serializable``
160+
isolation level.
161+
162+
Client libraries that currently support this mode:
163+
164+
* TypeScript/JS
165+
* Python
166+
* Go
167+
112168
.. list-table::
113169
:class: seealso
114170

docs/reference/using/js/client.rst

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,14 +187,14 @@ Both ``execute`` and the ``query*`` methods support scripts (queries containing
187187
});
188188
result; // { id: string }[]: the result of the `insert Person` statement
189189
190-
For more fine grained control of atomic exectution of multiple statements, use the ``transaction()`` API.
190+
For more fine grained control of atomic execution of multiple statements, use the ``transaction()`` API.
191191

192192
.. _gel-js-api-transaction:
193193

194194
Transactions
195195
------------
196196

197-
For more fine grained control of atomic exectution of multiple statements, use the ``transaction()`` API.
197+
For more fine grained control of atomic execution of multiple statements, use the ``transaction()`` API.
198198

199199
.. code-block:: typescript
200200
@@ -213,6 +213,8 @@ The ``transaction()`` API guarantees that:
213213

214214
The *transaction* object exposes ``query()``, ``execute()``, ``querySQL()``, ``executeSQL()``, and other ``query*()`` methods that *clients* expose, with the only difference that queries will run within the current transaction and can be retried automatically.
215215

216+
Default isolation level is serializable. You can change it via ``Client.withTransactionOptions``.
217+
216218
.. warning::
217219

218220
In transactions, the entire nested code block can be re-run, including any non-querying JavaScript code. In general, the code inside the transaction block **should not have side effects or run for a significant amount of time**. Consider the following example:
@@ -628,6 +630,9 @@ Client Reference
628630
);
629631
});
630632
633+
By default, transactions will be executed in the strictest, serializable isolation level.
634+
To change the isolation level, use the ``Client.withTransactionOptions``.
635+
631636
.. js:method:: ensureConnected(): Promise<Client>
632637

633638
If the client does not yet have any open connections in its pool, attempts to open a connection, else returns immediately.
@@ -743,6 +748,40 @@ Client Reference
743748
// ...
744749
});
745750
751+
.. js:method:: withTransactionOptions(opts: {
752+
isolation?: IsolationLevel, \
753+
readonly?: boolean, \
754+
deferrable?: boolean, \
755+
}): Client
756+
757+
Returns a clone of the ``Client`` instance with the specified transaction options.
758+
759+
Available isolation levels are ``Serializable``, ``RepeatableRead``, and ``PreferRepeatableRead``. ``PreferRepeatableRead`` uses repeatable read isolation level if server analysis concludes that it is supported for a given query. Otherwise, uses serializable isolation level.
760+
761+
When readonly is set, the transaction is now allowed to execute ``insert``, ``update``, or ``delete`` statements.
762+
763+
When deferrable is set, and isolation is serializable and readonly is set, the transaction only block when first acquiring its snapshot, after which it is able to run without the normal overhead of a serializable transaction and without any risk of contributing to or being canceled by a serialization failure. This mode is well suited for long-running reports or backups.
764+
765+
For more information, see :ref:`the transaction options reference <ref_eql_statements_start_tx>`.
766+
767+
:arg opts: An object mapping transaction options to values.
768+
769+
:returns: ``Client``
770+
771+
Example:
772+
773+
.. code-block:: javascript
774+
775+
const readonlyClient = client.withTransactionOptions({
776+
isolation: IsolationLevel.PreferRepeatableRead,
777+
readonly: true,
778+
});
779+
780+
// This transaction is less likely to raise a serialization error
781+
await readonlyClient.transaction(async (tx) => {
782+
// ...
783+
});
784+
746785
.. js:method:: withWarningHandler(handler: (warnings: errors.GelError[]) => void): Client
747786

748787
Returns a clone of the ``Client`` instance with the specified warning handler. Some queries may generate warnings while still returning a result. The ``handler`` function will be called with an array of ``GelError`` objects whenever the client encounters warnings during query execution.

docs/reference/using/python/api/advanced.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ Transactions can be customized with different options:
3737
3838
Repeatable read isolation level (supported in read-only transactions)
3939

40+
.. py:attribute:: PreferRepeatableRead
41+
42+
Uses repeatable read isolation level if server analysis concludes that it is supported for a given query.
43+
Otherwise, uses serializable isolation level.
44+
4045
:py:class:`TransactionOptions` can be set on :py:class:`~gel.Client` or :py:class:`~gel.AsyncIOClient` using one of these methods:
4146

4247
* :py:meth:`gel.Client.with_transaction_options`

0 commit comments

Comments
 (0)