Skip to content

Add recommendations on non-idempotent operations to vshard docs #5252

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: latest
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions doc/platform/sharding/vshard_admin.rst
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,93 @@ In a router application, you can define the ``put`` function that specifies how

Learn more at :ref:`vshard-process-requests`.

.. _vshard-deduplication:

Deduplication of non-idempotent requests
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

**Idempotent requests** produce the same result every time they are executed.
For example, a data read request or a multiplication by one are both idempotent.
Therefore, incrementing by one is an example of a non-idempotent operation.
When such an operation is applied again, the value for the field increases by 2 instead of just 1.

.. note::

Any write requests that are intended to be executed repeatedly should be idempotent.
The operations' idempotency ensures that the change is applied **only once**.

A request may need to be run again if an error occurs on the server or client side.
In this case:

- Read requests can be executed repeatedly.
To do this, the :ref:`vshard.router.call() <router_api-call>` (with ``mode=read``) uses the ``request_timeout`` parameter
(since ``vshard`` 0.1.28).
It is necessary to pass the ``request_timeout`` and ``timeout`` parameters together, with the following requirement:

.. code-block:: text

timeout > request_timeout


For example, if ``timeout = 10`` and ``request_timeout = 2``,
within 10 seconds the router is able to make up to 5 attempts (2 seconds each) to send a request to different replicas
until the request finally succeeds.

- Write requests (:ref:`vshard.router.callrw() <router_api-callrw>`) generally **cannot be re-executed** without checking
that the request has not been applied before.
Lack of such a check may lead to duplicate records or unplanned data changes.

For example, a client has sent a request to the server, waiting for a response within a specified timeout.
If the server sends a response about successful execution after this time has elapsed, the client will receive an error.
When re-executing the request without additional checking, the operation may be applied twice.

A write request can be re-executed without a check only if the error occurred on the server side --
for example, `ER_READONLY`.

**Deduplication examples**

To ensure idempotency of write requests (INSERT, UPDATE, UPSERT, and autoincrement),
it is necessary to implement a check in the code that the request is applied for the first time.

For example, when adding a new tuple to a space, a unique key by which the insertion is performed can be used for checking.
In such a request within a single transaction:

1. It is checked whether there is a tuple with the key ``key`` in the ``bands`` space.
2. If there is no record with such a key in the space, the tuple is inserted.

.. code-block:: lua

box.begin()
if box.space.bands:get{key} == nil then
box.space.bands:insert{key, value}
end
box.commit()

For tuple update requests, a separate *deduplication* space can be created, in which the request IDs will be saved.
*Deduplication space* is a user space that contains a list of unique identifiers.
Each such identifier corresponds to one executed request.
This space can have any name, in the example it is called ``deduplication``.

In the example below, within a single transaction:

1. The ``deduplication`` space is checked for the presence of the ``deduplication_key`` request ID.
2. If there is no such ID, this ID is added to the deduplication space.
3. The request then increments the specified field in the ``bands`` space by one.

This approach ensures that each data modification request will be executed **only once**.

.. code-block:: lua

function update_1(deduplication_key, key)
box.begin()
if box.space.deduplication:get{deduplication_key} == nil then
box.space.deduplication:insert{deduplication_key}
box.space.bands:update(key, {{'+', 'value', 1 }})
end
box.commit()
end


.. _vshard-maintenance:

Sharded cluster maintenance
Expand Down
6 changes: 6 additions & 0 deletions doc/reference/reference_rock/vshard/vshard_router.rst
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,12 @@ Router public API
optional attribute containing a message with the human-readable error description,
and other attributes specific for the error code.

.. note::

Any write requests that are planned to be executed repeatedly should be idempotent.
The idempotency of such operations ensures that the change from the operation is applied **only once**.
Read more: :ref:`<vshard-deduplication>`.

**Examples:**

To call ``customer_add`` function from ``vshard/example``, say:
Expand Down