5

I have a Core Data layer with several thousand entities, constantly syncing to a server. The sync process uses fetch requests to check for deleted_at for the purposes of soft-deletion. There is a single context performing save operations in a performBlockAndWait call. The relationship mapping is handled by the RestKit library.

The CoreDataEntity class is a subclass of NSManagedObject, and it is also the superclass for all our different core data object classes. It has some attributes that are inherited by all our entities, such as deleted_at, entity_id, and all the boilerplate fetch and sync methods.

My issue is some fetch requests seem to return inconsistent results after modifications to the objects. For example after deleting an object (setting deleted_at to the current date):

[CoreDataEntity fetchEntitiesWithPredicate:[NSPredicate predicateWithFormat:@"deleted_at==nil"]];

Returns results with deleted_at == [NSDate today]

I have successfully worked around this behavior by additionally looping through the results and removing the entities with deleted_at set, however I cannot fix the converse issue:

[CoreDataEntity fetchEntitiesWithPredicate:[NSPredicate predicateWithFormat:@"deleted_at!=nil"]];

Is returning an empty array in the same conditions, preventing a server sync from succeeding.

I have confirmed deleted_at is set on the object, and the context save was successful. I just don't understand where to reset whatever cache is causing the outdated results?

Thanks for any help!

Edit: Adding a little more information, it appears that once one of these objects becomes corrupted, the only way get it to register is modifying the value again. Could this be some sort of Core Data index not updating when a value is modified?

Update: It appears to be a problem with RestKit https://github.com/RestKit/RestKit/issues/2218

Lytic
  • 786
  • 5
  • 12
  • Have you checked your contexts? Ensure that you are saving on proper contexts that are merging to the main context by the time you access them. You could be performing operations and might not be syncing your contexts correctly. With the code provided above, there's really not a lot to go off of considering it could be a lot of things. – TheCodingArt Jun 30 '15 at 16:56
  • There is only one context. I will add that to the question. Thanks! – Lytic Jun 30 '15 at 17:15
  • Can you add details of your `CoreDataEntity` to your post? Plus the `performBlockAndWait` code. – pbasdf Jun 30 '15 at 21:29
  • I don't know if you've figured out the issue in your own time, but we definitely need more code to go off of to determine the noted issue. Can you setup a dummy project with data models and a core data stack that can replicate this issue? – TheCodingArt Jul 02 '15 at 18:53
  • It's extremely intermittent. I will attempt to do so. – Lytic Jul 02 '15 at 23:39
  • Still sounds like a threading issue to me. Typically any Core Data that's out of sync is due to the proper data not being saved to the main context. – TheCodingArt Jul 03 '15 at 18:53
  • Sorry it has taken so long to respond, I agree with your suggestion that it's likely threading. Now that I have time to look into the issue it appears to be a problem with an unresolved issue in RestKit https://github.com/RestKit/RestKit/issues/2218 – Lytic Aug 03 '15 at 23:54

3 Answers3

0

First, after a save have you looked in the store to make sure your changes are there? Without seeing your entire Core Data stack it is difficult to get a solid understanding what might be going wrong. If you are saving and you see the changes in the store then the question comes into your contexts. How are they built and when. If you are dealing with sibling contexts that could be causing your issue.

More detail is required as to how your core data stack looks.

Yes, the changes are there. As I mentioned in the question, I can loop through my results and remove all those with deleted_at set successfully

That wasn't my question. There is a difference between looking at objects in memory and looking at them in the SQLite file on disk. The questions I have about this behavior are:

  1. Are the changes being persisted to disk before you query for them again
  2. Are you working with multiple contexts and potentially trying to fetch from a stale sibling.

Thus my questions about on disk changes and what your core data stack looks like.

Threading

If you are using one context, are you using more than one thread in your app? If so, are you using that context on more than one thread?

I can see a situation where if you are violating the thread confinement rules you can be corrupting data like this.

Marcus S. Zarra
  • 46,571
  • 9
  • 101
  • 182
  • Yes, the changes are there. As I mentioned in the question, I can loop through my results and remove all those with deleted_at set successfully. – Lytic Jul 02 '15 at 09:48
  • Ah, sorry for misunderstanding. I will check the disk and let you know. As far as multiple contexts... I believe we are only using one. I have been attempting to create a new context for every save operation to see if that mitigates the issue, but I have not been successful yet. – Lytic Jul 02 '15 at 17:10
  • Adding more contexts will confuse issues, don't use more than one context unless you know why you are doing it. Throwing them in there to "help" works as well as adding threads to "help". – Marcus S. Zarra Jul 06 '15 at 21:39
0

You are apparently using some sintactic sugar extension to Core Data. I suppose that in your case it is a SheepData, right?

fetchEntitiesWithPredicate: there implemented as follows:

+ (NSArray*)fetchEntitiesWithPredicate:(NSPredicate*)aPredicate
{
    return [self fetchEntitiesWithPredicate:aPredicate inContext:[SheepDataManager sharedInstance].managedObjectContext];
}

Are you sure that [SheepDataManager sharedInstance].managedObjectContext receives all the changes that you are making to your objects? Is it receives notifications of saves, or is it child context of your save context?

Try to replace your fetch one-liner with this:

[<your saving context> performBlockAndWait:^{
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"CoreDataEntity"];
    request.predicate = [NSPredicate predicateWithFormat:@"deleted_at==nil"];

    NSArray *results = [<your saving context> executeFetchRequest:request error:NULL];
}];
bteapot
  • 1,897
  • 16
  • 24
-1

Try adding an extra attribute deleted that is a bool with a default of false. Then the attribute is always set and you can look for entities that are either true or false depending on your needs at the moment. If the value is true then you can look at deleted_at to find out when.

Alternatively try setting the deleted_at attribute to some old date (like perhaps 1 Jan 1980), then anything that isn't deleted will have a fixed date that is too old to have been set by the user.

Edit: There is likely some issue with deleted_at having never been touched on some entities that is confusing the system. It is also possible that you have set the fetch request to return results in the dictionary style in which case recent changes will not be reflected in the fetch results.

theMikeSwan
  • 4,739
  • 2
  • 31
  • 44
  • I will attempt this, thank you. However, we are having a similar issue with a boolean favorite flag on a different entity. I believe they are related. I will report back if this works. Thanks! – Lytic Jun 30 '15 at 17:20
  • In response to your edit: what would cause recent changes not to be reflected specifically? – Lytic Jun 30 '15 at 18:02
  • Fetches come from the on disk version of the store not the in-memory version when the fetch request is set to a return type of `DictionaryResultType` this means that it ignores the value in `includesPendingChanges` (for full discussion see https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Reference/CoreDataFramework/Classes/NSFetchRequest_Class/index.html#//apple_ref/occ/instp/NSFetchRequest/includesPendingChanges) Telling the context to save first hasn't worked in my experience. – theMikeSwan Jun 30 '15 at 18:13
  • We are using the default result type, and we were not including pending changes... but including pending changes did not fix the issue. – Lytic Jun 30 '15 at 18:26