@@ -551,6 +551,93 @@ In a router application, you can define the ``put`` function that specifies how
551551
552552Learn more at :ref: `vshard-process-requests `.
553553
554+ .. _vshard-deduplication :
555+
556+ Deduplication of non-idempotent requests
557+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
558+
559+ **Idempotent requests ** produce the same result every time they are called.
560+ For example, a data read request or a multiplication by one are idempotent.
561+ So, increment by one is an example of a non-idempotent operation.
562+ When such an operation is applied again, the value for the field will be increased by 2 instead of 1.
563+
564+ .. note ::
565+
566+ Any write requests that are planned to be executed repeatedly should be idempotent.
567+ The idempotency of such operations ensures that the change from the operation is applied **only once **.
568+
569+ A request may need to be re-executed if an error occurs on the server or client side.
570+ In this case:
571+
572+ - Read requests can be executed repeatedly.
573+ To do this, the :ref: `vshard.router.call() <router_api-call >` (with ``mode=read ``) uses the ``request_timeout `` parameter
574+ (since ``vshard `` 0.1.28).
575+ The ``request_timeout `` and ``timeout `` parameters should be passed together, with the following condition:
576+
577+ .. code-block :: text
578+
579+ timeout > request_timeout
580+
581+
582+ For example, if ``timeout = 10 `` and ``request_timeout = 2 ``,
583+ within 10 seconds the router is able to make up to 5 attempts to contact (2 seconds each) with a request to different replicas,
584+ until the request finally succeeds.
585+
586+ - Write requests (:ref: `vshard.router.callrw() <router_api-callrw >`) generally **can't be re-executed ** without checking
587+ that the request has not been applied before.
588+ Lack of such a check may lead to duplicate records or unplanned data changes.
589+
590+ For example, a client has sent a request to the server, waiting for response within a specified timeout.
591+ If the server sends a response about successful execution after this time has elapsed, the client will receive an error.
592+ When re-executing the request without additional checking, the operation may be applied twice.
593+
594+ A write request can be re-executed without a check only if the error occurred on the server side --
595+ for example, `ER_READONLY `.
596+
597+ **Deduplication examples **
598+
599+ To ensure idempotency of write requests (INSERT, UPDATE, UPSERT, and autoincrement),
600+ it is necessary to implement a check in the code that the request is applied for the first time.
601+
602+ For example, when adding a new tuple to a space, a unique key by which the insertion is performed can be used for checking.
603+ In such a request within a single transaction:
604+
605+ 1. It is checked whether there is a tuple with the key ``key `` in the ``bands `` space.
606+ 2. If there is no record with such a key in the space, the tuple is inserted.
607+
608+ .. code-block :: lua
609+
610+ box.begin()
611+ if box.space.bands:get{key} == nil then
612+ box.space.bands:insert{key, value}
613+ end
614+ box.commit()
615+
616+ For tuple update requests, a separate *deduplication * space can be created, in which the request IDs will be saved.
617+ *Deduplication space * is a user space that contains a list of unique identifiers.
618+ Each such identifier corresponds to one executed request.
619+ This space can have any name, in the example it is called ``deduplication ``.
620+
621+ In the example below, within a single transaction:
622+
623+ 1. The ``deduplication `` space is checked for the presence of the ``deduplication_key `` request ID.
624+ 2. If there is no such ID, this ID is added to the deduplication space.
625+ 3. The request then increments the specified field in the ``bands `` space by one.
626+
627+ This approach ensures that each data modification request will be executed **only once **.
628+
629+ .. code-block :: lua
630+
631+ function update_1(deduplication_key, key)
632+ box.begin()
633+ if box.space.deduplication:get{deduplication_key} == nil then
634+ box.space.deduplication:insert{deduplication_key}
635+ box.space.bands:update(key, {{'+', 'value', 1 }})
636+ end
637+ box.commit()
638+ end
639+
640+
554641 .. _vshard-maintenance :
555642
556643Sharded cluster maintenance
0 commit comments