fix(library-service): prevent concurrent library scans with a tracking set (#2637)

Signed-off-by: Balázs Szücs <bszucs1209@gmail.com>
This commit is contained in:
Balázs Szücs
2026-02-06 21:06:41 +01:00
committed by GitHub
parent b98663acf0
commit 37f6417e3d

View File

@@ -38,6 +38,7 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Slf4j @Slf4j
@@ -45,6 +46,17 @@ import java.util.stream.Collectors;
@AllArgsConstructor @AllArgsConstructor
public class LibraryService { public class LibraryService {
private static final Set<Long> scanningLibraries = ConcurrentHashMap.newKeySet();
/**
* Checks whether a library is currently being scanned.
* Can be used by other components (e.g., file watcher) to avoid processing
* files while a full scan is in progress.
*/
public static boolean isLibraryScanning(long libraryId) {
return scanningLibraries.contains(libraryId);
}
private final LibraryRepository libraryRepository; private final LibraryRepository libraryRepository;
private final LibraryPathRepository libraryPathRepository; private final LibraryPathRepository libraryPathRepository;
private final BookRepository bookRepository; private final BookRepository bookRepository;
@@ -125,10 +137,16 @@ public class LibraryService {
if (!newPaths.isEmpty()) { if (!newPaths.isEmpty()) {
SecurityContextVirtualThread.runWithSecurityContext(() -> { SecurityContextVirtualThread.runWithSecurityContext(() -> {
if (!scanningLibraries.add(libraryId)) {
log.warn("Library {} is already being scanned, skipping duplicate process request", libraryId);
return;
}
try { try {
libraryProcessingService.processLibrary(libraryId); libraryProcessingService.processLibrary(libraryId);
} catch (InvalidDataAccessApiUsageException e) { } catch (InvalidDataAccessApiUsageException e) {
log.debug("InvalidDataAccessApiUsageException - Library id: {}", libraryId); log.debug("InvalidDataAccessApiUsageException - Library id: {}", libraryId);
} finally {
scanningLibraries.remove(libraryId);
} }
log.info("Parsing task completed!"); log.info("Parsing task completed!");
}); });
@@ -169,10 +187,16 @@ public class LibraryService {
} }
SecurityContextVirtualThread.runWithSecurityContext(() -> { SecurityContextVirtualThread.runWithSecurityContext(() -> {
if (!scanningLibraries.add(libraryId)) {
log.warn("Library {} is already being scanned, skipping duplicate process request", libraryId);
return;
}
try { try {
libraryProcessingService.processLibrary(libraryId); libraryProcessingService.processLibrary(libraryId);
} catch (InvalidDataAccessApiUsageException e) { } catch (InvalidDataAccessApiUsageException e) {
log.debug("InvalidDataAccessApiUsageException - Library id: {}", libraryId); log.debug("InvalidDataAccessApiUsageException - Library id: {}", libraryId);
} finally {
scanningLibraries.remove(libraryId);
} }
log.info("Parsing task completed!"); log.info("Parsing task completed!");
}); });
@@ -184,6 +208,10 @@ public class LibraryService {
libraryRepository.findById(libraryId).orElseThrow(() -> ApiError.LIBRARY_NOT_FOUND.createException(libraryId)); libraryRepository.findById(libraryId).orElseThrow(() -> ApiError.LIBRARY_NOT_FOUND.createException(libraryId));
SecurityContextVirtualThread.runWithSecurityContext(() -> { SecurityContextVirtualThread.runWithSecurityContext(() -> {
if (!scanningLibraries.add(libraryId)) {
log.warn("Library {} is already being scanned, skipping duplicate rescan request", libraryId);
return;
}
try { try {
RescanLibraryContext context = RescanLibraryContext.builder() RescanLibraryContext context = RescanLibraryContext.builder()
.libraryId(libraryId) .libraryId(libraryId)
@@ -193,6 +221,8 @@ public class LibraryService {
log.debug("InvalidDataAccessApiUsageException - Library id: {}", libraryId); log.debug("InvalidDataAccessApiUsageException - Library id: {}", libraryId);
} catch (IOException e) { } catch (IOException e) {
log.error("Error while parsing library books", e); log.error("Error while parsing library books", e);
} finally {
scanningLibraries.remove(libraryId);
} }
log.info("Parsing task completed!"); log.info("Parsing task completed!");
}); });