2013-04-04 8 views
5

Ich implementiere eine benutzerdefinierte NSIncrementalStore Unterklasse, die eine relationale Datenbank für persistenten Speicher verwendet. Eines der Dinge, mit denen ich immer noch zu kämpfen habe, ist die Unterstützung für optimistisches Sperren.Unterstützung für optimistisches Sperren in der Unterklasse NSIncrementalStore


(fühlen sich frei, diese lange Beschreibung Recht auf meine Frage unten überspringen) durch

ich analysiert, wie Core Data die SQLite inkrementelle Speicher nähert sich diesem Problem erzeugt durch die Untersuchung SQL-Protokolle und kam mit folgenden Ergebnissen:

  • Jede Entität Tabelle in der Datenbank hat eine Z_OPT c olumn, die angibt, wie oft eine bestimmte Instanz dieser Entität (Zeile) geändert wurde, beginnend mit 1 (anfängliche Einfügung).

  • Jedes Mal, wenn ein verwaltetes Objekt geändert wird, wird der Wert Z_OPT in der entsprechenden Datenbankzeile inkrementiert.

  • Der Speicher hält Cache (bezeichnet als Reihe Cache in Core Data-Dokumente) von NSIncrementalStoreNode Instanzen, die jeweils durch vorherige SELECT eine Version Eigenschaft gleich Z_OPT Wert zurückgekehrt oder UPDATE SQL-Abfrage in der Zeile des verwalteten Objekts.

  • Wenn ein verwaltetes Objekt aus NSManagedObjectContext (beispielsweise durch Ausführen von NSFetchRequest darauf) zurückgegeben wird, erstellt MOC snapshot dieses Objekt, das diese Version Nummer enthält.

  • Wenn das Objekt geändert oder gelöscht wird, stellt Core Data sicher, dass es nicht außerhalb des Kontexts geändert oder gelöscht wurde, indem es Versionen zwischengespeicherter Zeilen und Objekt-Snapshots vergleicht. All dies geschieht, wenn -save: für den Kontext aufgerufen wird, zu dem das Objekt gehört. Wenn die Versionen unterschiedlich sind, wird ein Zusammenführungskonflikt erkannt und basierend auf der Richtlinie zum Zusammenführen von Zusammenfassungen behandelt.

Wenn MOC gespeichert wird, die -newValuesForObjectWithID: withContext: Fehler: Verfahren wird für jeden geänderten/gelöschten Objekt aufgerufen, die wiederum kehrt NSIncrementalStoreNode mit der Versionsnummer. Diese Version wird dann mit der Snapshot-Version verglichen, und wenn sie unterschiedlich sind, schlägt das Speichern mit entsprechenden Zusammenführungskonflikten fehl (zumindest mit der standardmäßigen Zusammenführungsrichtlinie).

Dieser einfache Anwendungsfall funktioniert mit meinem Speicher seit -newValuesForObjectWithID: withContext: Fehler: prüft die Zeile Cache zuerst die ausreichend ist, wenn das Objekt gleichzeitig in anderem Kontext mit der gleichen Speicherinstanz geändert wurde. Wenn dies der Fall ist, enthält der Cache eine aktualisierte Zeile mit einer höheren Versionsnummer, die ausreicht, um einen Konflikt zu erkennen.

Aber wie kann ich erkennen, dass die zugrunde liegende Datenbank außerhalb meines Geschäfts geändert wurde, möglicherweise von einer anderen Anwendung oder einer anderen Geschäftsinstanz, die die gleiche Datenbankdatei verwendet? Ich weiß, dass dies ein selten auftretender Randfall ist, aber Core Data behandelt es richtig und ich würde es vorziehen, dasselbe zu tun.

Core-Data Store verwendet SQL-Abfragen, wie diese zu aktualisieren/löschen Zeile des Objekts:

UPDATE ZFOO SET Z_OPT=Y, (...) WHERE (...) AND Z_OPT=X 
DELETE FROM ZFOO WHERE (...) AND Z_OPT=X 

Wo:
X - Versionsnummer zuletzt in den Laden bekannt (aus dem Cache)
Y - Neue Versionsnummer

Wenn eine solche Abfrage fehlschlägt (keine betroffenen Zeilen), wird die Zeile im Cache des Speichers aktualisiert und die Version mit der zuvor zwischengespeicherten Version verglichen.


Meine Frage ist: Wie kann eine benutzerdefinierte NSIncrementalStore informieren Core Data, dass das optimistische Sperren Ausfall für einige aktualisiert/gelöscht/gesperrten Objekte aufgetreten ist? Es ist nur der Speicher, der in der Lage ist zu sagen, dass, wenn es behandelt NSSaveChangesRequest übergibt es -executeRequest: withContext: error: Methode.

