views:

53

answers:

2

Sometimes I noticed that a 'save:' operation an a ManagedObjectContext never returns and consumes 100% CPU.

I'm using an SQL Store in a GarbageCollected environment (Mac OS X 10.6.3). The disk activity shows about 700 KB/s writing. While having a look at the folder that contains the sqlite database file the "-journal" file appears and disappears, appears and disappears, ...

This is part of the call graph from the process analysis:

  2203 -[NSManagedObjectContext save:]
    1899 -[NSPersistentStoreCoordinator(_NSInternalMethods) executeRequest:withContext:]
      1836 -[NSSQLCore executeRequest:withContext:]
        1836 -[NSSQLCore saveChanges:]
          1479 -[NSSQLCore performChanges]
            ...
          335 -[NSSQLCore recordChangesInContext:]
            ...
          20 -[NSSQLCore rollbackChanges]
            ...
          2 -[NSSQLCore prepareForSave:]
            ...
      62 -[NSPersistentStoreCoordinator(_NSInternalMethods) _checkRequestForStore:originalRequest:andOptimisticLocking:]
        ...
      1 -[NSPersistentStore(_NSInternalMethods) _preflightCrossCheck]
        ...
    184 -[NSMergePolicy resolveConflicts:]
      ...
    120 -[NSManagedObjectContext(_NSInternalChangeProcessing) _prepareForPushChanges:]
      ...

Everything a happening in the main GUI thread.

Any ideas what I can to do to resolve the problem?

A: 

Clearly you most likely have a loop. Are you monitoring or reacting to the NSNotification being broadcast by the NSManagedObjectContext? That is usually the only thing that will cause a loop like this.

Are you using any threading?

Update

Turn off your observer of the save and see if the loop still exists. Without seeing your code that is the best guess as to the cause of your loop.

Marcus S. Zarra
I've registered an observer for NSManagedObjectContextDidSaveNotification but this is never called in this situation (the entire call graph doesn't contain any ob my methods after calling the NSManagedObjectContext's save method. Maybe you'll find the reason for the problem if you send you / or post here the entire call graph!?No, I'm not using any threading ...
Robert
A: 

Thanks Marcus for your hint that this is a loop! But this loop wasn't caused by an observer for a notification.

The reason for the loop is an bug (I think it is one) in CoreData: One of the entities that are saved in this situation has a property of type Double. And the property value was changed. So CoreData compares the stored value with the cached value and sometimes the decimal digits causes CoreData NSSQLCore to "think" that the stored values was changed from outside.

Normally this would cause the save: method to fail and return an NSError but in my case a configured the NSManagedObjectContext to use the NSMergeByPropertyObjectTrumpMergePolicy merge policy. So because CoreData falsely thinks that there was an external change it just takes the in-memory property value (that has some "unfavorable" digits) and stores that. After that it compares the stored value with the in-memory value and again detects an inequality and performs an rollback and stores the value again - that's the loop.

Since I force the Double property to store NSNumber objects that contain integer values, the save: is successful.

This link link text helped me to find the out.

I'm NOT an CoreData specialist so I'm not sure if everything was explained correctly.

Robert