Perform slow metadata to FileProviderItem conversion concurrently in FileProviderEnum...
authorClaudio Cambra <claudio.cambra@nextcloud.com>
Sat, 18 Mar 2023 20:35:12 +0000 (21:35 +0100)
committerClaudio Cambra <claudio.cambra@nextcloud.com>
Fri, 12 May 2023 08:02:36 +0000 (16:02 +0800)
Signed-off-by: Claudio Cambra <claudio.cambra@nextcloud.com>
shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderEnumerator.swift

index 4ac2a72afe6bdea15c821a05de308563308ff637..426334f45ea80d72919fea7e1b60aa1ecc6aad9e 100644 (file)
@@ -273,25 +273,36 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
 
     // MARK: - Helper methods
 
-    private static func metadatasToFileProviderItems(_ itemMetadatas: [NextcloudItemMetadataTable], ncKit: NextcloudKit) -> [NSFileProviderItem] {
+    private static func metadatasToFileProviderItems(_ itemMetadatas: [NextcloudItemMetadataTable], ncKit: NextcloudKit, completionHandler: @escaping(_ items: [NSFileProviderItem]) -> Void) {
         var items: [NSFileProviderItem] = []
 
+        let dispatchGroup = DispatchGroup()
+
         for itemMetadata in itemMetadatas {
-            if itemMetadata.e2eEncrypted {
-                Logger.enumeration.info("Skipping encrypted metadata in enumeration: \(itemMetadata.ocId) \(itemMetadata.fileName, privacy: OSLogPrivacy.auto(mask: .hash))")
-                continue
-            }
+            dispatchGroup.enter()
 
-            if let parentItemIdentifier = NextcloudFilesDatabaseManager.shared.parentItemIdentifierFromMetadata(itemMetadata) {
-                let item = FileProviderItem(metadata: itemMetadata, parentItemIdentifier: parentItemIdentifier, ncKit: ncKit)
-                Logger.enumeration.debug("Will enumerate item with ocId: \(itemMetadata.ocId) and name: \(itemMetadata.fileName, privacy: OSLogPrivacy.auto(mask: .hash))")
-                items.append(item)
-            } else {
-                Logger.enumeration.error("Could not get valid parentItemIdentifier for item with ocId: \(itemMetadata.ocId, privacy: .public) and name: \(itemMetadata.fileName, privacy: OSLogPrivacy.auto(mask: .hash)), skipping enumeration")
+            DispatchQueue.global(qos: .userInitiated).async {
+                if itemMetadata.e2eEncrypted {
+                    Logger.enumeration.info("Skipping encrypted metadata in enumeration: \(itemMetadata.ocId, privacy: .public) \(itemMetadata.fileName, privacy: OSLogPrivacy.auto(mask: .hash))")
+                    dispatchGroup.leave()
+                    return
+                }
+
+                if let parentItemIdentifier = NextcloudFilesDatabaseManager.shared.parentItemIdentifierFromMetadata(itemMetadata) {
+                    let item = FileProviderItem(metadata: itemMetadata, parentItemIdentifier: parentItemIdentifier, ncKit: ncKit)
+                    Logger.enumeration.debug("Will enumerate item with ocId: \(itemMetadata.ocId, privacy: .public) and name: \(itemMetadata.fileName, privacy: OSLogPrivacy.auto(mask: .hash))")
+                    items.append(item)
+                } else {
+                    Logger.enumeration.error("Could not get valid parentItemIdentifier for item with ocId: \(itemMetadata.ocId, privacy: .public) and name: \(itemMetadata.fileName, privacy: OSLogPrivacy.auto(mask: .hash)), skipping enumeration")
+                }
+
+                dispatchGroup.leave()
             }
         }
 
-        return items
+        dispatchGroup.notify(queue: DispatchQueue.main) {
+            completionHandler(items)
+        }
     }
 
     private static func fileProviderPageforNumPage(_ numPage: Int) -> NSFileProviderPage {
@@ -300,21 +311,22 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
 
     private static func completeEnumerationObserver(_ observer: NSFileProviderEnumerationObserver, ncKit: NextcloudKit, numPage: Int, itemMetadatas: [NextcloudItemMetadataTable]) {
 
-        let items = FileProviderEnumerator.metadatasToFileProviderItems(itemMetadatas, ncKit: ncKit)
-        observer.didEnumerate(items)
-        Logger.enumeration.info("Did enumerate \(items.count) items")
-
-        // TODO: Handle paging properly
-        /*
-        if items.count == maxItemsPerFileProviderPage {
-            let nextPage = numPage + 1
-            let providerPage = NSFileProviderPage("\(nextPage)".data(using: .utf8)!)
-            observer.finishEnumerating(upTo: providerPage)
-        } else {
-            observer.finishEnumerating(upTo: nil)
+        metadatasToFileProviderItems(itemMetadatas, ncKit: ncKit) { items in
+            observer.didEnumerate(items)
+            Logger.enumeration.info("Did enumerate \(items.count) items")
+
+            // TODO: Handle paging properly
+            /*
+             if items.count == maxItemsPerFileProviderPage {
+             let nextPage = numPage + 1
+             let providerPage = NSFileProviderPage("\(nextPage)".data(using: .utf8)!)
+             observer.finishEnumerating(upTo: providerPage)
+             } else {
+             observer.finishEnumerating(upTo: nil)
+             }
+             */
+            observer.finishEnumerating(upTo: fileProviderPageforNumPage(numPage))
         }
-         */
-        observer.finishEnumerating(upTo: fileProviderPageforNumPage(numPage))
     }
 
     private static func completeChangesObserver(_ observer: NSFileProviderChangeObserver, anchor: NSFileProviderSyncAnchor, ncKit: NextcloudKit, newMetadatas: [NextcloudItemMetadataTable]?, updatedMetadatas: [NextcloudItemMetadataTable]?, deletedMetadatas: [NextcloudItemMetadataTable]?) {
@@ -341,36 +353,19 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
             allDeletedMetadatas = deletedMetadatas
         }
 
-        var allFpItemUpdates: [FileProviderItem] = []
         var allFpItemDeletionsIdentifiers = Array(allDeletedMetadatas.map { NSFileProviderItemIdentifier($0.ocId) })
+        if !allFpItemDeletionsIdentifiers.isEmpty {
+            observer.didDeleteItems(withIdentifiers: allFpItemDeletionsIdentifiers)
+        }
 
-        for updMetadata in allUpdatedMetadatas {
-            guard let parentItemIdentifier = NextcloudFilesDatabaseManager.shared.parentItemIdentifierFromMetadata(updMetadata) else {
-                Logger.enumeration.warning("Not enumerating change for metadata: \(updMetadata.ocId) \(updMetadata.fileName, privacy: OSLogPrivacy.auto(mask: .hash)) as could not get parent item metadata.")
-                continue
-            }
+        metadatasToFileProviderItems(allUpdatedMetadatas, ncKit: ncKit) { updatedItems in
 
-            guard !updMetadata.e2eEncrypted else {
-                // Precaution, if all goes well in NKFile conversion then this should not happen
-                // TODO: Remove when E2EE supported
-                Logger.enumeration.info("Encrypted metadata in changes enumeration \(updMetadata.ocId) \(updMetadata.fileName, privacy: OSLogPrivacy.auto(mask: .hash)), adding to deletions")
-                allFpItemDeletionsIdentifiers.append(NSFileProviderItemIdentifier(updMetadata.ocId))
-                continue
+            if !updatedItems.isEmpty {
+                observer.didUpdate(updatedItems)
             }
 
-            let fpItem = FileProviderItem(metadata: updMetadata, parentItemIdentifier: parentItemIdentifier, ncKit: ncKit)
-            allFpItemUpdates.append(fpItem)
-        }
-
-        if !allFpItemUpdates.isEmpty {
-            observer.didUpdate(allFpItemUpdates)
-        }
-
-        if !allFpItemDeletionsIdentifiers.isEmpty {
-            observer.didDeleteItems(withIdentifiers: allFpItemDeletionsIdentifiers)
+            Logger.enumeration.info("Processed \(updatedItems.count) new or updated metadatas, \(allDeletedMetadatas.count) deleted metadatas.")
+            observer.finishEnumeratingChanges(upTo: anchor, moreComing: false)
         }
-
-        Logger.enumeration.info("Processed \(allUpdatedMetadatas.count) new or updated metadatas, \(allDeletedMetadatas.count) deleted metadatas.")
-        observer.finishEnumeratingChanges(upTo: anchor, moreComing: false)
     }
 }