Wenn die zugrunde liegende Datenbank unter dem Laden nicht ändert, dann sind Konflikte erkannt, da Core Data ruft -newValuesForObjectWithID: withContext: Fehler: auf jeden geändert werden/gelöscht/gesperrt Objekt vor der Ausführung speichern Änderungen auf dem Speicher fordern. Ich konnte keinen Weg für NSIncrementalStore finden, um Core Data zu informieren, dass ein optimistischer Sperrfehler aufgetreten ist nach begann es mit der Speicheranforderung umzugehen. Gibt es eine undokumentierte Möglichkeit, das zu tun? Core Data scheint einige Ausnahme in diesem Fall zu werfen, die dann magisch in fehlgeschlagene Speicheranforderung mit NSError übersetzt wird, die alle Konflikte auflistet. Ich kann das nur teilweise nachahmen, indem ich null von -executeRequest: withContext: error: zurückmelde und die Fehlermeldung selbst kreiere. Ich denke, dass es auch in diesem Szenario eine Möglichkeit geben muss, den standardmäßigen Kerndaten-Konfliktbehandlungsmechanismus zu verwenden.

+0

Ich bin nicht wirklich darüber nachgedacht, wie genau das im CoreData-Framework implementiert ist. Aber ich weiß, wenn CoreData einen Merge-Konflikt meldet, besteht auch die Möglichkeit, dass der Merge-Konflikt das persistentSnapshot-Objekt enthält (das Objekt, wie es jetzt im Store existiert). Dies kann passieren, wenn der Koordinator die gespeicherten Daten in den Speicher schreibt und diese verändert findet. [siehe hier] (https://developer.apple.com/library/mac/#documentation/CoreData/Reference/NSMergeConflict_Class/Reference/Reference.html) –

+0

@DanShelly: Vielen Dank für Ihren Kommentar. Sie haben völlig Recht, und das ist es, was ich in meiner inkrementellen Store-Implementierung zu erreichen versuche. Ich kann Zusammenführungskonflikte zwischen dem verwalteten Objektkontext und dem Zeilencache des persistenten Speichers erkennen und erzeugen (Fall 1 im Abschnitt "Überblick" des verknüpften Dokuments), aber nicht zwischen dem Zeilencache und dem externen Speicher (in meinem Fall rel. Datenbank). siehe Fall 2). Ich kann einen solchen Konflikt genau wie CoreDatas SQLite-Speicher erkennen, aber ich weiß nicht, wie ich diesen Konflikt an MOC melden soll, der gespeichert wird. –

Antwort

1

Mir ist klar, dass dies nicht eine Antwort für Sie in Frage, aber ich werde versuchen, Ihnen meine Sicht auf Coredata und Korrelation zu Datenbanken:

(1. Level-Cache)
NSPesistentStoreCoordinator + NSPersistentStore = = Eine einzige Verbindung zur Datenbank

(2-Level-Cache)
NSManagedObjectContext == Cache über die Verbindung halten, ändert sich

Also, meine zu verstehen Ihr Problem besteht darin, dass Sie mehrere Verbindungen zu Ihrem Geschäft haben, von denen jede Änderungen vornimmt, Sie jedoch keine zentrale Versionskontrolle über Ihre Datensätze haben. Ihr Geschäft wird -executeRequest:withContext:error: mit
Sie werden dann dafür verantwortlich sein zu überprüfen, dass die Datensatzversionen übereinstimmen, wenn Sie einen Konflikt in der Verbindungsebene (Stufe 1) finden, melden Sie Versionskonflikt zwischen dem Kontext (Ebene 2) und der Koordinator.
Sie müssen Version missmatch zwischen Ihrer Verbindung (Ebene 1) und Ihrem Geschäft melden.
Um dies zu tun, muss Ihr Geschäft Änderungen über alle Verbindungen zu ihm melden (ConnectionManager), oder es kann Hooks für Änderungen bieten, die daran vorgenommen werden.
Ich bin kein SQLite-Experte, aber der SQLite API tut etwas in diesem Bereich zu bieten hat:
update hook
commit hook
changes
total changes
(Ich habe keine Erfahrung, um diese Art von Haken bei der Einrichtung, aber wenn CoreData verwendet sie, es wird nicht in den Debug - Protokollen angezeigt)

Sie können diese Fehler melden, indem Sie den Fehlerzeiger (NSError **) setzen und die internen Daten so einstellen, dass sie mit denen übereinstimmen, die der CoreData - Koordinator erstellt (Merge - Konflikt erstellen und setze die Informationen in sie wie benötigt)

Beachten Sie, dass ein optimistischer Sperrfehler nur während -executeRequest:withContext:error: auftritt (es sei denn, Sie haben eine nicht autorisierte Verbindung zum Speicher, die vom Manager nicht überwacht wird.
Um dieses Verhalten zu Ihrem Manager müssen möglicherweise unterstützen jeden Datensatz, um zu überprüfen, wie es für einen [enorme Leistungskosten] sparen begangen wird, oder einige Haken in die Änderungen an Datensätzen gemacht verwenden kürzlich )

