mirror of
https://github.com/immich-app/immich.git
synced 2024-12-25 10:43:13 +02:00
fix(server): queue library asset refresh in batches (#7914)
* add debug logs * scan assets in batches * Cleanup * don't normalize * Removing extra log * remove unneeded code * change log levels
This commit is contained in:
parent
428b7b0c4e
commit
ba38713fbc
@ -300,12 +300,18 @@ export class LibraryService extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async scanAssets(libraryId: string, assetPaths: string[], ownerId: string, force = false) {
|
private async scanAssets(libraryId: string, assetPaths: string[], ownerId: string, force = false) {
|
||||||
|
this.logger.verbose(`Queuing refresh of ${assetPaths.length} asset(s)`);
|
||||||
|
|
||||||
|
// We perform this in batches to save on memory when performing large refreshes (greater than 1M assets)
|
||||||
|
const batchSize = 5000;
|
||||||
|
for (let i = 0; i < assetPaths.length; i += batchSize) {
|
||||||
|
const batch = assetPaths.slice(i, i + batchSize);
|
||||||
await this.jobRepository.queueAll(
|
await this.jobRepository.queueAll(
|
||||||
assetPaths.map((assetPath) => ({
|
batch.map((assetPath) => ({
|
||||||
name: JobName.LIBRARY_SCAN_ASSET,
|
name: JobName.LIBRARY_SCAN_ASSET,
|
||||||
data: {
|
data: {
|
||||||
id: libraryId,
|
id: libraryId,
|
||||||
assetPath: path.normalize(assetPath),
|
assetPath: assetPath,
|
||||||
ownerId,
|
ownerId,
|
||||||
force,
|
force,
|
||||||
},
|
},
|
||||||
@ -313,6 +319,9 @@ export class LibraryService extends EventEmitter {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug('Asset refresh queue completed');
|
||||||
|
}
|
||||||
|
|
||||||
private async validateImportPath(importPath: string): Promise<ValidateLibraryImportPathResponseDto> {
|
private async validateImportPath(importPath: string): Promise<ValidateLibraryImportPathResponseDto> {
|
||||||
const validation = new ValidateLibraryImportPathResponseDto();
|
const validation = new ValidateLibraryImportPathResponseDto();
|
||||||
validation.importPath = importPath;
|
validation.importPath = importPath;
|
||||||
@ -611,14 +620,6 @@ export class LibraryService extends EventEmitter {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a given path is in a user's external path. Both arguments are assumed to be normalized
|
|
||||||
private isInExternalPath(filePath: string, externalPath: string | null): boolean {
|
|
||||||
if (externalPath === null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return filePath.startsWith(externalPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
async handleQueueAssetRefresh(job: ILibraryRefreshJob): Promise<boolean> {
|
async handleQueueAssetRefresh(job: ILibraryRefreshJob): Promise<boolean> {
|
||||||
const library = await this.repository.get(job.id);
|
const library = await this.repository.get(job.id);
|
||||||
if (!library || library.type !== LibraryType.EXTERNAL) {
|
if (!library || library.type !== LibraryType.EXTERNAL) {
|
||||||
@ -626,7 +627,7 @@ export class LibraryService extends EventEmitter {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.verbose(`Refreshing library: ${job.id}`);
|
this.logger.log(`Refreshing library: ${job.id}`);
|
||||||
|
|
||||||
const crawledAssetPaths = await this.getPathTrie(library);
|
const crawledAssetPaths = await this.getPathTrie(library);
|
||||||
this.logger.debug(`Found ${crawledAssetPaths.size} asset(s) when crawling import paths ${library.importPaths}`);
|
this.logger.debug(`Found ${crawledAssetPaths.size} asset(s) when crawling import paths ${library.importPaths}`);
|
||||||
@ -637,16 +638,20 @@ export class LibraryService extends EventEmitter {
|
|||||||
this.assetRepository.getLibraryAssetPaths(pagination, library.id),
|
this.assetRepository.getLibraryAssetPaths(pagination, library.id),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.logger.verbose(`Crawled asset paths paginated`);
|
||||||
|
|
||||||
const shouldScanAll = job.refreshAllFiles || job.refreshModifiedFiles;
|
const shouldScanAll = job.refreshAllFiles || job.refreshModifiedFiles;
|
||||||
for await (const page of pagination) {
|
for await (const page of pagination) {
|
||||||
for (const asset of page) {
|
for (const asset of page) {
|
||||||
const isOffline = !crawledAssetPaths.has(asset.originalPath);
|
const isOffline = !crawledAssetPaths.has(asset.originalPath);
|
||||||
if (isOffline && !asset.isOffline) {
|
if (isOffline && !asset.isOffline) {
|
||||||
assetIdsToMarkOffline.push(asset.id);
|
assetIdsToMarkOffline.push(asset.id);
|
||||||
|
this.logger.verbose(`Added to mark-offline list: ${asset.originalPath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isOffline && asset.isOffline) {
|
if (!isOffline && asset.isOffline) {
|
||||||
assetIdsToMarkOnline.push(asset.id);
|
assetIdsToMarkOnline.push(asset.id);
|
||||||
|
this.logger.verbose(`Added to mark-online list: ${asset.originalPath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!shouldScanAll) {
|
if (!shouldScanAll) {
|
||||||
@ -655,6 +660,8 @@ export class LibraryService extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose(`Crawled assets have been checked for online/offline status`);
|
||||||
|
|
||||||
if (assetIdsToMarkOffline.length > 0) {
|
if (assetIdsToMarkOffline.length > 0) {
|
||||||
this.logger.debug(`Found ${assetIdsToMarkOffline.length} offline asset(s) previously marked as online`);
|
this.logger.debug(`Found ${assetIdsToMarkOffline.length} offline asset(s) previously marked as online`);
|
||||||
await this.assetRepository.updateAll(assetIdsToMarkOffline, { isOffline: true });
|
await this.assetRepository.updateAll(assetIdsToMarkOffline, { isOffline: true });
|
||||||
|
Loading…
Reference in New Issue
Block a user