feat: integrity check jobs (missing files, untracked files, checksums)#24205
feat: integrity check jobs (missing files, untracked files, checksums)#24205
Conversation
refactor: rename checksum_fail to checksum_mismatched
refactor: combine the stream query
chore: lint & format
feat: update summary after job runs
Signed-off-by: izzy <me@insrt.uk>
Signed-off-by: izzy <me@insrt.uk>
Signed-off-by: izzy <me@insrt.uk>
Signed-off-by: izzy <me@insrt.uk>
Signed-off-by: izzy <me@insrt.uk>
Signed-off-by: izzy <me@insrt.uk>
fix: should provide arrow function to , oops! Signed-off-by: izzy <me@insrt.uk>
Signed-off-by: izzy <me@insrt.uk>
Signed-off-by: izzy <me@insrt.uk>
Signed-off-by: izzy <me@insrt.uk>
| type?: IntegrityReportType; | ||
| isDeleting: boolean; | ||
| }) => { | ||
| if (type === reportType || reportId === id) { |
There was a problem hiding this comment.
Isn't the id globally unique? Is it only unique for a given report type?
There was a problem hiding this comment.
Yes it's globally unique.
This check succeeds if:
type?is present, i.e. "delete all" triggered for a typereportId?is present, i.e. this specific report is being deleted
There was a problem hiding this comment.
I misread this as an && 🤦
Signed-off-by: izzy <me@insrt.uk>
Signed-off-by: izzy <me@insrt.uk>
Signed-off-by: izzy <me@insrt.uk>
Signed-off-by: izzy <me@insrt.uk>
danieldietzler
left a comment
There was a problem hiding this comment.
The code looks great to me. Handing it off to @jrasm91 :D
| .where('asset.deletedAt', 'is', null) | ||
| .select(['asset.originalPath as path']) | ||
| .select((eb) => [ | ||
| eb.ref('asset.id').$castTo<string | null>().as('assetId'), |
There was a problem hiding this comment.
Now that you cast the end result to something much more meaningful, you also don't need these casts anymore. (+ the sql template strings don't need to be typed as string | null if they're always null
| }, | ||
| }: ArgOf<'ConfigInit'>) { | ||
| this.integrityLock = await this.databaseRepository.tryLock(DatabaseLock.IntegrityCheck); | ||
| if (this.integrityLock) { |
There was a problem hiding this comment.
Maybe change this to if (!this.integrityLock) { return; } so that the entire function body does not need to be in that if
| } | ||
|
|
||
| getIntegrityReport(dto: IntegrityGetReportDto): Promise<IntegrityReportResponseDto> { | ||
| return this.integrityRepository.getIntegrityReport({ cursor: dto.cursor, limit: dto.limit || 100 }, dto.type); |
There was a problem hiding this comment.
If I set a limit of 0, this will give me the first 100 reports. Do you want ?? 100 instead, or is this intentional?
| date: lastCreatedAt?.toISOString(), | ||
| }); | ||
|
|
||
| printStats(); |
There was a problem hiding this comment.
Why does printStats even need to be a function? You're only calling it once
| const byAsset = reports.filter((report) => report.assetId); | ||
| const byFileAsset = reports.filter((report) => report.fileAssetId); | ||
| const byPath = reports.filter((report) => !report.assetId && !report.fileAssetId); |
|
Suggestions: Wouldn't it make sense to make these integrity checks also be part of the "restoring from db dump/backup" process, so you can directly verify after a restore if there are missing assets in your database or in your filesystem? Also for assets that are on the filesystem but not in the database, it would be nice if they can be re-uploaded through immich itself, so you don't have to press "download", get them on your device, and then re-upload, but just press "import" or similar. |
|
Will this catch instances of: See #7082 for more discussion. Thank you! |
How Has This Been Tested?
Screenshots
Screencast_20260107_131048.webm
Checklist:
src/services/uses repositories implementations for database calls, filesystem operations, etc.src/repositories/is pretty basic/simple and does not have any immich specific logic (that belongs insrc/services/)Please describe to which degree, if any, an LLM was used in creating this pull request.
Used to figure out some of the types in the SQL query builder syntax.
Used as a starting point for some research into Postgres / SQL query performance.
Future Work