@@ -551,6 +551,104 @@ 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 executed.
560+ For example, a data read request or a multiplication by one are both idempotent.
561+ Therefore, incrementing by one is an example of a non-idempotent operation.
562+ When such an operation is applied again, the value for the field increases by 2 instead of just 1.
563+
564+ .. note ::
565+
566+ Any write requests that are intended to be executed repeatedly (for example, retried after an error) should be idempotent.
567+ The operations' idempotency ensures that the change is applied **only once **.
568+
569+ A request may need to be run again if an error occurs on the server or client side.
570+ In this case:
571+
572+ - Read requests can be executed repeatedly.
573+ For this purpose, :ref: `vshard.router.call() <router_api-call >` (with ``mode=read ``) uses the ``request_timeout `` parameter
574+ (since ``vshard `` 0.1.28).
575+ It is necessary to pass the ``request_timeout `` and ``timeout `` parameters together, with the following requirement:
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 5 attempts (2 seconds each) to send a request to different replicas
584+ until the request finally succeeds.
585+
586+ - Write requests (:ref: `vshard.router.callrw() <router_api-callrw >`) generally **cannot be re-executed ** without verifying
587+ that they have not been applied before.
588+ Lack of such a check might lead to duplicate records or unplanned data changes.
589+
590+ For example, a client has sent a request to the server. The client is waiting for a response within a specified timeout.
591+ If the server sends a successful response after this time has elapsed,
592+ the client won't see this response due to a timeout, and will consider the request as failed.
593+ When re-executing this request without additional check, the operation may be applied twice.
594+
595+ A write request can be executed repeatedly without a check in two cases:
596+
597+ - The request is idempotent.
598+
599+ - It's known for sure that the previous request raised an error before executing any write operations.
600+ For example, ER_READONLY was thrown by the server.
601+ In this case, we know that the request couldn't complete due to server in read-only mode.
602+
603+ **Deduplication examples **
604+
605+ To ensure that the write requests (INSERT, UPDATE, UPSERT, and autoincrement) are idempotent,
606+ you should implement a check that the request is applied for the first time.
607+
608+ .. note ::
609+
610+ There is no built-in deduplication check in Tarantool.
611+ Currently, deduplication can be only implemented by the user in the application code.
612+
613+ For example, when you add a new tuple to a space, you can use a unique insert ID to check the request.
614+ In the example below, within a single transaction:
615+
616+ 1. It is checked whether a tuple with the ``key `` ID exists in the ``bands `` space.
617+ 2. If there is no tuple with this ID in the space, the tuple is inserted.
618+
619+ .. code-block :: lua
620+
621+ box.begin()
622+ if box.space.bands:get{key} == nil then
623+ box.space.bands:insert{key, value}
624+ end
625+ box.commit()
626+
627+ For update and upsert requests, you can create a *deduplication space * where the request IDs will be saved.
628+ *Deduplication space * is a user space that contains a list of unique identifiers.
629+ Each identifier corresponds to one applied request.
630+ This space can have any name, in the example it is called ``deduplication ``.
631+
632+ In the example below, within a single transaction:
633+
634+ 1. It is checked whether the ``deduplication_key `` request ID exists in the ``deduplication `` space.
635+ 2. If there is no such ID, the ID is added to the deduplication space.
636+ 3. If the request hasn't been applied before, it increments the specified field in the ``bands `` space by one.
637+
638+ This approach ensures that each data modification request will be executed **only once **.
639+
640+ .. code-block :: lua
641+
642+ function update_1(deduplication_key, key)
643+ box.begin()
644+ if box.space.deduplication:get{deduplication_key} == nil then
645+ box.space.deduplication:insert{deduplication_key}
646+ box.space.bands:update(key, {{'+', 'value', 1 }})
647+ end
648+ box.commit()
649+ end
650+
651+
554652 .. _vshard-maintenance :
555653
556654Sharded cluster maintenance
0 commit comments