Understanding the faulting of Transformables in CoreData

93 views Asked by At

I have the problem in my app, that deleting a lot of data out of my Database takes very long. So I started debugging and found out, that the value-transformers are the problem!

To see the problem I created a project that isolates the problem...

I have the following:

+-----------+     +-----------+
| Entity  A |     | Entity  B |
+-----------+     +-----------+
|           |     | property  |
| b         | 1:1 | a         |
+-----------+     +-----------+

So Entity A has a 1:1 relation to Entity B. And Entitiy B has a property called property which is Transformable and has a ValueTransformer. The delete rule of A to B is nullify.

Now I create one instance of A and B, link them and write something into property. Save the context and everything works as expected.

Now I want to delete all object of Entity A. I create a fetch request, and delete all entities. In my sample I do that like so:

context.perform {
    let fetchRequest = NSFetchRequest<EntityA>(entityName: "EntityA")
    guard let allEntities = try? context.fetch(fetchRequest) else { return }

    for entity in allEntities {
        context.delete(entity)
    }
    try? context.save()
}

And now when that save happens, the value transformers reverseTransformedValue is called. And I don't understand WHY?!? Cause I don't access that property... I don't set or get it...

Would be cool if somebody could shed some light for me.

I also have my sample project available here: https://www.dropbox.com/s/651dmyughosr90p/ValueTransformerIssue-Sample.zip?dl=0

1

There are 1 answers

4
Tom Harrington On BEST ANSWER

This is an interesting and somewhat surprising result, but I think it's Core Data operating normally. A couple of background details on Core Data:

  • When an object is read, it's normally a "fault". Property values aren't immediately loaded unless you configure your fetch request to require loading.
  • When the fault object "fires", all property values are loaded. This happens automatically when you access any property value, so if you look up one property, all property values are loaded.
  • In order to change the value on a property, Core Data is going to load the property values.

In your case what appears to be happening is:

  • You fetch instances of EntityA.
  • You delete those instances.
  • Since you're using "nullify" as the delete rule, deleting an EntityA means that Core Data needs to modify the corresponding EntityB by setting its value for a to nil.
  • Internally Core Data needs to load the EntityB instance. Since it's modifying one of the properties, it first loads all of the properties. At this point your value transformer executes even though it isn't really needed here.

If your value transformers are hurting performance, some things you might try to fix that include:

  • Move this property to a separate entity that's related to EntityB. Relationships faults don't fire when property faults do, so the nullify rule wouldn't load instances of this new entity.
  • Although NSBatchDeleteRequest will not honor delete rules (so "nullify" won't be applied), you might be able to combine one with an NSBatchUpdateRequest to fix the relationship. Something like, do a batch delete for EntityA instances followed by a batch update to set the relationship to nil on affected EntityB instances.

Or of course, maybe your value transformer can be optimized so that it's less of a problem?

Hope this helps. Good luck!