-
Notifications
You must be signed in to change notification settings - Fork 121
[Core Data] Make new Core Data migration path. Remove models below 30 #15987
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Core Data] Make new Core Data migration path. Remove models below 30 #15987
Conversation
Generated by 🚫 Danger |
this will make some test fail (expected), ie: test_iterativeMigrate_can_iteratively_migrate_from_model_1_to_model_10 since we cannot iterateively migrate anymore below 30
|
|
Modules/Tests/StorageTests/CoreData/CoreDataIterativeMigratorTests.swift
Show resolved
Hide resolved
|
Only one review is needed. No rush to review this before 23.0 freeze. |
|
Apology for not getting to it today. I will take a look first thing next Monday if it's still open. |
jaclync
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for taking on this task to reduce the app size. I had some questions marked with ❓ , especially the one about whether it can detect a deleted model version. Let me know what you think.
Modules/Sources/Storage/CoreData/CoreDataIterativeMigrator.swift
Outdated
Show resolved
Hide resolved
| let modelVersion23 = ModelVersion(name: "Model 23") | ||
| let modelVersion31 = ModelVersion(name: "Model 31") | ||
| let modelVersion23 = ModelVersion(name: "Model 33") | ||
| let modelVersion31 = ModelVersion(name: "Model 41") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❓ I'm a bit confused by why these model versions need to be incremented by 10, instead of removing the ones < 30?
nit: for all the diffs in this file, the constant names can be updated to match the model version name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit confused by why these model versions need to be incremented by 10, instead of removing the ones < 30?
No specific reason, I just added +10 to all of the cases for consistency. Since model 23 is below the current threshold it will return nil and not trigger the iterative migration for the test. Or are you referring to something else?
the constant names can be updated to match the model version name
Uggh of course I forgot, thanks! 😅 Updated 66da437
Modules/Tests/StorageTests/CoreData/CoreDataIterativeMigrator+MigrationStepTests.swift
Show resolved
Hide resolved
| guard let sourceVersion = CoreDataMigratorUtils.findSourceVersion(for: sourceModel, in: modelsInventory) else { | ||
| DDLogInfo("Could not determine source model version. Using iterative migration.") | ||
| return false | ||
| } | ||
| // If the merchant is on a model version older than this, nuke the DB and migrate them to the latest one without iterative process | ||
| let oldestModelThreshold = Constants.oldestSupportedDataModel | ||
| let versionNumber = CoreDataMigratorUtils.extractVersionNumber(from: sourceVersion.name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: how about combining CoreDataMigratorUtils.findSourceVersion and CoreDataMigratorUtils.extractVersionNumber into one function to simplify the code a bit? It looks like the two methods are only called here.
| guard let sourceVersion = CoreDataMigratorUtils.findSourceVersion(for: sourceModel, in: modelsInventory) else { | |
| DDLogInfo("Could not determine source model version. Using iterative migration.") | |
| return false | |
| } | |
| // If the merchant is on a model version older than this, nuke the DB and migrate them to the latest one without iterative process | |
| let oldestModelThreshold = Constants.oldestSupportedDataModel | |
| let versionNumber = CoreDataMigratorUtils.extractVersionNumber(from: sourceVersion.name) | |
| guard let versionNumber = CoreDataMigratorUtils.findSourceVersionNumber(for: sourceModel, in: modelsInventory) else { | |
| DDLogInfo("Could not determine source model version. Using iterative migration.") | |
| return false | |
| } | |
| // If the merchant is on a model version older than this, nuke the DB and migrate them to the latest one without iterative process | |
| let oldestModelThreshold = Constants.oldestSupportedDataModel |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general I'd prefer to keep these separated, but with the simplification you suggested on 6080964 we can also remove extractVersionNumber entirely.
Modules/Tests/StorageTests/CoreData/CoreDataIterativeMigratorTests.swift
Show resolved
Hide resolved
Modules/Tests/StorageTests/CoreData/CoreDataIterativeMigratorTests.swift
Show resolved
Hide resolved
Modules/Tests/StorageTests/CoreData/CoreDataIterativeMigratorTests.swift
Outdated
Show resolved
Hide resolved
| XCTAssertEqual(versionNumber, 0) | ||
| } | ||
|
|
||
| func test_extractVersionNumber__when_version_number_then_handles_numbered_models() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
super nit:
| func test_extractVersionNumber__when_version_number_then_handles_numbered_models() { | |
| func test_extractVersionNumber_when_version_number_then_handles_numbered_models() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed with 6080964
| XCTAssertNil(NSEntityDescription.entity(forEntityName: Note.entityName, | ||
| // The OrderFeeLine entity does not exist in Model 30, was introduced in Model 42 | ||
| // This proves that the store was reset to Model 30. | ||
| XCTAssertNil(NSEntityDescription.entity(forEntityName: OrderFeeLine.entityName, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice replacement 👍
Rather than relying on a set constant and comparing model versions, we can rely on the existence of the model itself in the build to simplify the logic
jaclync
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the updates, LGTM
Tested with TestFlight 22.3 build then PR build with model versions up to 121 deleted. Didn't see the DDLogInfo messages, but the app didn't crash and recovered after a bit 👍

Apologies for the 15k LOC, it belongs mostly to deleting the
.xcdatamodelsand associated.xcmappings, the actual changes are the ~200 additions.Description
The app currently has 124+ Core Data model versions, which represents 7+ years of incremental migrations. This PR starts the work on clearing the app from core data old models as part of p91TBi-drE-p2 by triggering a new migration path when a merchant hits the threshold (model 30). In future PRs we'll be raising the threshold to get it closer to the latest version.
I left a bunch of DDLogs with what's happening, following the style of other core data handling functions, and through the migration steps.
Changes
CoreDataMigratorUtilsto encapsulate the logic related to finding the current DB versionCoreDataIterativeMigratorwith the new migration path. This relies on the existing persistent store coordinator to automatically create a fresh store with the latest model as needed..xcdatamodel.xcmappingsmigrations between several of these modelsSteps to reproduce/Testing information
This is not very straight-forward to test, ultimately we'd want the merchant to not crash if they happen to be on a model version that has been deleted when a migration is performed. This crash is expected with an iterative migration if a step is missing, but won't happen with the direct migration.
If the worst happens and they crash due being unable to perform iterative migration, the app already performs a direct migration as a fallback, which is what we leverage directly via the threshold check when we can.
I added instructions on the P2 at p91TBi-drE-p2 on how I initially tested it by deleting 120 models instead, since this is the oldest available in TestFlight it allows us to simulate what would happen when a migration is needed and the threshold is hit.
At the moment of writing this PR the latest available TF version is 22.3, which as per MIGRATIONS.md was using model 120, so I'll use that as example. You can:
oldestSupportedDataModelto 122 or higher (including the model version that your TF build should be in), so it determines that anything below is an old model and should be purged entirely..xcdatamodelsup till 122 in Xcode, then run the app from Xcode in the same device you previously ran the TF build.RELEASE-NOTES.txtif necessary.