Datos básicos "La database aparece corrupta" – ¿Qué causa este error?

Estoy golpeando mi cabeza contra la panetworking aquí, estoy usando Core Data para un SQLLite DB, y puedo save con éxito en la database (he comprobado el contenido en un browser SQLLite sin connection), pero después de save el file La primera consulta que bash ejecutar vuelve con el error que se ve a continuación, y no puedo encontrar ninguna información útil en Internet relacionada con este error específico:

Datos básicos: error: -executeRequest: encountenetworking exception = La database aparece dañada. (key primaria no válida) con userInfo = {NSFilePath = "/ Users / user / Library / Application Support / iPhone Simulator / 7.0.3 / Documents / db.sqlite";

La pregunta aquí es qué causa este error, ya que no puedo encontrar ninguna información relacionada con él.

Para un poco de historia, esta es mi configuration, supongamos que tengo buenas razones para el layout que se ha realizado y no ofrezco una respuesta de "Cambiar su layout" a less que pueda ver algo fundamentalmente roto con el patrón en sí.

Tengo 3 contexts de objects gestionados, todos son NSPrivateQueueConcurrencyType, el primero (A) está adjunto al Coordinador de tienda persistente, el segundo (B) tiene A configurado como su context principal y el tercero (C) tiene B configurado como es context padre: una cadena. La razón de esto es que C es un context de escritura, que trae datos de una fuente de networking y lo sincroniza y lo guarda, B es un context compartido por los elementos de la interfaz de usuario, y quiero que sea receptivo, finalmente A es un context diseñado para download cualquier retraso para save en el disco apagado Context B y C

PSC <- A <- B <- C

Si elimino el último paso (Guardar A en PSC), la aplicación funcionará muy bien, manteniendo todo en la memory y consultando en contexts de memory. El locking solo se produce después de volver a agregar el paso de guardado, y solo en la primera consulta se ejecuta contra el DB después de ese guardado. Tanto la ejecución de my Save como la de mi búsqueda se envuelven en performBlock:

Aquí está el último guardado:

- (void)deepSave { // Save to the Save Context which happens in memory, so the actual write to disk operation occurs on background thread // Expects to be called with performBlock NSError *error = nil; [super save:&error]; NSAssert(!error, error.localizedDescription); // Trigger the save context to save to disk, operation will be queued and free up read only context NSManagedObjectContext *saveContext = self.parentContext; [saveContext performBlock:^{ NSError *error = nil; [saveContext save:&error]; NSAssert(!error, error.localizedDescription); }]; } 

Y aquí está la stack de ejecución (en el hilo de la queue NSManagedObjectContext)

 #0 0x0079588a in objc_exception_throw () #1 0x079d98e7 in -[NSSQLiteConnection handleCorruptedDB:] () #2 0x078d9b8d in -[NSSQLiteConnection fetchResultSet:usingFetchPlan:] () #3 0x078e24a5 in newFetchedRowsForFetchPlan_MT () #4 0x078cd48e in -[NSSQLCore newRowsForFetchPlan:] () #5 0x078cca8d in -[NSSQLCore objectsForFetchRequest:inContext:] () #6 0x078cc53f in -[NSSQLCore executeRequest:withContext:error:] () #7 0x078cbf62 in -[NSPersistentStoreCoordinator executeRequest:withContext:error:] () #8 0x078c96c6 in -[NSManagedObjectContext executeFetchRequest:error:] () #9 0x0791e526 in -[NSManagedObjectContext(_NestedContextSupport) _parentObjectsForFetchRequest:inContext:error:] () #10 0x0799c1f4 in __82-[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:]_block_invoke () #11 0x0791e321 in internalBlockToNSManagedObjectContextPerform () #12 0x013c34b0 in _dispatch_client_callout () #13 0x013b0778 in _dispatch_barrier_sync_f_invoke () #14 0x013b0422 in dispatch_barrier_sync_f () #15 0x0791e2a2 in _perform () #16 0x0791e14e in -[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:] () #17 0x078c96c6 in -[NSManagedObjectContext executeFetchRequest:error:] () #18 0x0791e526 in -[NSManagedObjectContext(_NestedContextSupport) _parentObjectsForFetchRequest:inContext:error:] () #19 0x0799c1f4 in __82-[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:]_block_invoke () #20 0x0791e321 in internalBlockToNSManagedObjectContextPerform () #21 0x013c34b0 in _dispatch_client_callout () #22 0x013b0778 in _dispatch_barrier_sync_f_invoke () #23 0x013b0422 in dispatch_barrier_sync_f () #24 0x0791e2a2 in _perform () #25 0x0791e14e in -[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:] () #26 0x078c96c6 in -[NSManagedObjectContext executeFetchRequest:error:] () 

De acuerdo, lo he rastreado. Parece que hay algo roto en propertiesToFetch contra NSManagedObject resultType (se supone que no debe usarse, nuestro error) en esta configuration – contra un context que tiene un context padre en lugar de un coordinador persistente. Esta testing de unidad muestra que todo lo que tiene que hacer es establecer una propiedad para search para get este error (mientras hace la consulta sin las properties para search funciona correctamente). La solución aquí para nosotros fue detener incorrectamente el uso de properties para get 🙂

 - (void)testManagedObjectContextDefect { NSManagedObjectContext *contextA = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; contextA.persistentStoreCoordinator = sqllitePersistentStoreCoordinator; NSManagedObjectContext *contextB = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; contextB.parentContext = contextA; NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"GCSCObject" inManagedObjectContext:contextB]; [contextB performBlockAndWait:^{ GCSCObject *object = [[GCSCObject alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:contextB]; object.serverID = @"1"; NSError *error = nil; XCTAssert([contextB save:&error] && !error, @"Failed to save - %@",error); // B -> A save }]; [contextA performBlock:^{ NSError *error = nil; XCTAssert([contextA save:&error] && !error, @"Failed to save - %@",error); // A -> PSC, background save }]; [contextB performBlockAndWait:^{ NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"GCSCObject"]; NSError *error = nil; NSArray *results = [contextB executeFetchRequest:request error:&error]; XCTAssert(results.count == 1 && !error, @"Fetch failed to retrieve - %@ / %@",results,error); GCSCObject *object = results[0]; XCTAssert([object.serverID isEqualToString:@"1"], @"Value retrieval failed"); // Everything passes up to here, so far so good! request = [[NSFetchRequest alloc] initWithEntityName:@"GCSCObject"]; request.propertiesToFetch = @[@"serverID"]; // This is the culprit of the index crash results = [contextB executeFetchRequest:request error:&error]; XCTAssert(!error, @"%@", error.localizedDescription); // !!! HERE we have a failure, assert: "Core Data: error: -executeRequest: encountenetworking exception = The database appears corrupt. (invalid primary key) with userInfo = { NSFilePath = "/path/db.sqlite }"; }]; } 

En este caso, GCSCObject es una entidad normal, y el ID de server es uno de sus parameters (no importa qué parámetro se usa o qué tipo es, lo intenté con varios. Aquí está la descripción del parámetro serverID que utilicé para esto testing:

descripción de serverID

El locking se produce independientemente de que proporcionemos el command andWait to contextA save (aunque hacerlo anularía el punto de tener una queue de background para save)

Me encantaría recibir comentarios sobre por qué este podría ser el caso, pero por ahora, el no usar properties para search permite que nuestra aplicación funcione sin problemas. Estoy contemplando archivar un Apple Bug aquí.

Si experimenta este error al realizar una request de recuperación que está recuperando algún resultado agregado (sum, máx., Mín., …), asegúrese de establecer

 fetchRequest.resultType = NSDictionaryResultType; 

Primero, su context de interfaz de usuario no debe ser un context de queue privada. Para eso es NSMainQueueConcurrencyType .

Segundo, no verifique el error en save. Compruebe el retorno de BOOL desde el -save: Ese error puede tener basura en él incluso en un salvado exitoso.

Tercero, ¿cómo se ven tus otras dos guardadas? Si todos están en queues privadas y se guardan asíncrono, es posible que se esté ejecutando una condición de carrera. C debería save sincrónicamente, B debería save sincrónicamente y A debería ser asíncrono.