2

私のアプリケーションには競合状態があり、複数の API リクエストがまったく同じデータを返し、それらを保存しようとする可能性があります。モデルに validateForInsert を追加することで、これが起こらないようにしたいと考えています。検証の前提は、このように識別子キーが既に存在するかどうかを確認するだけです

- (BOOL)validateForInsert:(NSError *__autoreleasing *)error
{
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([CWDeal class])];
    fetchRequest.predicate = [NSPredicate predicateWithFormat:@"identifier == %@", self.identifier];
    NSError *validateError;
    int count = [[(CWAppDelegate *)[[UIApplication sharedApplication] delegate] privatewriterManagedObjectContext] countForFetchRequest:fetchRequest error:&validateError];
    if (count > 0) {
        return FALSE;
    }
    return [super validateForInsert:error];
}

問題は、何も保存されないことです。privateWriterManagedObjectContext(PSC に接続) の親を持つ managedObjectContext(メイン スレッド) があります。ものをインポートするときは、managedObjectContext の親を持つ importContext (いくつかのバックグラウンド スレッド) を作成します。新しいデータを取得してフローを保存しようとすると、次のようになります。

(検証は、オブジェクトの privateWriterMOC をチェックしていることを思い出してください)

importContext でオブジェクトを作成 -> 保存 -> 検証 -> わかりました。

(データは importContext の親である managedObjectContext にプッシュされます)。

managedObjectedContext を保存 -> 検証 -> わかりました。

(データは managedObjectContext の親である privateWriterMOC にプッシュされます)。

privateWriterMOC を保存 -> 検証 -> 失敗。privateWriterMOC は、オブジェクトがそのコンテキスト上にあることを認識し、それらを保存しません。

validateForInsert の使用に関するドキュメントはあまりないように思われるので、これを行う方法について誰かが提案してくれることを願っていますか?

4

1 に答える 1

1

これが私が本番環境に行ったコードです:

- (BOOL)validateForInsert:(NSError *__autoreleasing *)error {
    [[(AppDelegate *)[[UIApplication sharedApplication] delegate] persistentStoreCoordinator] lock];
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([Deal class])];
    fetchRequest.predicate = [NSPredicate predicateWithFormat:@"identifier == %@", self.identifier];
    NSError *validateError;
    int count = [[(AppDelegate *)[[UIApplication sharedApplication] delegate] validationContext] countForFetchRequest:fetchRequest error:&validateError];
    if (count > 0) {
        [[(AppDelegate *)[[UIApplication sharedApplication] delegate] persistentStoreCoordinator] unlock];
        return FALSE;
    }
    [[(CWAppDelegate *)[[UIApplication sharedApplication] delegate] persistentStoreCoordinator] unlock];
    return [super validateForInsert:error];
}

PSC をロックすることが、この作業の鍵でした。異なるコンテキストが同時に PSC に到達しようとする前に、多くのデッドロックが発生していました。このアプローチで私が見つけた唯一の欠点は、1 つのオブジェクトが false を返すと、コンテキスト全体が無効とマークされ、保存されないことです。多くの良いオブジェクトがあり、保存されていないコンテキストに 1 つの悪いオブジェクトがあるとします。その 1 つの悪いオブジェクトでは、良いオブジェクトを保存できません。テスト中に問題が発生しましたが、本番環境では問題は発生していません。


質問を書いてテストしたときに思いついた回答を投稿します。これが正しい考えかどうかはわかりませんが、現時点では重複を防いでいるようです。

私がしたことは、appDelegate に validationContext という別のコンテキストを作成し、privateWriterMOC とまったく同じように設定することでした。基本的には、ストアに接続されているだけで、認識しているデータのみが既に書き込まれているという考え方です。validateForInsert が呼び出されたときに、validationContext を使用してフェッチを実行すると、そのオブジェクトが既に保存されているかどうかが通知されます。

競合状態はまだ発生する可能性があると思います (書き込みとフェッチが同時に発生し、フェッチが最初に返されると仮定します) が、さらに詳しく調べます (おそらく sqlite3 データベースはアトミックですか?)。競合状態は非常にまれなケースですが、念のため処理したかっただけです。

于 2012-12-20T22:47:32.323 に答える