Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core Data migration techniques: moving attribute -> modelled relationship

I have a rather large Core Data-based database schema (~20 entities, over 140 properties) that is undergoing large changes as it migrates from our 1.x code base over to our 2.x code base.

I'm very familiar with performing lightweight migrations, but I'm a bit flummoxed with this particular migration because there's a few entities that used to store related objects as transformable attributes on the entity itself, but now I want to migrate those to actual entities.

This seems like a perfect example of when you should use a heavy migration instead of a lightweight one, but I'm not too happy about that either. I'm not familiar with heavy migrations, one of the entities that has this array -> modeled relationship conversion occurring takes up ~90% of the rows in the database, these databases tend to be more than 200 MB in size, and I know a good portion of our customers are using iPad 1s. That combined with the repeated warnings in Apple documentation and Marcus Zarra's (excellent) Core Data book regarding the speed and memory usage of a heavy migration make me very wary, and searching for another way to handle this situation.

WWDC 2010's "Mastering Core Data" session 118 (slides here, requires login, the 9th to last slide, with the title 'Migration Post-Processing' is what I'm referring to) mentions a way to sort of work around this – performing the migration, then using the store metadata to flag whether or not custom post processing you want to perform has been completed. I'm thinking this might be the way to go, but it feels a bit hacky (for lack of a better word) to me. Also, I'm worried about leaving attributes hanging around that are in practice, deprecated. ex. if I move entity foo's barArray attribute into a relationship between entity foo, and entity bar, and I nil out barArray, barArray still exists as an attribute that can be written to and read from. A potential way to solve this would be to signal that these attributes are deprecated by changing their names to have "deprecated" in front, as well as perhaps overriding the accessors to assert if they're used, but with KVO, there's no guaranteed compile-time solution that will prevent people from using them, and I loathe leaving 'trap code' around, especially since said 'trap code' will have to be around as long as I potentially have customers who still need to migrate from 1.0.

This turned into more of a brain dump than I intended, so for sake of clarity, my questions are:
1) Is a heavy migration a particularly poor choice with the constraints I'm working under? (business-critical app, lack of experience with heavy migrations, databases over 200 MB in size, tens of thousands of rows, customers using iPad 1s running iOS 5+)
2) If so, is the migration post-processing technique described in session 118 my best bet?
3) If so, how can I right away/eventually eliminate those 'deprecated' attributes so that they are no longer polluting my code base?

like image 736
refulgentis Avatar asked Apr 03 '13 22:04

refulgentis


People also ask

What is core data migration?

Core Data can typically perform an automatic data migration, referred to as lightweight migration. Lightweight migration infers the migration from the differences between the source and the destination managed object models.

What is data model migration?

Data migration allows you to convert data from one model (schema) to another, using mappings. The migration process itself is discussed in The Migration Process. How you perform a migration is discussed in Initiating the Migration Process.


1 Answers

My suggestion is to stay away from heavy migration; full stop. It is too expensive on iOS and most likely will lead to a unacceptable user experience.

In this situation I would do a lazy migration. Create a lightweight migration that has the associated objects.

Then do the migration but don't move any data yet.

Change the accessor for that new relationship so that it first checks the old transformable, if the transformable is populated it pulls the data out, copies it over to the new relationship and then nil's out the transformable.

Doing this will cause the data to move over as it is being used.

Now there are some issues with this design.

If you are wanting to use predicates against those new objects it is going to be ... messy. You will want to do a two pass fetch. i.e. Fetch with a predicate that does not hit that new object and then do a section fetch once they are memory so that the transformable gets moved over.

like image 189
Marcus S. Zarra Avatar answered Oct 20 '22 04:10

Marcus S. Zarra