You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-11-26 22:41:17 +02:00
Desktop: Fix infinite loop on startup after quickly moving folders (#12140)
This commit is contained in:
@@ -163,6 +163,23 @@ describe('models/Folder', () => {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('folder hierarchy cycles should not cause addNoteCounts to loop infinitely', async () => {
|
||||||
|
const f1 = await Folder.save({ title: 'folder1' });
|
||||||
|
const f2 = await Folder.save({ title: 'folder2', parent_id: f1.id });
|
||||||
|
const f3 = await Folder.save({ title: 'folder3', parent_id: f2.id });
|
||||||
|
await Note.save({ title: 'test', parent_id: f1.id });
|
||||||
|
|
||||||
|
// Create a cycle.
|
||||||
|
// Note: This has been observed to happen, likely as a result of a bug in other code.
|
||||||
|
await Folder.save({ id: f1.id, parent_id: f3.id });
|
||||||
|
|
||||||
|
const folders = await Folder.all();
|
||||||
|
// Should not loop indefinitely:
|
||||||
|
await Folder.addNoteCounts(folders);
|
||||||
|
// Note count may be incorrect
|
||||||
|
expect(folders.find(folder => folder.id === f1.id)).toHaveProperty('note_count');
|
||||||
|
});
|
||||||
|
|
||||||
it('should not count completed to-dos', (async () => {
|
it('should not count completed to-dos', (async () => {
|
||||||
|
|
||||||
const f1 = await Folder.save({ title: 'folder1' });
|
const f1 = await Folder.save({ title: 'folder1' });
|
||||||
|
|||||||
@@ -187,6 +187,24 @@ export default class Folder extends BaseItem {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks for invalid state -- whether startId or its parents is part of a cycle
|
||||||
|
// in the folder graph (which should be a tree).
|
||||||
|
private static checkForFolderHierarchyCycle_(
|
||||||
|
idToFolder: Record<string, FolderEntity>,
|
||||||
|
startId: string,
|
||||||
|
) {
|
||||||
|
let folderId = startId;
|
||||||
|
const seenIds = new Set();
|
||||||
|
for (; idToFolder[folderId]; folderId = idToFolder[folderId].parent_id) {
|
||||||
|
if (seenIds.has(folderId)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
seenIds.add(folderId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Calculates note counts for all folders and adds the note_count attribute to each folder
|
// Calculates note counts for all folders and adds the note_count attribute to each folder
|
||||||
// Note: this only calculates the overall number of nodes for this folder and all its descendants
|
// Note: this only calculates the overall number of nodes for this folder and all its descendants
|
||||||
public static async addNoteCounts(folders: FolderEntity[], includeCompletedTodos = true) {
|
public static async addNoteCounts(folders: FolderEntity[], includeCompletedTodos = true) {
|
||||||
@@ -226,10 +244,22 @@ export default class Folder extends BaseItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const noteCounts: NoteCount[] = await this.db().selectAll(sql);
|
const noteCounts: NoteCount[] = await this.db().selectAll(sql);
|
||||||
// eslint-disable-next-line github/array-foreach -- Old code before rule was applied
|
for (const noteCount of noteCounts) {
|
||||||
noteCounts.forEach((noteCount) => {
|
|
||||||
let parentId = noteCount.folder_id;
|
let parentId = noteCount.folder_id;
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
let checkedForCycle = false;
|
||||||
do {
|
do {
|
||||||
|
// Handle invalid state, preventing infinite loops -- check whether the current
|
||||||
|
// folder has itself as a parent.
|
||||||
|
if (i++ > 100 && !checkedForCycle) {
|
||||||
|
if (Folder.checkForFolderHierarchyCycle_(foldersById, parentId)) {
|
||||||
|
logger.warn(`Invalid state: Folder ${parentId} has itself as a parent.`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
checkedForCycle = true;
|
||||||
|
}
|
||||||
|
|
||||||
const folder = foldersById[parentId];
|
const folder = foldersById[parentId];
|
||||||
if (!folder) break; // https://github.com/laurent22/joplin/issues/2079
|
if (!folder) break; // https://github.com/laurent22/joplin/issues/2079
|
||||||
folder.note_count = (folder.note_count || 0) + noteCount.note_count;
|
folder.note_count = (folder.note_count || 0) + noteCount.note_count;
|
||||||
@@ -240,7 +270,7 @@ export default class Folder extends BaseItem {
|
|||||||
|
|
||||||
parentId = folder.parent_id;
|
parentId = folder.parent_id;
|
||||||
} while (parentId);
|
} while (parentId);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Folders that contain notes that have been modified recently go on top.
|
// Folders that contain notes that have been modified recently go on top.
|
||||||
|
|||||||
Reference in New Issue
Block a user