@@ -551,6 +551,93 @@ In a router application, you can define the ``put`` function that specifies how
551
551
552
552
Learn more at :ref: `vshard-process-requests `.
553
553
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 are 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(mode=read) <router_api-call >` uses the `request_timeout ` parameter
574
+ (since `vshard ` 0.1.28).
575
+ The `request_timeout ` and `timeout ` parameters should be passed together, observing 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 queries, such as data insert, update, and upsert, as well as autoincrement,
600
+ it is necessary to implement a check in the code that the request is being used 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
+
554
641
.. _vshard-maintenance :
555
642
556
643
Sharded cluster maintenance
0 commit comments