You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 2458cf8
Browse filesBrowse the repository at this point in the historyBrowse files
refactor: simplify the timeout demo and address review findings
The metrics snapshot stays sorted, a rejected submission surfaces as ServiceCallException, TimeoutRegistry is dropped and the two demo services merge into DownstreamService. The class diagram is a rendered PNG.
Copy file name to clipboardExpand all lines: timeout/README.md
+35-69Lines changed: 35 additions & 69 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -59,6 +59,8 @@ sequenceDiagram
59
59
end
60
60
```
61
61
62
+

63
+
62
64
## Programmatic Example of Timeout Pattern in Java
63
65
64
66
The example models an online shop that calls two downstream services. The product catalog is fast; the recommendation engine is slow. Each gets its own time limit.
@@ -87,34 +89,9 @@ public record TimeoutPolicy(String serviceName, Duration timeout) {
87
89
}
88
90
```
89
91
90
-
2.**Keep the limits configurable in one place**
92
+
2.**Enforce the limit**
91
93
92
-
A `TimeoutRegistry` stores the policies and hands out a default for services nobody configured explicitly.
`TimeoutExecutor` runs the call on a worker thread and waits for at most the configured duration. On a timeout it cancels the worker with an interrupt, logs the event, counts it in `TimeoutMetrics`, and returns the fallback. A failure raised by the service is not a timeout and is rethrown as `ServiceCallException`.
94
+
`TimeoutExecutor` runs the call on a worker thread and waits for at most the configured duration. On a timeout it cancels the worker with an interrupt, logs the event, counts it in `TimeoutMetrics`, and returns the fallback. A failure raised by the service, or a call the pool refuses to accept, is not a timeout and is rethrown as `ServiceCallException`.
118
95
119
96
```java
120
97
@Slf4j
@@ -130,8 +107,9 @@ public class TimeoutExecutor implements AutoCloseable {
var result = future.get(limitMillis, TimeUnit.MILLISECONDS);
136
114
LOGGER.info("{} responded within its {} ms limit", serviceName, limitMillis);
137
115
return result;
@@ -147,6 +125,8 @@ public class TimeoutExecutor implements AutoCloseable {
147
125
future.cancel(true);
148
126
Thread.currentThread().interrupt();
149
127
thrownewServiceCallException(serviceName, e);
128
+
} catch (RejectedExecutionException e) {
129
+
thrownewServiceCallException(serviceName, e);
150
130
}
151
131
}
152
132
@@ -157,64 +137,52 @@ public class TimeoutExecutor implements AutoCloseable {
157
137
}
158
138
```
159
139
160
-
4.**Make the slow service cooperate with cancellation**
140
+
3.**Make the service cooperate with cancellation**
161
141
162
-
The simulated `RecommendationService` sleeps interruptibly, so the interrupt sent by the executor actually stops the work instead of leaving it running in the background.
142
+
The simulated `DownstreamService` sleeps interruptibly, so the interrupt sent by the executor actually stops the work instead of leaving it running in the background. Its name, latency and payload are constructor arguments, so the same class plays both the fast catalog and the slow recommendation engine.
`App`registers a 500 ms limit for the catalog and a 100 ms limit for recommendations. Each call lives in a small helper that pairs the policy with the call and its fallback; `main` runs both. The catalog answers in time; the recommendation engine needs 400 ms, so the customer sees popular items instead and the timeout counter shows one event.
159
+
`App`gives the catalog a 500 ms limit and recommendations a 100 ms one, then runs both calls through a small helper that pairs the policy with the call and its fallback. The catalog answers in time; the recommendation engine needs 400 ms, so the customer sees popular items instead and the timeout counter shows one event.
var catalogPolicy =TimeoutPolicy.of(catalog.name(), 500);
179
+
var recommendationPolicy =TimeoutPolicy.of(recommendations.name(), 100);
212
180
213
181
try (var executor =newTimeoutExecutor()) {
214
-
var products =loadProducts(executor, registry, catalog);
182
+
var products =call(executor, catalogPolicy, catalog, List.of());
215
183
LOGGER.info("Products: {}", products);
216
184
217
-
var suggested =loadRecommendations(executor, registry, recommendations, "alice");
185
+
var suggested =call(executor, recommendationPolicy, recommendations, POPULAR_ITEMS);
218
186
LOGGER.info("Recommendations shown to alice: {}", suggested);
219
187
220
188
LOGGER.info("Timeouts per service: {}", executor.metrics().snapshot());
@@ -226,20 +194,18 @@ Running the application produces output along these lines:
226
194
```
227
195
Configured per-service limits: catalog 500 ms, recommendations 100 ms
228
196
Calling product-catalog
229
-
product-catalog: fetching products, expected latency 50 ms
197
+
product-catalog: responding, expected latency 50 ms
230
198
product-catalog responded within its 500 ms limit
231
199
Products: [Laptop, Headphones, Monitor]
232
200
Calling recommendations
233
-
recommendations: computing recommendations for alice, expected latency 400 ms
201
+
recommendations: responding, expected latency 400 ms
202
+
recommendations: interrupted, abandoning the call
234
203
recommendations exceeded its 100 ms limit; call cancelled, using fallback
235
-
recommendations: interrupted, abandoning the computation for alice
236
204
Recommendations shown to alice: [Wireless mouse, Webcam]
237
205
Timeouts per service: {recommendations=1}
238
206
```
239
207
240
-
## Class diagram
241
-
242
-
See [timeout.urm.puml](./etc/timeout.urm.puml) for the PlantUML class diagram.
208
+
The "exceeded its 100 ms limit" line is written by the caller and the "interrupted, abandoning" line by the worker thread, so the two may appear in either order from run to run.
0 commit comments