Skip to content
Snippets Groups Projects
Commit c0ff70a0 authored by ocelot-inc's avatar ocelot-inc
Browse files

Fixes gh-1243 Clarify database operations description to take into account...

Fixes gh-1243 Clarify database operations description to take into account multi-statement transactions
parent c3a19104
No related branches found
No related tags found
No related merge requests found
.. _atomic_execution:
-------------------------------------------------------------------------------
Atomic execution
-------------------------------------------------------------------------------
......@@ -6,38 +8,51 @@ In several places in this manual it's been noted that Lua processes occur in fib
single thread. That is why there can be a guarantee of execution atomicity.
That requires emphasis.
.. _cooperative_multitasking:
===========================================================
Cooperative multitasking environment
===========================================================
Tarantool uses cooperative multi-tasking: unless a
running fiber deliberately yields control to some other fiber, it is not
preempted. “Yield points” are built into all calls from Tarantool core to the
operating system. Any system call which can block is performed in an
asynchronous manner and the fiber waiting on the system call is preempted with
a fiber ready to run. This model makes all programmatic locks unnecessary:
Tarantool uses cooperative multitasking: unless a
running fiber deliberately yields control, it is not
preempted by some other fiber.
But a running fiber will deliberately yield when it encounters a
"yield point": an explicit yield() request, or an implicit
yield due to an operating-system call.
Any system call which can block will be performed asynchronously,
and any running fiber which must wait for a system call will be
preempted so that another ready-to-run fiber takes its place and
becomes the new running fiber.
This model makes all programmatic locks unnecessary:
cooperative multitasking ensures that there will be no concurrency around a resource,
no race conditions and no memory consistency issues.
no race conditions, and no memory consistency issues.
When requests are small, e.g. simple UPDATE, INSERT, DELETE, SELECT, fiber
When requests are small, for example simple UPDATE or INSERT or DELETE or SELECT, fiber
scheduling is fair: it takes only a little time to process the request, schedule
a disk write, and yield to a fiber serving the next client.
A function, however, can perform complex computations, or be written in such a
way that control is not given away for a long time. This can lead to unfair
However, a function might perform complex computations or might be written in such a
way that yields do not occur for a long time. This can lead to unfair
scheduling, when a single client throttles the rest of the system, or to
apparent stalls in request processing. Avoiding this situation is the
responsibility of the function's author. Most of the box calls, such as
:func:`box.space...insert <space_object.insert>`,
:func:`box.space...update <space_object.update>`,
:func:`box.space...delete <space_object.delete>` are yield points;
responsibility of the function's author. For the default memtx storage engine
most of the box calls, including the data-change requests
:func:`box.space...insert <space_object.insert>` or
:func:`box.space...update <space_object.update>` or
:func:`box.space...delete <space_object.delete>`, are yield points;
however, :func:`box.space...select <space_object.select>` is not.
It should also be noted that, in the absence of transactions, any yield in a
function is a potential change in the database state. Effectively, it's only
possible to have CAS (compare-and-swap) -like atomic stored procedures: i.e.
functions which select and then modify a record. Multiple data change requests
always run through a built-in yield point.
Note re storage engine: sophia has different rules:
insert or update or delete will very rarely cause a yield,
but select can cause a yield.
In the absence of transactions, any function that contains yield points
may see changes in the database state caused by fibers that preempt.
Then the only safe atomic functions for memtx databases would be
functions which contain only one database request, or functions which
contain a select request followed by a data-change request.
At this point an objection could arise: "It's good that a single data-change
request will commit and yield, but surely there are times when multiple
......@@ -49,19 +64,19 @@ block was designed.
.. function:: box.begin()
From this point, implicit yields are suspended. In effect the fiber which
executes ``box.begin()`` is starting an "active multi-request transaction",
blocking all other fibers until the transaction ends. All operations within
this transaction should use the same storage engine.
Begin the transaction. Disable implicit yields until the transaction ends.
Signal that writes to the write-ahead log will be deferred until the transaction ends.
In effect the fiber which executes ``box.begin()`` is starting an "active
multi-request transaction", blocking all other fibers.
.. function:: box.commit()
End the currently active transaction, and make all its data-change
End the transaction, and make all its data-change
operations permanent.
.. function:: box.rollback()
End the currently active transaction, but cancel all its data-change
End the transaction, but cancel all its data-change
operations. An explicit call to functions outside ``box.space`` that always
yield, such as ``fiber.yield`` or ``fiber.sleep``, will have the same effect.
......@@ -70,6 +85,11 @@ It is not enough to enclose them between ``begin`` and ``commit`` or ``rollback`
To ensure they are sent as a single block: put them in a function, or put them all
on one line, or use a delimiter so that multi-line requests are handled together.
**All database operations in a transaction should use the same storage engine**.
It is not safe to access tuple sets that are defined with {engine='sophia'}
and also access tuple sets that are defined with {engine='memtx'},
in the same transaction.
===========================================================
Example
===========================================================
......
......@@ -325,6 +325,8 @@ because after the UPDATE the transaction processor thread can switch
to another fiber, and delete the tuple that was just updated.
Note re storage engine: sophia handles yields differently, see
:ref:`differences between memtx and sophia <sophia_diff>`.
Note re multi-request transactions: there is a way to delay yields,
see :ref:`Atomic execution <atomic_execution>`.
Since locks don't exist, and disk writes only involve the write-ahead log,
transactions are usually fast. Also the Tarantool server may not be using
......
......@@ -55,7 +55,7 @@
It was explained :ref:`earlier <yields_must_happen>` that memtx does not "yield" on a select request,
it yields only on data-change requests. However, sophia does yield on a select
request, or on an equivalent such as get() or pairs(). This has significance
for cooperative multitasking.
for :ref:`cooperative multitasking <cooperative_multitasking>`.
For more about sophia, see Appendix E :ref:`sophia <sophia>`.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment