I have a memory-intensive exporting function that needs to retrieve over 1M data from the MongoDB and write it to an Excel file. This export method is already triggered asynchronously by using AWS SNS/SQS. However, it always encounters an OOM issue without using the SingleThreadPool.
@Transactional
@SqsListener("${sqs-example}")
public void exportData(String json) throws IOException {
LOGGER.info("Export: {}", json);
ExportPayload exportPayload = objectMapper.readValue(json, ExportPayload.class);
try {
TenantContext.setTenant(exportPayload.getTenant());
exportService.export(exportPayload.getExportId(), exportPayload.getTenant());
} finally {
TenantContext.clear();
}
}
The exportFunc
function is the one that writes the retrieved MongoDB data to an excel file. Without the singleThreadExectuer, it will have an OOM issue after around 130,000 rows. But if writing it as below, it could handle over 1M rows easily.
public void export(String exportId, Long tenantId) {
ExecutorService es = Executors.newSingleThreadExecutor();
if (oExport.isPresent()) {
try {
es.submit(() -> {
try {
TenantContext.setTenant(tenantId);
exportFunc();
} finally {
TenantContext.clear();
}
});
} finally {
es.shutdown();
}
} else {
LOGGER.error("No export found by id: {}", exportId);
}
}
In my opinion, the export will be triggered when the program receives a message, and it's already running in a separate thread, why does the singleThreadExecutor
can solve the OOM issue in this case?