Middle+
There is a backend and a UI. The stack is standard: Java, Spring, React, Postgres. Task: Design a REST endpoint that needs to fetch data from 3 sources and deliver it to the UI, while ensuring maximum possible throughput and minimal response time (max/min criteria based on our specific conditions, since essentially, there is no silver bullet here and we need to find a compromise). It is known that the expected peak RPS for this REST endpoint will be 200. Details about the sources: 1st source - our database, query takes about 3 seconds. 2nd source - a related REST service, degrades to 2 minutes under 100 RPS, in normal mode responds in 5 seconds. We cannot change the behavior of this source on their side. 3rd source - a related REST service, randomly (no discernible pattern) returns 503, in normal mode response takes 10 seconds. We cannot change the behavior of this source on their side.
sobes.tech AI
Answer from AI
To design a REST endpoint with three data sources and requirements for maximum throughput and minimal response time, considering the features of the sources, the following approach can be proposed:
-
Asynchronous calls to sources:
- The database request (3 seconds) can be performed in parallel with calls to REST services.
- Calls to the two REST services should also be performed in parallel.
-
Caching:
- For the second source, which degrades under high load, caching results can be used to reduce the number of requests to it.
- The cache can be updated periodically or upon an event.
-
Error handling and timeouts:
- For the third source, which may return 503, a retry mechanism with exponential backoff and fallback (e.g., returning the last successful data or a placeholder) should be implemented.
- Set reasonable timeouts for all requests to avoid blocking the UI response.
-
Thread pool and concurrency limit:
- Use a thread pool with a limit on the number of concurrent requests for REST services to prevent overload of external services.
-
Data collection and aggregation:
- After receiving data from all sources, aggregate them and send to the UI.
-
Monitoring and metrics:
- Implement response time and error monitoring to promptly react to degradation.
An example simplified Java code using CompletableFuture:
CompletableFuture<Data1> dbFuture = CompletableFuture.supplyAsync(() -> fetchFromDb());
CompletableFuture<Data2> service2Future = CompletableFuture.supplyAsync(() -> fetchFromService2WithCache());
CompletableFuture<Data3> service3Future = CompletableFuture.supplyAsync(() -> fetchFromService3WithRetry());
CompletableFuture.allOf(dbFuture, service2Future, service3Future).join();
Data1 d1 = dbFuture.get();
Data2 d2 = service2Future.get();
Data3 d3 = service3Future.get();
Response response = aggregate(d1, d2, d3);
return response;
Thus, a balance is achieved between response time and load on external services, as well as ensuring fault tolerance.