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.Paths;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@Slf4j
@@ -45,6 +46,17 @@ import java.util.stream.Collectors;
@AllArgsConstructor
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 LibraryPathRepository libraryPathRepository;
private final BookRepository bookRepository;
@@ -125,10 +137,16 @@ public class LibraryService {
if (!newPaths.isEmpty()) {
SecurityContextVirtualThread.runWithSecurityContext(() -> {
if (!scanningLibraries.add(libraryId)) {
log.warn("Library {} is already being scanned, skipping duplicate process request", libraryId);
return;
}
try {
libraryProcessingService.processLibrary(libraryId);
} catch (InvalidDataAccessApiUsageException e) {
log.debug("InvalidDataAccessApiUsageException - Library id: {}", libraryId);
} finally {
scanningLibraries.remove(libraryId);
}
log.info("Parsing task completed!");
});
@@ -169,10 +187,16 @@ public class LibraryService {
}
SecurityContextVirtualThread.runWithSecurityContext(() -> {
if (!scanningLibraries.add(libraryId)) {
log.warn("Library {} is already being scanned, skipping duplicate process request", libraryId);
return;
}
try {
libraryProcessingService.processLibrary(libraryId);
} catch (InvalidDataAccessApiUsageException e) {
log.debug("InvalidDataAccessApiUsageException - Library id: {}", libraryId);
} finally {
scanningLibraries.remove(libraryId);
}
log.info("Parsing task completed!");
});
@@ -184,6 +208,10 @@ public class LibraryService {
libraryRepository.findById(libraryId).orElseThrow(() -> ApiError.LIBRARY_NOT_FOUND.createException(libraryId));
SecurityContextVirtualThread.runWithSecurityContext(() -> {
if (!scanningLibraries.add(libraryId)) {
log.warn("Library {} is already being scanned, skipping duplicate rescan request", libraryId);
return;
}
try {
RescanLibraryContext context = RescanLibraryContext.builder()
.libraryId(libraryId)
@@ -193,6 +221,8 @@ public class LibraryService {
log.debug("InvalidDataAccessApiUsageException - Library id: {}", libraryId);
} catch (IOException e) {
log.error("Error while parsing library books", e);
} finally {
scanningLibraries.remove(libraryId);
}
log.info("Parsing task completed!");
});