Originally created by @jeanetienne on GitHub (Jun 25, 2021).
Hi John & Contributors, thanks for providing such a valuable tool, making CoreData much more palatable, high level and swifty!
I am using CoreStore 8.0.1 and I can't find a way to make a custom migration work.
It seems to hang for a veeery long time (7+ minutes in iOS Simulator), and I get an error:
[BackgroundTask] Background Task 4 ("CoreData: Adding persistent store"), was created over 30 seconds ago. In applications running in the background, this creates a risk of termination. Remember to call UIApplication.endBackgroundTask(_:) for your task in a timely manner to avoid this.
When I only try to do a V1 to V2 migration and I let CoreStore infer all the migration (deleting a table and deleting a column), it works fine. As soon as I add a V2 to V3 migration with the CustomSchemaMappingProvider, it exhibits this behaviour (hanging for 7 min and erroring).
Let me know if you need more details. I would appreciate any help understanding this problem! Thank you!
(I renamed my entities and table names, but everything else is the same)
Setup Details
Project
iOS 12.1+
CoreStore 8.0.1
Xcode 12.5
Schema
enumDatabaseSchema{enumV3{staticletname:ModelVersion="V3"staticletschema=CoreStoreSchema(modelVersion:Self.name,entities:[Entity<SomeNewEntity>("SomeNewTable"),Entity<MyEntity_01>("MyTable_01"),Entity<MyEntity_02>("MyTable_02"),Entity<MyEntity_03>("MyTable_03"),Entity<MyEntity_03>("MyTable_04"),],versionLock:[// ...])}enumV2{staticletname:ModelVersion="V2"staticletschema=CoreStoreSchema(modelVersion:Self.name,entities:[Entity<MyEntity_01>("MyTable_01"),Entity<MyEntity_02>("MyTable_02"),Entity<MyEntity_03>("MyTable_03"),Entity<MyEntity_04>("MyTable_04"),// Removed a column since DatabaseSchema.V1.MyLegacyEntity_04// Removing MyLegacyEntity_05 and its associated table "MyTable_05"],versionLock:[//...])}enumV1{staticletname:ModelVersion="V1"staticletschema=CoreStoreSchema(modelVersion:Self.name,entities:[Entity<MyEntity_01>("MyTable_01"),Entity<MyEntity_02>("MyTable_02"),Entity<MyEntity_03>("MyTable_03"),Entity<DatabaseSchema.V1.MyLegacyEntity_04>("MyTable_04"),Entity<DatabaseSchema.V1.MyLegacyEntity_05>("MyTable_05"),],versionLock:[//...])}}extensionDatabaseSchema.V3{classSomeNewEntity:CoreStoreObject{@Field.Stored("first_property")varfirstProperty:String=""@Field.Stored("second_property")varsecondProperty:Date?@Field.Stored("third_property")varthirdProperty:Bool=false}}
Database setup
funcsetup(_completion:@escaping(Result<Void,Error>)->Void){Log.info("Setting up the local database")CoreStoreDefaults.dataStack=DataStack(DatabaseSchema.V1.schema,DatabaseSchema.V2.schema,DatabaseSchema.V3.schema,migrationChain:[DatabaseSchema.V1.name,DatabaseSchema.V2.name,DatabaseSchema.V3.name])letv2_to_v3_mapping=CustomSchemaMappingProvider(from:"V2",to:"V3",entityMappings:[.insertEntity(destinationEntity:"SomeNewTable"),.copyEntity(sourceEntity:"MyTable_01",destinationEntity:"MyTable_01"),.copyEntity(sourceEntity:"MyTable_02",destinationEntity:"MyTable_02"),.copyEntity(sourceEntity:"MyTable_04",destinationEntity:"MyTable_04"),.transformEntity(sourceEntity:"MyTable_03",destinationEntity:"MyTable_03",transformer:{(sourceObject:CustomSchemaMappingProvider.UnsafeSourceObject,createDestinationObject:()->CustomSchemaMappingProvider.UnsafeDestinationObject)inletdestinationObject=createDestinationObject()destinationObject.enumerateAttributes{(attribute,sourceAttribute)inifletsourceAttribute=sourceAttribute{destinationObject[attribute]=sourceObject[sourceAttribute]}}// Mark all items as enableddestinationObject["enabled"]=true// Uppercase all items' titlesdestinationObject["title"]=(destinationObject["title"]as?String)?.uppercased()})])letstorage=SQLiteStore(fileName:Constants.databaseFileName,migrationMappingProviders:[v2_to_v3_mapping])// Discarding the progress indicator for now. Will use later._=CoreStoreDefaults.dataStack.addStorage(storage){resultincompletion(result.map{_in}.mapError{$0asError})}}
Simulator output
2021-06-25 11:35:42.972661+1000 MyApp[4861:318117] info: Setting up the local database
2021-06-25 11:36:18.790029+1000 MyApp[4861:318117][BackgroundTask] Background Task 4("CoreData: Adding persistent store"), was created over 30 seconds ago. In applications running in the background, this creates a risk of termination. Remember to call UIApplication.endBackgroundTask(_:)for your task in a timely manner to avoid this.
2021-06-25 11:43:43.988169+1000 MyApp[4861:319180][error] error: addPersistentStoreWithType:configuration:URL:options:error: returned error NSCocoaErrorDomain (134090)
CoreData: error: addPersistentStoreWithType:configuration:URL:options:error: returned error NSCocoaErrorDomain (134090)
CoreData: annotation: userInfo:
CoreData: annotation: NSSQLiteErrorDomain : 5
CoreData: annotation: storeType: SQLite
CoreData: annotation: configuration: (null)
CoreData: annotation: URL: file:///Users/jeanetienne/Library/Developer/CoreSimulator/Devices/D7BC2069-1D37-466E-BE36-04A8530AB99E/data/Containers/Data/Application/37A4C44D-BCD1-442D-924E-37694F0367BD/Library/Application%20Support/my_app_bundle_id/my_database.sqlite
CoreData: annotation: options:
CoreData: annotation: _NSBinaryStoreInsecureDecodingCompatibilityOption : 1
CoreData: annotation: NSSQLitePragmasOption : {"journal_mode"= DELETE;}
⚠️ [CoreStore: Error] DataStack+Migration.swift:449 upgradeStorageIfNeeded(_:metadata:completion:)
↪︎ Failed to migrate version model "V1" to version "V2".
(CoreStore.CoreStoreError) .internalError (
.errorDomain ="com.corestore.error";
.errorCode = 5;
.NSError =(
.domain ="NSCocoaErrorDomain";
.code = 134090;
.userInfo =1 key-value(s)["NSSQLiteErrorDomain"= 5;];);)
Surprisingly this error output make it look like the migration from V1 to V2 has a problem, when in fact this migration works fine when I comment out anything related to V3🤔 ...
Thanks!
Originally created by @jeanetienne on GitHub (Jun 25, 2021).
Hi John & Contributors, thanks for providing such a valuable tool, making CoreData much more palatable, high level and swifty!
I am using CoreStore 8.0.1 and I can't find a way to make a custom migration work.
It seems to hang for a veeery long time (7+ minutes in iOS Simulator), and I get an error:
```
[BackgroundTask] Background Task 4 ("CoreData: Adding persistent store"), was created over 30 seconds ago. In applications running in the background, this creates a risk of termination. Remember to call UIApplication.endBackgroundTask(_:) for your task in a timely manner to avoid this.
```
When I only try to do a `V1` to `V2` migration and I let CoreStore infer all the migration (deleting a table and deleting a column), it works fine. As soon as I add a `V2` to `V3` migration with the `CustomSchemaMappingProvider`, it exhibits this behaviour (hanging for 7 min and erroring).
Let me know if you need more details. I would appreciate any help understanding this problem! Thank you!
_(I renamed my entities and table names, but everything else is the same)_
# Setup Details
## Project
- iOS 12.1+
- CoreStore 8.0.1
- Xcode 12.5
## Schema
```swift
enum DatabaseSchema {
enum V3 {
static let name: ModelVersion = "V3"
static let schema = CoreStoreSchema(
modelVersion: Self.name,
entities: [
Entity<SomeNewEntity>("SomeNewTable"),
Entity<MyEntity_01>("MyTable_01"),
Entity<MyEntity_02>("MyTable_02"),
Entity<MyEntity_03>("MyTable_03"),
Entity<MyEntity_03>("MyTable_04"),
],
versionLock: [
// ...
]
)
}
enum V2 {
static let name: ModelVersion = "V2"
static let schema = CoreStoreSchema(
modelVersion: Self.name,
entities: [
Entity<MyEntity_01>("MyTable_01"),
Entity<MyEntity_02>("MyTable_02"),
Entity<MyEntity_03>("MyTable_03"),
Entity<MyEntity_04>("MyTable_04"), // Removed a column since DatabaseSchema.V1.MyLegacyEntity_04
// Removing MyLegacyEntity_05 and its associated table "MyTable_05"
],
versionLock: [
//...
]
)
}
enum V1 {
static let name: ModelVersion = "V1"
static let schema = CoreStoreSchema(
modelVersion: Self.name,
entities: [
Entity<MyEntity_01>("MyTable_01"),
Entity<MyEntity_02>("MyTable_02"),
Entity<MyEntity_03>("MyTable_03"),
Entity<DatabaseSchema.V1.MyLegacyEntity_04>("MyTable_04"),
Entity<DatabaseSchema.V1.MyLegacyEntity_05>("MyTable_05"),
],
versionLock: [
//...
]
)
}
}
extension DatabaseSchema.V3 {
class SomeNewEntity: CoreStoreObject {
@Field.Stored("first_property") var firstProperty: String = ""
@Field.Stored("second_property") var secondProperty: Date?
@Field.Stored("third_property") var thirdProperty: Bool = false
}
}
```
## Database setup
```swift
func setup(_ completion: @escaping (Result<Void, Error>) -> Void) {
Log.info("Setting up the local database")
CoreStoreDefaults.dataStack = DataStack(
DatabaseSchema.V1.schema, DatabaseSchema.V2.schema, DatabaseSchema.V3.schema,
migrationChain: [DatabaseSchema.V1.name, DatabaseSchema.V2.name, DatabaseSchema.V3.name]
)
let v2_to_v3_mapping = CustomSchemaMappingProvider(
from: "V2",
to: "V3",
entityMappings: [
.insertEntity(destinationEntity: "SomeNewTable"),
.copyEntity(sourceEntity: "MyTable_01", destinationEntity: "MyTable_01"),
.copyEntity(sourceEntity: "MyTable_02", destinationEntity: "MyTable_02"),
.copyEntity(sourceEntity: "MyTable_04", destinationEntity: "MyTable_04"),
.transformEntity(
sourceEntity: "MyTable_03",
destinationEntity: "MyTable_03",
transformer: { (sourceObject: CustomSchemaMappingProvider.UnsafeSourceObject, createDestinationObject: () -> CustomSchemaMappingProvider.UnsafeDestinationObject) in
let destinationObject = createDestinationObject()
destinationObject.enumerateAttributes { (attribute, sourceAttribute) in
if let sourceAttribute = sourceAttribute {
destinationObject[attribute] = sourceObject[sourceAttribute]
}
}
// Mark all items as enabled
destinationObject["enabled"] = true
// Uppercase all items' titles
destinationObject["title"] = (destinationObject["title"] as? String)?.uppercased()
})
]
)
let storage = SQLiteStore(fileName: Constants.databaseFileName, migrationMappingProviders: [v2_to_v3_mapping])
// Discarding the progress indicator for now. Will use later.
_ = CoreStoreDefaults.dataStack.addStorage(storage) { result in
completion(result.map { _ in }.mapError { $0 as Error })
}
}
```
# Simulator output
```bash
2021-06-25 11:35:42.972661+1000 MyApp[4861:318117] info: Setting up the local database
2021-06-25 11:36:18.790029+1000 MyApp[4861:318117] [BackgroundTask] Background Task 4 ("CoreData: Adding persistent store"), was created over 30 seconds ago. In applications running in the background, this creates a risk of termination. Remember to call UIApplication.endBackgroundTask(_:) for your task in a timely manner to avoid this.
2021-06-25 11:43:43.988169+1000 MyApp[4861:319180] [error] error: addPersistentStoreWithType:configuration:URL:options:error: returned error NSCocoaErrorDomain (134090)
CoreData: error: addPersistentStoreWithType:configuration:URL:options:error: returned error NSCocoaErrorDomain (134090)
CoreData: annotation: userInfo:
CoreData: annotation: NSSQLiteErrorDomain : 5
CoreData: annotation: storeType: SQLite
CoreData: annotation: configuration: (null)
CoreData: annotation: URL: file:///Users/jeanetienne/Library/Developer/CoreSimulator/Devices/D7BC2069-1D37-466E-BE36-04A8530AB99E/data/Containers/Data/Application/37A4C44D-BCD1-442D-924E-37694F0367BD/Library/Application%20Support/my_app_bundle_id/my_database.sqlite
CoreData: annotation: options:
CoreData: annotation: _NSBinaryStoreInsecureDecodingCompatibilityOption : 1
CoreData: annotation: NSSQLitePragmasOption : {
"journal_mode" = DELETE;
}
⚠️ [CoreStore: Error] DataStack+Migration.swift:449 upgradeStorageIfNeeded(_:metadata:completion:)
↪︎ Failed to migrate version model "V1" to version "V2".
(CoreStore.CoreStoreError) .internalError (
.errorDomain = "com.corestore.error";
.errorCode = 5;
.NSError = (
.domain = "NSCocoaErrorDomain";
.code = 134090;
.userInfo = 1 key-value(s) [
"NSSQLiteErrorDomain" = 5;
];
);
)
```
Surprisingly this error output make it look like the migration from `V1` to `V2` has a problem, when in fact this migration works fine when I comment out anything related to `V3` 🤔 ...
-
Thanks!
@jeanetienne I see. I think your data is simply too large and is hitting memory performance limits. Note that NSCocoaErrorDomain (134090) is NSPersistentStoreTimeoutError.
Unfortunately, Core Data's migration methods don't do well with large migrations like this since Core Data runs migrations in one transaction pass, which means the migrating NSManagedObjectContext will eat up large amounts of memory.
I had a project where I had to handle something like this. What I did was create a new empty DataStack for the new model (your V3), and manually move over chunks of data from the original DataStack (your V2). I don't think there's much we can do on CoreStore's side, since migration logic depends on how models need to be transformed to the new model.
@JohnEstropia commented on GitHub (Jun 25, 2021):
@jeanetienne I see. I think your data is simply too large and is hitting ~memory~ performance limits. Note that `NSCocoaErrorDomain (134090)` is `NSPersistentStoreTimeoutError`.
Unfortunately, Core Data's migration methods don't do well with large migrations like this since Core Data runs migrations in one transaction pass, which means the migrating `NSManagedObjectContext` will eat up large amounts of memory.
I had a project where I had to handle something like this. What I did was create a new empty `DataStack` for the new model (your `V3`), and manually move over chunks of data from the original `DataStack` (your `V2`). I don't think there's much we can do on CoreStore's side, since migration logic depends on how models need to be transformed to the new model.
That's surprising, I only have 11 tables in total, and when running this above example, no table had more than 40 records... The whole sqlite file is 78KB... Could this be caused by something else?
So there's nothing wrong from the setup or the way the schema/migrations are implemented?
@jeanetienne commented on GitHub (Jun 25, 2021):
Thanks @JohnEstropia for the reply.
That's surprising, I only have 11 tables in total, and when running this above example, no table had more than 40 records... The whole sqlite file is 78KB... Could this be caused by something else?
So there's nothing wrong from the setup or the way the schema/migrations are implemented?
The strings ("MyTable_01" etc) should stay the same, but I have never reused the same entity in multiple versions. I don't think that is supported.
In your case, even if MyEntity_01 didn't change across models try to create a new type for it for every new version.
Let me know how that works for you.
@JohnEstropia commented on GitHub (Jun 25, 2021):
I see, then it isn't a memory error.
I took a closer look at your models and it looks like you are reusing entities in multiple versions:
```swift
Entity<MyEntity_01>("MyTable_01"),
Entity<MyEntity_02>("MyTable_02"),
Entity<MyEntity_03>("MyTable_03"),
Entity<MyEntity_04>("MyTable_04"),
```
The strings (`"MyTable_01"` etc) should stay the same, but I have never reused the same entity in multiple versions. I don't think that is supported.
In your case, even if `MyEntity_01` didn't change across models try to create a new type for it for every new version.
Let me know how that works for you.
That was it. The migration took ~2.4s in the simulator once I reshuffled my schema by separating each entity versions so I don't reuse them between version.
Thanks for the help! 🙏
@jeanetienne commented on GitHub (Jun 25, 2021):
@JohnEstropia you were right on target 🎯
That was it. The migration took ~2.4s in the simulator once I reshuffled my schema by separating each entity versions so I don't reuse them between version.
Thanks for the help!
🙏
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Originally created by @jeanetienne on GitHub (Jun 25, 2021).
Hi John & Contributors, thanks for providing such a valuable tool, making CoreData much more palatable, high level and swifty!
I am using CoreStore 8.0.1 and I can't find a way to make a custom migration work.
It seems to hang for a veeery long time (7+ minutes in iOS Simulator), and I get an error:
When I only try to do a
V1toV2migration and I let CoreStore infer all the migration (deleting a table and deleting a column), it works fine. As soon as I add aV2toV3migration with theCustomSchemaMappingProvider, it exhibits this behaviour (hanging for 7 min and erroring).Let me know if you need more details. I would appreciate any help understanding this problem! Thank you!
(I renamed my entities and table names, but everything else is the same)
Setup Details
Project
Schema
Database setup
Simulator output
Surprisingly this error output make it look like the migration from
V1toV2has a problem, when in fact this migration works fine when I comment out anything related toV3🤔 ...Thanks!
@JohnEstropia commented on GitHub (Jun 25, 2021):
@jeanetienne I see. I think your data is simply too large and is hitting
memoryperformance limits. Note thatNSCocoaErrorDomain (134090)isNSPersistentStoreTimeoutError.Unfortunately, Core Data's migration methods don't do well with large migrations like this since Core Data runs migrations in one transaction pass, which means the migrating
NSManagedObjectContextwill eat up large amounts of memory.I had a project where I had to handle something like this. What I did was create a new empty
DataStackfor the new model (yourV3), and manually move over chunks of data from the originalDataStack(yourV2). I don't think there's much we can do on CoreStore's side, since migration logic depends on how models need to be transformed to the new model.@jeanetienne commented on GitHub (Jun 25, 2021):
Thanks @JohnEstropia for the reply.
That's surprising, I only have 11 tables in total, and when running this above example, no table had more than 40 records... The whole sqlite file is 78KB... Could this be caused by something else?
So there's nothing wrong from the setup or the way the schema/migrations are implemented?
@JohnEstropia commented on GitHub (Jun 25, 2021):
I see, then it isn't a memory error.
I took a closer look at your models and it looks like you are reusing entities in multiple versions:
The strings (
"MyTable_01"etc) should stay the same, but I have never reused the same entity in multiple versions. I don't think that is supported.In your case, even if
MyEntity_01didn't change across models try to create a new type for it for every new version.Let me know how that works for you.
@jeanetienne commented on GitHub (Jun 25, 2021):
Oh, interesting! I'll give that a try now. Thanks for the suggestion!
@jeanetienne commented on GitHub (Jun 25, 2021):
@JohnEstropia you were right on target 🎯
That was it. The migration took ~2.4s in the simulator once I reshuffled my schema by separating each entity versions so I don't reuse them between version.
Thanks for the help!
🙏
@JohnEstropia commented on GitHub (Jun 25, 2021):
That's great to hear!