coredata and loooooooooooooooong waits

  • Hi, I have a problem with coredata fetches. The problem is strange, since I can't reproduce them myself, but a few beta testers contact me that my app sometimes taes 5-25 seconds to complete a task. Narrowing down the problem using extensive loging (the bet testers just hated all the lines in the console prointout :D) I found that the problem is in a method I called arrangedObjectsForStore.

    My data model contains two entities, one is called a store and the other is an item. A store can have multiple items using a one-to-many relationship, and there's a reverse relationship so I can always tell from an item which store it is assigned to.

    The code is as follows:

    - (NSArray*)arrangedObjectsForStore:(NSManagedObject*)store
    {
    NSManagedObjectContext * moc = [[NSApp delegate] managedObjectContext];
    NSEntityDescription * entityDescription = [NSEntityDescription entityForName:@"storeEntity" inManagedObjectContext:moc];
    NSFetchRequest * request = [[[NSFetchRequest alloc] init] autorelease];
    [request setEntity:entityDescription];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"storeID = %d", [[store valueForKeyPath:@"storeID"] intValue]];
    [request setPredicate:predicate];
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"storeID" ascending:NO];
    [request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
    [sortDescriptor release];

    NSArray* result = [moc executeFetchRequest:request error:nil];

    result = [[[result objectAtIndex:0] valueForKeyPath:@"storeClips"] allObjects];

    result = [result sortedArrayUsingDescriptors:[NSArray arrayWithObject:[[[NSSortDescriptor alloc] initWithKey:@"clipID" ascending:NO] autorelease]]];

    return result;
    }

    Now judging from the logs this can take anywhere from 5-25 seconds to return, and the number of stores is less than 5, the items per store are limited to 1000. I don't really think it should take this long to return. Also, once fetched, the next time it will be almost instantaneous until I add a new item to the context, then it again takes 5-25 seconds to return.

    And this happens on both a dual G4 and a core duo iMac - at least that's the machines the beta testers reported problems with.

    Am I doing something wrong in my fetch request? How can I speed things up? SHould this really be happening?

    THanks for any hints to the right direction.

    Ben
  • On 17/10/2006, at 10.00, Benjamin Salanki wrote:

    > - (NSArray*)arrangedObjectsForStore:(NSManagedObject*)store
    > {
    > NSManagedObjectContext * moc = [[NSApp delegate]
    > managedObjectContext];
    > NSEntityDescription * entityDescription = [NSEntityDescription
    > entityForName:@"storeEntity" inManagedObjectContext:moc];
    > NSFetchRequest * request = [[[NSFetchRequest alloc] init]
    > autorelease];
    > [request setEntity:entityDescription];
    > NSPredicate *predicate = [NSPredicate
    > predicateWithFormat:@"storeID = %d", [[store
    > valueForKeyPath:@"storeID"] intValue]];
    > [request setPredicate:predicate];
    > NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]
    > initWithKey:@"storeID" ascending:NO];
    > [request setSortDescriptors:[NSArray
    > arrayWithObject:sortDescriptor]];
    > [sortDescriptor release];
    >
    > NSArray* result = [moc executeFetchRequest:request error:nil];

    I can't tell exactly, but isn't this fetch request retrieving your
    "store" object? The one you already have? What is the point of that?

    > result = [[[result objectAtIndex:0] valueForKeyPath:@"storeClips"]
    > allObjects];

    This gives you an array of clips, all potentially faults.

    > result = [result sortedArrayUsingDescriptors:[NSArray
    > arrayWithObject:[ autorelease]]];

    This fires the faults one at a time. Very slow.

    Just execute a fetch against the clip entity directly, using the
    inverse relationship:

    NSFetchRequest * request = [[[NSFetchRequest alloc] init] autorelease];
    [request setEntity:[NSEntityDescription entityForName:@"clipEntity"
    inManagedObjectContext:moc]];
    [request setPredicate:[NSPredicate predicateWithFormat:@"store = %
    @", store]];
    NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc]
    initWithKey:@"clipID" ascending:NO];
    [request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
    [sortDescriptor release];
    NSArray* result = [moc executeFetchRequest:request error:nil];

    And don't ignore the error...
  • I am in fact using an SQL store, and I originally used this code:

    NSManagedObjectContext * moc = [[NSApp delegate] managedObjectContext];
    NSEntityDescription * entityDescription = [NSEntityDescription entityForName:@"SCEntity" inManagedObjectContext:moc];
    NSFetchRequest * request = [[[NSFetchRequest alloc] init] autorelease];
    [request setEntity:entityDescription];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"storeID.storeID = %d", [[store valueForKeyPath:@"storeID"] intValue]];
    [request setPredicate:predicate];
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"clipID" ascending:NO];
    [request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
    [sortDescriptor release];

    NSArray* result = [moc executeFetchRequest:request error:nil];

    and this is where I first got the responses that it was taking too long. After this I changed to the approach of going from the store side of things rather than the item side.

    Ben


    On Tuesday, October 17, 2006, at 11:10AM, Jakob Olesen <stoklund...> wrote:

    >
    > On 17/10/2006, at 10.54, Benjamin Salanki wrote:
    >
    >> But if I do it this way it will read through all the clips, which
    >> can be in the thousands, rather than all the stores, where there's
    >> only 5 max. Or am I not getting this right?
    >
    > In theory, yes, but Core Data creates indices for all the
    > relationships, so SQLite is able to use an index for that query.
    > It is pretty much the same query Core Data is using internally when
    > you say [store valueForKey:@"storeClips"]
    >
    > Alternatively, you can do a batch fault with a predicate "SELF in %
    > @", [store valueForKey:@"storeClips"]
    >
    > This is only true for the SQLite store. The XML store will be slow,
    > it has no indices.
    >
    >
    >
    >
previous month october 2006 next month
MTWTFSS
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          
Go to today