Skip to content

Commit 8943777

Browse files
committed
Fix consistency_checks for externally stored blobs
1 parent 297ad75 commit 8943777

File tree

3 files changed

+35
-3
lines changed

3 files changed

+35
-3
lines changed

apps/server/src/becca/entities/abstract_becca_entity.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,25 @@ abstract class AbstractBeccaEntity<T extends AbstractBeccaEntity<T>> {
230230
const blobNeedsInsert = !sql.getValue("SELECT 1 FROM blobs WHERE blobId = ?", [newBlobId]);
231231

232232
if (!blobNeedsInsert) {
233-
return newBlobId;
233+
if (!blobStorageService.hasExternalContentColumns()) {
234+
// If no external storage support, safe to reuse blob
235+
return newBlobId;
236+
}
237+
238+
// Self recover external blob if the file was deleted externally
239+
const existingBlob = sql.getRow<{ contentLocation: BlobContentLocation }>(/*sql*/
240+
`SELECT contentLocation FROM blobs WHERE blobId = ?`,
241+
[newBlobId]
242+
);
243+
244+
const isInternalBlob = existingBlob?.contentLocation === 'internal';
245+
if (isInternalBlob || blobStorageService.externalFileExists(existingBlob.contentLocation)) {
246+
// If external blob is still present, safe to reuse
247+
return newBlobId;
248+
}
249+
250+
// External file is missing, recreate it
251+
log.info(`External file missing for blob ${newBlobId}, recreating...`);
234252
}
235253

236254
// Check if we should store this blob externally

apps/server/src/services/blob-storage.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export class BlobStorageService {
2323
/**
2424
* Check if the external content columns (contentLocation, contentLength) exist in the blobs table.
2525
* This is cached for performance.
26-
* Returns false before migration 234 has been applied (for example when applying older migrations).
26+
* Returns false before migration 234 has been applied (aka when applying older migrations).
2727
*/
2828
hasExternalContentColumns(): boolean {
2929
if (!this._hasExternalContentColumns) {
@@ -123,6 +123,19 @@ export class BlobStorageService {
123123

124124
return blob.getContentLength(content) > config.ExternalBlobStorage.thresholdBytes;
125125
}
126+
127+
/**
128+
* Check if an external blob file exists
129+
*/
130+
externalFileExists(contentLocation: BlobContentLocation): boolean {
131+
if (contentLocation === "internal" || !contentLocation.startsWith("file://")) {
132+
return false;
133+
}
134+
135+
const relativePath = contentLocation.replace("file://", "");
136+
const filePath = path.join(this.externalBlobPath, relativePath);
137+
return fs.existsSync(filePath);
138+
}
126139
}
127140

128141
export default new BlobStorageService();

apps/server/src/services/consistency_checks.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,8 @@ class ConsistencyChecks {
524524
JOIN blobs USING (blobId)
525525
WHERE isDeleted = 0
526526
AND isProtected = 0
527-
AND content IS NULL`,
527+
AND content IS NULL
528+
AND (contentLocation IS NULL OR contentLocation = 'internal')`,
528529
({ noteId, type, mime }) => {
529530
if (this.autoFix) {
530531
const note = becca.getNote(noteId);

0 commit comments

Comments
 (0)