Skip to content

Commit 60c5cd3

Browse files
authored
Merge pull request #45 from digma-ai/feature/rds-load
Feature/rds load
2 parents 11bd82b + d3e4270 commit 60c5cd3

File tree

2 files changed

+142
-0
lines changed

2 files changed

+142
-0
lines changed

src/main/java/org/springframework/samples/petclinic/clinicactivity/ClinicActivityController.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,39 @@ public ResponseEntity<String> recreateAndPopulateLogs(@RequestParam(name = "coun
161161
}
162162
}
163163

164+
@PostMapping("/io-intensive-load")
165+
public ResponseEntity<String> createIOIntensiveLoad(@RequestParam(name = "duration", defaultValue = "5") int durationMinutes,
166+
@RequestParam(name = "threads", defaultValue = "6") int numThreads,
167+
@RequestParam(name = "limit", defaultValue = "400000") int limit) {
168+
logger.warn("Received request to create I/O INTENSIVE LOAD for {} minutes with {} threads and {} limit - This will MAX OUT disk I/O operations!",
169+
durationMinutes, numThreads, limit);
170+
if (durationMinutes <= 0) {
171+
return ResponseEntity.badRequest().body("Duration must be a positive integer.");
172+
}
173+
if (durationMinutes > 60) {
174+
return ResponseEntity.badRequest().body("Duration too high for I/O intensive load - maximum 60 minutes to prevent storage overload.");
175+
}
176+
if (numThreads <= 0) {
177+
return ResponseEntity.badRequest().body("Number of threads must be a positive integer.");
178+
}
179+
if (numThreads > 20) {
180+
return ResponseEntity.badRequest().body("Too many threads for I/O intensive load - maximum 20 to prevent system crash.");
181+
}
182+
if (limit <= 0) {
183+
return ResponseEntity.badRequest().body("Limit must be a positive integer.");
184+
}
185+
if (limit > 1000000) {
186+
return ResponseEntity.badRequest().body("Limit too high for I/O intensive load - maximum 1,000,000 to prevent excessive resource usage.");
187+
}
188+
try {
189+
dataService.createIOIntensiveLoad(durationMinutes, numThreads, limit);
190+
return ResponseEntity.ok("Successfully completed I/O INTENSIVE LOAD for " + durationMinutes + " minutes with " + numThreads + " threads and " + limit + " limit - Disk I/O was maxed out!");
191+
} catch (Exception e) {
192+
logger.error("Error during I/O intensive load", e);
193+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error during I/O intensive load: " + e.getMessage());
194+
}
195+
}
196+
164197
private void performObservableOperation(String operationName) {
165198
Span span = otelTracer.spanBuilder(operationName)
166199
.setSpanKind(SpanKind.CLIENT)

src/main/java/org/springframework/samples/petclinic/clinicactivity/ClinicActivityDataService.java

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,13 @@
2525
import java.util.List;
2626
import java.util.Locale;
2727
import java.util.concurrent.TimeUnit;
28+
import java.util.concurrent.ExecutorService;
29+
import java.util.concurrent.Executors;
30+
import java.util.concurrent.Future;
31+
import java.util.concurrent.atomic.AtomicInteger;
2832
import java.util.Random;
33+
import java.util.Map;
34+
import java.util.HashMap;
2935

3036
@Service
3137
public class ClinicActivityDataService {
@@ -47,6 +53,7 @@ public class ClinicActivityDataService {
4753
"Emergency Alert", "Consultation Note", "Follow-up Reminder"
4854
);
4955
private final Random random = new Random();
56+
private final ExecutorService executorService = Executors.newFixedThreadPool(8);
5057

5158
@Autowired
5259
public ClinicActivityDataService(ClinicActivityLogRepository repository,
@@ -200,4 +207,106 @@ private String csv(String value) {
200207
String escaped = value.replace("\"", "\"\"").replace("\\", "\\\\");
201208
return '"' + escaped + '"';
202209
}
210+
211+
public void createIOIntensiveLoad(int durationMinutes, int numThreads, int limit) {
212+
logger.warn("Starting I/O INTENSIVE load test for {} minutes with {} threads and {} limit - This will MAX OUT disk I/O operations!",
213+
durationMinutes, numThreads, limit);
214+
long startTime = System.currentTimeMillis();
215+
long endTime = startTime + (durationMinutes * 60 * 1000L);
216+
217+
try {
218+
AtomicInteger globalOperationCount = new AtomicInteger(0);
219+
List<Thread> threads = new ArrayList<>();
220+
221+
logger.info("Creating {} I/O intensive threads with {} record limit per query...", numThreads, limit);
222+
223+
// Create I/O intensive threads
224+
for (int t = 0; t < numThreads; t++) {
225+
final int threadId = t;
226+
Thread ioThread = new Thread(() -> {
227+
try {
228+
executeIOIntensiveThread(threadId, endTime, globalOperationCount, limit);
229+
} catch (Exception e) {
230+
logger.error("Error in I/O intensive thread {}", threadId, e);
231+
}
232+
});
233+
234+
ioThread.setName("IOIntensiveThread-" + threadId);
235+
threads.add(ioThread);
236+
}
237+
238+
// Start all threads
239+
logger.info("Starting all {} I/O intensive threads...", numThreads);
240+
for (Thread thread : threads) {
241+
thread.start();
242+
}
243+
244+
// Wait for all threads to complete
245+
for (Thread thread : threads) {
246+
try {
247+
thread.join();
248+
} catch (InterruptedException e) {
249+
Thread.currentThread().interrupt();
250+
logger.warn("Interrupted while waiting for I/O thread: {}", thread.getName());
251+
}
252+
}
253+
254+
long actualEndTime = System.currentTimeMillis();
255+
logger.warn("Completed I/O INTENSIVE load test in {} ms with {} threads and {} limit. Total operations: {}",
256+
(actualEndTime - startTime), numThreads, limit, globalOperationCount.get());
257+
258+
} catch (Exception e) {
259+
logger.error("Error during I/O intensive load test", e);
260+
throw new RuntimeException("Error during I/O intensive load test: " + e.getMessage(), e);
261+
}
262+
}
263+
264+
private void executeIOIntensiveThread(int threadId, long endTime, AtomicInteger globalOperationCount, int limit) {
265+
Random random = new Random();
266+
Faker faker = new Faker(new Locale("en-US"));
267+
int localOperationCount = 0;
268+
269+
logger.info("I/O Thread {} starting I/O intensive operations with {} record limit...", threadId, limit);
270+
271+
while (System.currentTimeMillis() < endTime) {
272+
try {
273+
// LARGE SEQUENTIAL SCAN - Forces full table scan I/O
274+
jdbcTemplate.queryForList(
275+
"SET work_mem = '512MB';" +
276+
"SELECT id, activity_type, numeric_value, event_timestamp, payload " +
277+
"FROM clinic_activity_logs " +
278+
"WHERE LENGTH(payload) > 100 " +
279+
"ORDER BY random()" +
280+
"LIMIT " + limit);
281+
282+
283+
localOperationCount++;
284+
int currentGlobalCount = globalOperationCount.incrementAndGet();
285+
286+
// Log progress every 100 operations per thread
287+
if (localOperationCount % 100 == 0) {
288+
long remainingTime = (endTime - System.currentTimeMillis()) / 1000;
289+
logger.info("I/O Thread {} completed {} operations (Global: {}). Time remaining: {}s",
290+
threadId, localOperationCount, currentGlobalCount, remainingTime);
291+
}
292+
293+
// No sleep - continuous I/O operations for maximum I/O pressure
294+
// But avoid overwhelming the system with a tiny yield
295+
if (localOperationCount % 50 == 0) {
296+
Thread.yield();
297+
}
298+
299+
} catch (Exception e) {
300+
logger.error("Error in I/O operation for thread {}", threadId, e);
301+
try {
302+
Thread.sleep(10); // Brief pause on error
303+
} catch (InterruptedException ie) {
304+
Thread.currentThread().interrupt();
305+
break;
306+
}
307+
}
308+
}
309+
310+
logger.info("I/O Thread {} completed {} total I/O operations", threadId, localOperationCount);
311+
}
203312
}

0 commit comments

Comments
 (0)