Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core Data slow fetching relationship objects

I'm loading all Node entities from Core Data with fetch request like this:

NSFetchRequest* request = [NSFetchRequest fetchRequestWithEntityName:@"Node"];
self.graphNodes = [self.viewController.objectContext executeFetchRequest:request error:&error];

Entity Node has one-to-many ordered (NSOrderedSet) relationship with Departure entities. I work with them directly:

self.nodeDepartures = node.departures.array;
Departure* departure = self.nodeDeparturesReference[index]; //Slow operation

But the last operation executes very slow and I don't understand why. In the XCode's time profiler I see a lot of executes of some NSSQLCore methods. Here is an image of called methods, but the main ones are (in chronological order):

[_NSFaultingMutableOrderedSet objectAtIndex:]
[_NSFaultingMutableOrderedSet willReadWithContents:]
[NSFaultHandler retainedFulfillAggregateFaultForObject:andRelationship:withContext:]
[NSPersistentStoreCoordinator(_NSInternalMethods) newValueForRelationship:forObjectWithID:withContext:error:]
... and cca. 20 more strange calls (check the image) ...

As far as I see, some error is occurred and the program can't find needed data. What am I doing wrong? Any ideas, please.

EDIT 1: I found out that problem was in faulting relationship (more about faulting here). The problem was "solved" with new way to fetch nodes:

NSFetchRequest* request = [NSFetchRequest fetchRequestWithEntityName:@"Node"];
[request setReturnsObjectsAsFaults:NO];
[request setRelationshipKeyPathsForPrefetching:@[ @"departures" ]];
self.graphNodes = [self.viewController.objectContext executeFetchRequest:request error:&error];

Anyway, it is not a good solution for me, because this prefetching takes a lot of time, but I need Nodes array immediately. Is there any way to prefetch departures property in background thread and use an array in parallel?

like image 442
kandaurov_net Avatar asked May 01 '26 13:05

kandaurov_net


1 Answers

You're on the right track, but with 1000 or so relationships per node you need to go a little farther.

Managed object contexts maintain an internal cache of fetched objects. You probably need to try and "warm up" the cache by fetching the objects in advance, before you need them. Fetch them early but don't use them. Then later when you need them, fetch again and the result will come from the cache.

The initial fetch doesn't need to ask for complete objects. You can set the resultType to managedObjectIDResultType and get the same result.

This will work best if you're using a background queue, i.e. one that doesn't use the main queue. Before you need the results, tell the background queue to perform this fetch in the background and then don't use the results.

like image 159
Tom Harrington Avatar answered May 04 '26 10:05

Tom Harrington



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!