Skip to content

Commit badeae4

Browse files
authored
gh-2906 Update the transactions section (#2934)
* Rewrite sections * Update toctree * Delete streams, move sections * Improve the text, add links * Add details to txn: default * Fix a link * Add read committed * Impove the text * Add MVCC best effort * Rewrite tnx_model, fix remarks * Fix links * Fix remarks * Improve wording * Fix remarks * Minor fixes * Impove yields.svg * Add links, improve wording * Fix a link * Fix yields.svg * Improve client.lua * Fix warnings
1 parent d14d802 commit badeae4

18 files changed

+777
-375
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,29 @@
1-
.. _atomic-cooperative_multitasking:
2-
3-
Cooperative multitasking
4-
========================
5-
6-
Cooperative multitasking means that unless a running fiber deliberately yields
7-
control, it is not preempted by some other fiber. But a running fiber will
8-
deliberately yield when it encounters a “yield point”: a transaction commit,
9-
an operating system call, or an explicit :ref:`"yield" <fiber-yield>` request.
10-
Any system call which can block will be performed asynchronously, and any running
11-
fiber which must wait for a system call will be preempted, so that another
12-
ready-to-run fiber takes its place and becomes the new running fiber.
13-
14-
This model makes all programmatic locks unnecessary: cooperative multitasking
15-
ensures that there will be no concurrency around a resource, no race conditions,
16-
and no memory consistency issues. The way to achieve this is simple:
17-
Use no yields, explicit or implicit in critical sections, and no one can
18-
interfere with code execution.
19-
20-
When dealing with small requests, such as simple UPDATE or INSERT or DELETE or
21-
SELECT, fiber scheduling is fair: it takes only a little time to process the
22-
request, schedule a disk write, and yield to a fiber serving the next client.
23-
24-
However, a function may perform complex computations or be written in
25-
such a way that yields take a long time to occur. This can lead to
26-
unfair scheduling when a single client throttles the rest of the system, or to
27-
apparent stalls in request processing. Avoiding this situation is
28-
the responsibility of the function’s author.
1+
.. _app-cooperative_multitasking:
2+
3+
Cooperative multitasking
4+
========================
5+
6+
Cooperative multitasking means that unless a running fiber deliberately yields
7+
control, it is not preempted by some other fiber. But a running fiber will
8+
deliberately yield when it encounters a “yield point”: a transaction commit,
9+
an operating system call, or an explicit :ref:`"yield" <fiber-yield>` request.
10+
Any system call which can block will be performed asynchronously, and any running
11+
fiber which must wait for a system call will be preempted, so that another
12+
ready-to-run fiber takes its place and becomes the new running fiber.
13+
14+
This model makes all programmatic locks unnecessary: cooperative multitasking
15+
ensures that there will be no concurrency around a resource, no race conditions,
16+
and no memory consistency issues. The way to achieve this is simple:
17+
Use no yields, explicit or implicit in critical sections, and no one can
18+
interfere with code execution.
19+
20+
For small requests, such as simple UPDATE or INSERT or DELETE or
21+
SELECT, fiber scheduling is fair: it takes little time to process the
22+
request, schedule a disk write, and yield to a fiber serving the next client.
23+
24+
However, a function may perform complex calculations or be written in
25+
such a way that yields take a long time to occur. This can lead to
26+
unfair scheduling when a single client throttles the rest of the system, or to
27+
apparent stalls in processing requests. It is the responsibility of the function
28+
author to avoid this situation.
29+

doc/book/app_server/index.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ This chapter contains the following sections:
2121
installing_module
2222
contributing_module
2323
reloading_module
24+
yields
25+
cooperative_multitasking
2426
luajit_memprof
2527
luajit_getmetrics
2628
using_ide

doc/book/app_server/yields.rst

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
.. _app-yields:
2+
3+
Yields
4+
======
5+
6+
Any live fiber can be in one of three states: ``running``, ``suspended``, and
7+
``ready``. After a fiber dies, the ``dead`` status returns. By observing
8+
fibers from the outside, you can only see ``running`` (for the current fiber)
9+
and ``suspended`` for any other fiber waiting for an event from eventloop (``ev``)
10+
for execution.
11+
12+
13+
.. image:: yields.svg
14+
:align: center
15+
16+
17+
Yield is an action that occurs in a :ref:`cooperative <app-cooperative_multitasking>` environment that
18+
transfers control of the thread from the current fiber to another fiber that is ready to execute.
19+
20+
21+
After yield has occurred, the next ``ready`` fiber is taken from the queue and executed.
22+
When there are no more ``ready`` fibers, execution is transferred to the event loop.
23+
24+
After a fiber has yielded and regained control, it immediately issues :ref:`testcancel <fiber-testcancel>`.
25+
26+
There are :ref:`explicit <app-explicit-yields>` and :ref:`implicit <app-implicit-yields>` yields.
27+
28+
.. _app-explicit-yields:
29+
30+
Explicit yields
31+
---------------
32+
33+
**Explicit yield** is clearly visible from the invoking code. There are only two
34+
explicit yields: :ref:`fiber.yield() <fiber-yield>` and :ref:`fiber.sleep(t) <fiber-sleep>`
35+
36+
:ref:`fiber.yield() <fiber-yield>` yields execution to another ``ready`` fiber while putting itself in the ``ready`` state,
37+
meaning that it will be executed again as soon as possible while being polite to other fibers
38+
waiting for execution.
39+
40+
:ref:`fiber.sleep(n) <fiber-sleep>` yields execution to another ``ready`` fiber and puts itself in the ``suspended``
41+
state for time ``t`` until time pass and the event loop wakes up that fiber to the ``ready`` state.
42+
43+
In general, it is good behavior for long-running cpu-intensive tasks to yield periodically to
44+
be :ref:`cooperative <app-cooperative_multitasking>` to other waiting fibers.
45+
46+
.. _app-implicit-yields:
47+
48+
Implicit yields
49+
---------------
50+
51+
On the other hand, there are many operations, such as operations with sockets, file system,
52+
and disk I/O, which imply some waiting for the current fiber while others can be
53+
executed. When such an operation occurs, a possible blocking operation would be passed into the
54+
event loop and the fiber would be suspended until the resource is ready to
55+
continue fiber execution.
56+
57+
Here is the list of implicitly yielding operations:
58+
59+
* Connection establishment (:ref:`socket <socket-module>`).
60+
61+
* Socket read and write (:ref:`socket <socket-module>`).
62+
63+
* Filesystem operations (from :ref:`fio <fio-section>`).
64+
65+
* Channel data transfer (:ref:`fiber.channel <fiber-channel>`).
66+
67+
* File input/output (from :ref:`fio <fio-section>`).
68+
69+
* Console operations (since console is a socket).
70+
71+
* HTTP requests (since HTTP is a socket operation).
72+
73+
* Database modifications (if they imply a disk write).
74+
75+
* Database reading for the :ref:`vinyl <engines-chapter>` engine.
76+
77+
* Invocation of another process (:ref:`popen <popen-module>`).
78+
79+
.. note::
80+
81+
Please note that all operations of ``os`` mosule are non-cooperative and
82+
exclusively block the whole tx thread.
83+
84+
For :ref:`memtx <engines-chapter>`, since all data is in memory, there is no yielding for a read requests
85+
(like ``:select``, ``:pairs``, ``:get``).
86+
87+
For :ref:`vinyl <engines-chapter>`, since some data may not be in memory, there may be disk I/O for a
88+
read (to fetch data from disk) or write (because a stall may occur while waiting for memory to be freed).
89+
90+
For both :ref:`memtx <engines-chapter>` and :ref:`vinyl <engines-chapter>`, since data change requests
91+
must be recorded in the :ref:`WAL <internals-wal>`, there is normally a :doc:`/reference/reference_lua/box_txn_management/commit`.
92+
93+
With the default ``autocommit`` mode the following operations are yielding:
94+
95+
* :ref:`space:alter <box_space-alter>`.
96+
97+
* :ref:`space:drop <box_space-drop>`.
98+
99+
* :ref:`space:create_index <box_space-create_index>`.
100+
101+
* :ref:`space:truncate <box_space-truncate>`.
102+
103+
* :ref:`space:insert <box_space-insert>`.
104+
105+
* :ref:`space:replace <box_space-replace>`.
106+
107+
* :ref:`space:update <box_space-update>`.
108+
109+
* :ref:`space:upserts <box_space-upsert>`.
110+
111+
* :ref:`space:delete <box_space-delete>`.
112+
113+
* :ref:`index:update <box_index-update>`.
114+
115+
* :ref:`index:delete <box_index-delete>`.
116+
117+
* :ref:`index:alter <box_index-alter>`.
118+
119+
* :ref:`index:drop <box_index-drop>`.
120+
121+
* :ref:`index:rename <box_index-rename>`.
122+
123+
* :ref:`box.commit <box-commit>` (*if there were some modifications within the transaction*).
124+
125+
To provide atomicity for transactions in transaction mode, some changes are applied to the
126+
modification operations for the :ref:`memtx <engines-chapter>` engine. After executing
127+
:ref:`box.begin <box-begin>` or within a :ref:`box.atomic <box-atomic>`
128+
call, any modification operation will not yield, and yield will occur only on :ref:`box.commit <box-commit>` or upon return
129+
from :ref:`box.atomic <box-atomic>`. Meanwhile, :ref:`box.rollback <box-rollback>` does not yield.
130+
131+
That is why executing separate commands like ``select()``, ``insert()``, ``update()`` in the console inside a
132+
transaction without MVCC will cause it to an abort. This is due to implicit yield after each
133+
chunk of code is executed in the console.
134+
135+
136+
**Example #1**
137+
138+
* Engine = memtx.
139+
140+
.. code-block:: memtx
141+
142+
space:get()
143+
space:insert()
144+
145+
146+
The sequence has one yield, at the end of the insert, caused by implicit commit;
147+
``get()`` has nothing to write to the :ref:`WAL <internals-wal>` and so does not yield.
148+
149+
* Engine = memtx.
150+
151+
.. code-block:: memtx
152+
153+
box.begin()
154+
space1:get()
155+
space1:insert()
156+
space2:get()
157+
space2:insert()
158+
box.commit()
159+
160+
161+
The sequence has one yield, at the end of the ``box.commit``, none of the inserts are yielding.
162+
163+
* Engine = vinyl.
164+
165+
.. code-block:: vinyl
166+
167+
space:get()
168+
space:insert()
169+
170+
171+
The sequence has one to three yields, since ``get()`` may yield if the data is not in the cache,
172+
``insert()`` may yield if it waits for available memory, and there is an implicit yield
173+
at commit.
174+
175+
* Engine = vinyl.
176+
177+
.. code-block:: vinyl
178+
179+
box.begin()
180+
space1:get()
181+
space1:insert()
182+
space2:get()
183+
space2:insert()
184+
box.commit()
185+
186+
187+
The sequence may yield from 1 to 5 times.
188+
189+
190+
**Example #2**
191+
192+
Assume that there are tuples in the :ref:`memtx <engines-chapter>` space ``tester`` where the third field
193+
represents a positive dollar amount.
194+
195+
196+
Let's start a transaction, withdraw from tuple#1, deposit in tuple#2, and end
197+
the transaction, making its effects permanent.
198+
199+
.. code-block:: tarantoolsession
200+
201+
tarantool> function txn_example(from, to, amount_of_money)
202+
> box.atomic(function()
203+
> box.space.tester:update(from, {{'-', 3, amount_of_money}})
204+
> box.space.tester:update(to, {{'+', 3, amount_of_money}})
205+
> end)
206+
> return "ok"
207+
> end
208+
209+
Result:
210+
---
211+
...
212+
tarantool> txn_example({999}, {1000}, 1.00)
213+
---
214+
- "ok"
215+
...
216+
217+
If :ref:`wal_mode <cfg_binary_logging_snapshots-wal_mode>` = ``none``, then
218+
there is no implicit yielding at the commit time because there are
219+
no writes to the :ref:`WAL <internals-wal>`.
220+
221+
If a request if performed via network connector such as :ref:`net.box <net_box-module>` and implies
222+
sending requests to the server and receiving responses, then it involves network
223+
I/O and thus implicit yielding. Even if the request that is sent to the server
224+
has no implicit yield. Therefore, the following sequence causes yields
225+
three times sequentially when sending requests to the network and awaiting the results.
226+
227+
228+
.. cssclass:: highlight
229+
.. parsed-literal::
230+
231+
conn.space.test:get{1}
232+
conn.space.test:get{2}
233+
conn.space.test:get{3}

doc/book/app_server/yields.svg

Lines changed: 4 additions & 0 deletions
Loading

doc/book/box/atomic.rst

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ For more information on how transactions work in Tarantool, see the following se
1313
.. toctree::
1414
:maxdepth: 2
1515

16-
atomic/atomic-threads_fibers_yields
17-
atomic/atomic-cooperative_multitasking
18-
atomic/atomic-transactions
19-
atomic/atomic-implicit-yields
20-
atomic/atomic-transactional-manager
16+
atomic/transaction_model
17+
atomic/thread_model
18+
atomic/txn_mode_default
19+
atomic/txn_mode_mvcc
20+

0 commit comments

Comments
 (0)