Wiki source code of Batch запити

Last modified by Ashterix on 2024/05/16 18:58

Show last authors
1 {{box cssClass="floatinginfobox" title="**Content**"}}
2 {{toc/}}
3 {{/box}}
4
5 = Introduction =
6
7 (% class="box infomessage" %)
8 (((
9 Batch requests to the API are an approach that allows you to combine multiple requests into one. Instead of sending numerous separate requests to the server, the client can send a single request containing multiple operations. The server receives this request, processes all the operations, and returns a single response that includes the results of all the requests.
10 )))
11
12 Let's imagine we are developing a backend API for an online store and we already have a method {{code language="none"}}ProductService.getInfo{{/code}} for the entity {{code language="none"}}Product{{/code}}. The task on the frontend is to implement the logic of adding products to the cart and updating product information upon request (for example, when opening the cart, we need to update information about the products the user added: availability, prices, descriptions, etc.).
13
14 == Scenario that 100% of developers follow ==
15
16 A task is set for the backend to create a separate API method that returns a collection of objects by an array of identifiers. This requires the working time of a backend developer, a tester, and a frontend developer since the new method must be covered by unit tests and additional scenarios for regression testing. This must be done despite having a method that can return one product by its id.
17
18 == Scenario that no one follows ==
19
20 Send a separate API request for each product to get the current information. If there are 10 products in the cart, this means 10 HTTP requests. This creates significant network load and increases waiting time for the user, which is an unacceptable option.
21
22 == Alternative using batch requests ==
23
24 If we have the ability to send and process batch requests, we can significantly simplify the implementation of business logic. We can use a batch request to combine all these requests into one. We send a single request to the API containing all the product identifiers, and the server returns information about all the products in one response. Thus, we get:
25
26 1. **Universality**: Using batch requests allows us to avoid creating specialized methods for each case. This simplifies the API and reduces the amount of required code.
27 2. **Convenience for the client**: The client can independently determine which requests to combine, making the API more flexible and convenient to use.
28 3. **Reduction of backend complexity**: Instead of writing and maintaining specialized methods for different scenarios, we can use a universal approach with batch requests, simplifying backend architecture.
29
30 (% class="box successmessage" %)
31 (((
32 This approach provides more efficient use of resources and improves performance, reducing the load on the network and servers, and enhances the user experience.
33 )))
34
35 (% class="box warningmessage" %)
36 (((
37 Of course, in high-load systems, such a scenario may be inefficient, but there are other optimization options that we will consider in other articles.
38 )))
39
40 = Example =
41
42 Consider the request example I mentioned above.
43
44 (% class="row" %)
45 (((
46 (% class="col-xs-12 col-sm-6" %)
47 (((
48 {{code language="json" layout="LINENUMBERS" title="Request"}}
49 [
50 {
51 "id":"example_1",
52 "method":"ProductService.getInfo",
53 "params":{
54 "productId": 345234
55 }
56 },
57 {
58 "id":"example_2",
59 "method":"ProductService.getInfo",
60 "params":{
61 "productId": 994234
62 }
63 }
64 ]
65 {{/code}}
66
67 (% class="box warningmessage" %)
68 (((
69 **Important!!!** Always specify unique request ids in batch requests, as responses may be returned in a different order.
70 )))
71 )))
72
73 (% class="col-xs-12 col-sm-6" %)
74 (((
75 {{code language="json" layout="LINENUMBERS" title="Response"}}
76 [
77 {
78 "id":"example_2",
79 "result": {
80 "id": 994234,
81 "name": "product 2",
82 "price": 11.99,
83 "balance": 28
84 }
85 },
86 {
87 "id":"example_1",
88 "result": {
89 "id": 345234,
90 "name": "product 1",
91 "price": 99.99,
92 "balance": 14
93 }
94 }
95 ]
96 {{/code}}
97 )))
98 )))
99
100 == Advantages of Batch Requests ==
101
102 1. **Simplifying the backend**: Instead of implementing specific logic on the backend each time, it can be transferred to the level of building client requests.
103 2. **Reducing the number of network requests**: Instead of sending multiple HTTP requests, which can create network and server load, only one request is used. This significantly reduces network and server resource load.
104 3. **Response time**: The RPC Server will return the result of all requests, regardless of their number, in the time required to process the longest request in the batch, as all requests are processed in parallel.
105 4. **Reducing latency**: Executing multiple requests in one connection can reduce overall latency as less time is spent on establishing and closing connections.
106 5. **Optimizing client and server resource use**: Fewer requests are processed simultaneously, reducing server load and allowing it to handle more requests. The client also uses fewer resources, which can be critical for mobile and limited devices.
107 6. **Improved transaction management**: When multiple operations need to be performed atomically (all or none), a batch request allows for easier implementation, as all operations are executed within one request.
108
109 = Preparing for Use =
110
111 Batch request functionality is available immediately and does not require additional settings from the developer.
112
113 = Request Order =
114
115 The order of filling the batch does not matter because on the RPC server all requests are executed in parallel, and you will receive a response at the speed of the longest request in the list.
116
117 If an error occurs in one of the requests in the list, it does not affect the processing of other requests in the list.
118
119 = Dependent Requests =
120
121 (% class="box infomessage" %)
122 (((
123 The batch request mechanism allows creating dependent requests. This is a request where one or more parameters depend on the response from another request in the list.
124 )))
125
126 (% class="wikigeneratedid" %)
127 Imagine that we need to get the user's email address by their identifier from the frontend and then send a message to this address. We already have the methods {{code language="none"}}UserService.getInfo{{/code}} and {{code language="none"}}Messenger.sendEmail{{/code}}. In a classical scenario, this would be either two requests or a separate method on the backend that sends an email by user id.
128
129 (% class="wikigeneratedid" %)
130 Using the {{code language="none"}}JsonRpcBundle{{/code}} library from UFO-Tech, you can create batch requests where one request can depend on another and use its response to execute. In the context of the example, we get the user's information in one request, and the second request uses the received email to send a message.
131
132 (% class="row" %)
133 (((
134 (% class="col-xs-12 col-sm-6" %)
135 (((
136 {{code language="json" layout="LINENUMBERS" title="Request"}}
137 [
138 {
139 "id":"example_1",
140 "method":"UserService.getInfo",
141 "params":{
142 "userId": 141
143 }
144 },
145 {
146 "id":"example_2",
147 "method":"Messenger.sendEmail",
148 "params":{
149 "email": "@FROM:example_1(email)",
150 "subject": "Welcome!",
151 "message": "Thank you for joining our service!"
152 }
153 }
154 ]
155 {{/code}}
156 )))
157
158 (% class="col-xs-12 col-sm-6" %)
159 (((
160 {{code language="json" layout="LINENUMBERS" title="Response"}}
161 [
162 {
163 "id":"example_1",
164 "result": {
165 "id": 141,
166 "name": "John",
167 "email": "john.dou@example.com",
168 "status": 1
169 }
170 },
171 {
172 "id":"example_2",
173 "result": "Email for 'john.dou@example.com' sent"
174 }
175 ]
176 {{/code}}
177 )))
178 )))
179
180 In this example:
181
182 1. The first request retrieves user information by id 141, including their email.
183 2. The second request sends an email to the received email using data from the first request.
184
185 (% class="box infomessage" %)
186 (((
187 **The request order does not matter!** The RPC Server determines the dependencies of the requests and queues them in the required order.
188 )))
189
190 (% class="box warningmessage" %)
191 (((
192 Dependent requests are executed sequentially, so the response to the batch request will be returned after the dependent requests are completed.
193 )))
194
195 == Advantages of Using Dependent Requests ==
196
197 1. **Convenience and efficiency**: Dependent requests allow performing complex operations with minimal effort, ensuring the correct order of request execution.
198 2. **Reducing the number of network requests**: All requests are combined into one, reducing network load.
199 3. **Flexibility**: The ability to create complex request scenarios without additional settings or changes on the backend.
200
201 = How It Works =
202
203 The batch request processing mechanism works asynchronously.
204
205 When receiving an array of requests, the RPC Server creates a queue of [[Symfony Process>>https://symfony.com/doc/current/components/process.html]], i.e., it runs CLI commands that are processed asynchronously. In a while loop, the state of the processes is checked, and if a result is obtained, it is added to the response array. If there is no response before the timeout expires, an error is returned indicating that the request was not processed.
206
207 == Algorithm ==
208
209 * The batch request is split into individual requests, each of which is added to the queue.
210 * The queue is checked in a loop for the presence of objects.
211 * For each object in the queue, it is checked whether the process has finished.
212 * If the process has finished, the result is added to the response array, and the process is removed from the queue.
213 * If the process has not finished and the timeout has not expired, the loop continues.
214 * If the timeout expires before receiving the result, an error is returned for that specific request indicating that it was not processed.
215
216 To increase the timeout for a batch request, you can specify an additional service parameter {{code language="none"}}$rpc.timeout{{/code}} in the request parameters, which is the maximum number of seconds to wait for a response from the process. By default, the timeout value is 10 seconds.
217
218 {{code language="json" layout="LINENUMBERS" title="Request"}}
219 [
220 {
221 "id":"example_1",
222 "method":"ExampleApi.fastMethod",
223 "params":{
224 "someParam": "someValue"
225 }
226 },
227 {
228 "id":"example_2",
229 "method":"ExampleApi.longMethod",
230 "params":{
231 "someParam": "someValue",
232 "$rpc.timeout": 30
233 }
234 }
235 ]
236 {{/code}}
237
238 This allows you to adjust the duration of waiting for results for methods that might take longer to execute, which can be important for processing certain requests.