From a381ca7ef80446c37101718e776a539f430ff354 Mon Sep 17 00:00:00 2001 From: Jan Date: Wed, 28 Jan 2026 11:21:00 +0100 Subject: [PATCH] Improved `removeOld` method in `BulkOperationRepository` to fix subquery limitations in MySQL and optimize deletion logic. --- .../bulk/BulkOperationRepository.java | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/main/java/de/avatic/lcc/repositories/bulk/BulkOperationRepository.java b/src/main/java/de/avatic/lcc/repositories/bulk/BulkOperationRepository.java index b11362c..d70deb3 100644 --- a/src/main/java/de/avatic/lcc/repositories/bulk/BulkOperationRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/bulk/BulkOperationRepository.java @@ -69,14 +69,27 @@ public class BulkOperationRepository { @Transactional public void removeOld(Integer userId) { - // First, update sys_error records to set bulk_operation_id to NULL - // for bulk operations that will be deleted (all but the 10 newest for the current user) - - // Build subquery to get the 10 newest operations - String newestSubquery = "SELECT id FROM bulk_operation WHERE user_id = ? AND state NOT IN ('SCHEDULED', 'PROCESSING') ORDER BY created_at DESC "; - String newestWithLimit = newestSubquery + dialectProvider.buildPaginationClause(10, 0); + // First, fetch the IDs of the 10 newest operations to keep + // (MySQL doesn't support LIMIT in IN/NOT IN subqueries) + String fetchNewestSql = "SELECT id FROM bulk_operation WHERE user_id = ? AND state NOT IN ('SCHEDULED', 'PROCESSING') ORDER BY created_at DESC " + + dialectProvider.buildPaginationClause(10, 0); Object[] paginationParams = dialectProvider.getPaginationParameters(10, 0); + Object[] fetchParams = new Object[]{userId, paginationParams[0], paginationParams[1]}; + List newestIds = jdbcTemplate.queryForList(fetchNewestSql, Integer.class, fetchParams); + + // If there are 10 or fewer operations, nothing to delete + if (newestIds.size() <= 10) { + return; + } + + // Build comma-separated list of IDs to keep + String idsToKeep = newestIds.stream() + .map(String::valueOf) + .reduce((a, b) -> a + "," + b) + .orElse("0"); + + // Update sys_error records to set bulk_operation_id to NULL for operations that will be deleted String updateErrorsSql = String.format(""" UPDATE sys_error SET bulk_operation_id = NULL @@ -84,29 +97,21 @@ public class BulkOperationRepository { SELECT id FROM bulk_operation WHERE user_id = ? AND state NOT IN ('SCHEDULED', 'PROCESSING') - AND id NOT IN ( - %s - ) + AND id NOT IN (%s) ) - """, newestWithLimit); + """, idsToKeep); - // Combine params: userId for outer query, userId + pagination params for subquery - Object[] updateParams = new Object[]{userId, userId, paginationParams[0], paginationParams[1]}; - jdbcTemplate.update(updateErrorsSql, updateParams); + jdbcTemplate.update(updateErrorsSql, userId); - // Then delete the old bulk_operation entries (keeping only the 10 newest for the current user) + // Delete the old bulk_operation entries (keeping only the 10 newest for the current user) String deleteBulkSql = String.format(""" DELETE FROM bulk_operation WHERE user_id = ? AND state NOT IN ('SCHEDULED', 'PROCESSING') - AND id NOT IN ( - %s - ) - """, newestWithLimit); + AND id NOT IN (%s) + """, idsToKeep); - // Combine params: userId for WHERE clause, userId + pagination params for subquery - Object[] deleteParams = new Object[]{userId, userId, paginationParams[0], paginationParams[1]}; - jdbcTemplate.update(deleteBulkSql, deleteParams); + jdbcTemplate.update(deleteBulkSql, userId); } @Transactional