Um mehrere Verbindungen zu Ihrem Griff speichern Sie die Shop-uRL einen gemeinsamen Cache von NSIncrementalStoreNode, verkeilt haben könnte müssen:
statische @ {
url1: actualCacheMapping1,
url2: actualCacheMapping2,
...
}
jede Verbindung speichern, um die Store wird gegen verifiziert werden der tatsächliche Cache des Speichers url.

Hoffe, dass dies einen Sinn für Sie machen.

+0

Vielen Dank für Ihre Meinung. Während ich im Allgemeinen Ihren Annahmen zustimme, bin ich nicht sicher über den 1. Level-Cache, den Sie erwähnen. Aus den Tests, die ich durchgeführt habe, gehe ich zu dem Schluss, dass es in Core Data keine Schicht gibt, die mehrere Verbindungen zu denselben SQLite-Datenbanken verwaltet und überwacht. Jeder Datensatz wird auf gleichzeitige Änderungen außerhalb des Geschäfts überprüft, indem einfach sein Z_OPT-Wert in der Datenbank mit dem Wert verglichen wird, der dem Speicher bekannt ist (siehe Beispielaktualisierungs-/Löschanweisungen, die ich gepostet habe). Ich hatte gehofft, dass es möglich ist zu vermeiden, den NSError manuell zu füllen (wie im Fall # 1), aber ich werde es versuchen, danke. –

+0

Sie hatten recht mit dem 1st Level Cache (keinen Beweis dafür gefunden). Ich habe meine Antwort bearbeitet. hoffe das hilft :) –

+0

Danke, deine Antwort war in der Tat hilfreich. Jetzt gebe ich NSError mit manuell erstellten NSMergeConflict-s zurück und es funktioniert wie ein Zauber :) –

1

My question is: how can a custom NSIncrementalStore inform Core Data that optimistic locking failure has occurred for some updated/deleted/locked objects? It is only the store that is able to tell that when it handles NSSaveChangesRequest passed to it its -executeRequest:withContext:error: method.

In einer NSIncrementalStore, NSIncrementalStoreNode s repräsentieren die Speicher-Snapshots. Die version-Eigenschaft des Knotens ist das optimistische Sperrgrundelement. Der persistente Speicher ist für das Erkennen optimistischer Sperrfehler auf der Speicherebene verantwortlich, während der Kontext des verwalteten Objekts diese höher erkennen kann. Ein optimistischer Sperrfehler auf Filialebene kann auftreten, wenn das System, mit dem der Speicher kommuniziert, durch etwas anderes geändert wurde, und es einen Konflikt zwischen dem Status dieses Systems und dieser Zustandsdarstellung im permanenten Speicher gibt. Beispiel: Wenn der Informationsspeicher mit einem Webdienst kommuniziert und die Daten des Webdiensts von einem anderen Benutzer geändert wurden, usw.

Wenn während der Speicherung in Ihrer Geschäftsimplementierung ein optimistischer Sperrfehler festgestellt wird, ist Ihr Geschäft für die Erstellung verantwortlich NSMergeConflict Objekte beschreiben es. Diese werden von der NSPersistentStoreCoordinator propagiert.

Snapshot-Wörterbücher sollten alle modellierten Attributeigenschaftsnamen zusammen mit ihren Werten als Schlüssel enthalten. Dies beinhaltet keine Beziehungen. Für einige Filialen kann es ausreichen, die Werte der Referenzobjekte oder NSIncrementalStoreNodes zu verwenden, sofern sie nur den Namen der modellierten Attributeigenschaft als Schlüssel enthalten (und diese können leicht aus der Entitätsbeschreibung abgerufen werden).

Sobald diese Objekte erstellt wurden, erstellen Sie NSError in NSCocoaErrorDomain mit dem Code NSPersistentStoreSaveConflictsError. Das userInfo-Objekt sollte den Schlüssel NSPersistentStoreSaveConflictsErrorKey enthalten, der ein Array der Objekte NSMergeConflict enthalten sollte. Geben Sie das aus der Speicheranforderung zurück, und NSPersistentStoreCoordinator wird für das Auffinden der Lösung verantwortlich sein. Denken Sie daran, dass Sie keine Konflikten zwischen dem Zustand der Objekte in NSManagedObjectContext und Ihrem Geschäft erzeugen sollten, sondern nur bei Konflikten zwischen dem im Arbeitsspeicher oder zwischengespeicherten Zustand in Ihrem Geschäft und wo immer die Daten aufbewahrt oder beibehalten werden (wie ein Web-Service) oder Datenbank, etc.)

Verwandte Themen