Core Data Single Managed Object Context and two threads - core-data

I am going crazy trying to figure this out. I am working on an application that is syncing up data from the webserver. There is a background thread that is pulling data from the server to the application. At the same time I am making changes to the UI. The values changed on UI are being saved to core data in foreground.
Through out the application I have one managedObjectContext that I fetch from the app delegate every time I create a fetchController . App delegate code
- (NSManagedObjectContext *)managedObjectContext
{
if (__managedObjectContext != nil) {
return __managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
__managedObjectContext = [[NSManagedObjectContext alloc] init];
[__managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return __managedObjectContext;
}
Now the problem is I am getting error while trying to save the context. The errors are happening randomly in the code. I am saving the context as soon as I am making change to any entity. Also I have two relationships each in each entity one to its child that is one to many and one to its parents that is to - one. All relationship have appropriate inverse.
I think I am doing something conceptually wrong over here by mentaining one context. Could you please advice how I should manage context in a situation where both background and foreground threads are reading and writing to the coredata. Thanks.

Managed object contexts are not thread safe, so if you use the same one on more than one thread without considering concurrency-- you're going to have major problems. As in, crashing and/or data loss and maybe even data corruption. There are a couple of ways to deal with this:
Use one of the queue concurrency types when creating the context-- see docs for initWithConcurrencyType:. Then, whenever you access the data store, use either performBlock: or performBlockAndWait: to synchronize access.
Create a new managed object context for the background thread. Use NSManagedObjectContextDidSaveNotification and mergeChangesFromContextDidSaveNotification: to keep multiple contexts synchronized.
But whatever you do, do not just use one managed object context on more than one thread.

Related

Do I understand these concepts correctly?

In most of my interviews, I've been asked about web services and multithreading. I've done neither, so I decided to learn more about Web Services and Multithreading using Grand Central Dispatch.
For web services, the way that I understand it is that you need to fetch the data using a class such as NSURLConnection. basically setup a new NSURL, then a connection, then a request. You also need to make use of the API's methods such as didConnect, didReceiveData, and didFailLoadWithError. After you receive the data, which is generally in JSON or XML format and stored as an NSData object, you can store it and parse through it. There are multiple ways to parse through it, such as by using SBJSON or NSXMLParser. You can then do with it what you need.
For multithreading, Grand Central Dispatch is a c-style way of multithreading. Basically, you use it when you need to do heavy hauling away from the main thread to avoid the app freezing. You can dispatch synchronously or asynchronously. Asynchronously means that the method on the main thread will continue executing, synchronously means that it will not. You never need to use GCD alongside with NSURLConnection, because NSURLConnection already does its work in the background then calls upon delegates in the main thread. But, for saving and unzipping files, you should use GCD. When you call dispatch_async, you pass in a dispatch queue. You can use either a serial queue or a concurrent queue. A serial queue will execute tasks in the queue one at a time, in the order that they arrived. It is the default setting. With concurrently queues, tasks executed concurrently might be executed at the same time.
My first question is, do I have a proper understanding of these two concepts? I know that there is a lot to learn about GCD, but I just want to make sure that I have the basic ideas correct. Also, with GCD, why would someone ever want to dispatch synchronously, wouldn't that defeat the purpose of multithreading?
The only reason to dispatch synchronously is to prevent the current code from continuing until the critical section finishes.
For example, if you wanted to get some value from the shared resource and use it right away, you would need to dispatch synchronously. If the current code does not need to wait for the critical section to complete, or if it can simply submit additional follow-up tasks to the same serial queue, submitting asynchronously is generally preferred.
You can make synchronous request and dispatch it by using dispatch_async or dispatch_sync call. It will totally run in background.
-(void)requestSomething:(NSString *)url
{
NSString *queue_id = #"queue_identifier";
dispatch_queue_t queue = dispatch_queue_create([queue_id UTF8String], 0);
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
NSError *serviceError = nil;
NSURLResponse *serviceResponse = nil;
NSData *dataResponse = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:&serviceResponse error:&serviceError];
if(serviceError)
{
dispatch_sync(main, ^{
// Do UI work like removing indicator or show user an alert with description of error using serviceError object.
return;
});
}
else
{
// Use dataResponse object and parse it as this part of code will not executed on main thread.
dispatch_sync(main, ^{
// Do UI work like updating table-view or labels using parsed data or removing indicator
});
}
});
// If your project is not developed under ARC mechanism, add following line
dispatch_release(queue);
}

Can I use NSManagedObjectContext with NSPrivateQueueConcurrencyType in a concurrent GCD queue

My approach so far has been something like this:
1- A main context initialized like so:
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.model];
if(![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
DDLogModel(#"Unresolved error %#", error.localizedDescription);
return;
}
self.context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
self.context.persistentStoreCoordinator =_persistentStoreCoordinator;
2- Then, as I go about creating core data objects or modifying their relationships concurrently:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSManagedObjectContext *tempContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
tempContext.persistentStoreCoordinator = self.persistentStoreCoordinator;
// Do stuff
[tempContext save:nil];
});
3- And finally, the main context merge via the NSManagedObjectContextDidSaveNotification
However, I've recently seen a different approach where for step 2, they instead create a context with NSPrivateQueueConcurrencyType, make it a child of the main context, and do any work by means of -performBlock:
Is this last approach concurrent by default (even without explicitly dispatching it as such)? Or what's the advantage over the approach I explained?
Another thing that threw me off is that even though the contexts have parentContext and persistentStoreCoordinator properties, it appears that setting the latter implies one cannot set the former. That is, a context with a persistent store coordinator actually has that store coordinator as it's parent context?
UPDATE:
Another interesting thing is that with the approach I described above (using GCD), everynow and then when I do [tempContext save:] I get a weird behaviour: No error is returned (assuming I do pass in an NSError object, unlike in the example), but if I set the generic Objective-C exception pointer on, the background thread does stop there, as if there was an exception. However, if I continue the app does not crash and keeps going and the main moc seems to be just fine.
You are right. performBlock will automatically perform the work in the background. In some cases it might make sense to use the current thread (main or background), in which case you can use performBlockAndWait. Using child contexts is the recommended approach.
I suppose your setup could work just as well. I guess the advantage of using a child context lies in a more structured approach of saving, i.e. "pushing up" saves into the parent context. Only the parent context will actually touch the persistent store, so this is better in terms of thread safety.
Your last question is not clear. A context can only have a context as its parent context, not a persistent store coordinator. However, what might be confusing you is that prior to iOS 5 there was only a "Parent Store", and with the introduction of child contexts it could be replaced optionally by a parent context. Read all about it here.

Best NHibernate multithreading pattern?

As we know, NHibernate sessions are not thread safe. But we have a code path split in several long running threads, all using objects loaded in the initial thread.
using (var session = factory.OpenSession())
{
var parent = session.Get<T>(parentId);
DoSthWithParent(session, parent);
foreach (var child in parent.children)
{
parallelThreadMethodLongRunning.BeginInvoke(session, child);
//[Thread #1] DoSthWithChild(child #1) -> SaveOrUpdate(child #1) + Flush()
//[Thread #2] DoSthWithChild(child #2) -> SaveOrUpdate(child #2) + Flush()
//[Thread #3] DoSthWithChild(child #3) -> SaveOrUpdate(child #3) + Flush()
// -> etc... changes to be persisted immediately, not all at the end.
EndInvoke();
}
DoFinalChangesOnParentAndChildren(parent);
session.Flush();
}
}
One way would be a session for each thread, but that would require the parent object to be reloaded in each. Plus, the final method is also doing changes on the children and would run in a StaleObjectException if another session changed it meanwhile, or had to be evicted/reloaded.
So all threads have to use the same session. What is the best way to do this?
Use save queue in initial thread (thread safe implementation), which is polled in a loop (instead of EndInvoke()) from the main thread. Child threads can insert NHibernate objects to be saved by the main thread.
Use some callback mechanism to save/flush objects in main thread. Is there something similar possible to UI thread callback in WPF, Control.Invoke() or BackgroundWorker?
Put Save/Flush accesses into lock(session) blocks? Maybe dangerous, because modifying the NHibernate objects might change the session, even if not doing a Save()/Flush().
Or should I live with the database overhead to load the same objects for separate sessions in each thread, evict and reload them in the main thread and then do changes again? [edit: bad "solution" due to object concurrency/risk of stale objects]
Consider also that the application has a business logic layer above NHibernate, which has similar objects, but sends it's property values to the NHibernate objects on it's own Save() command, only then modifying them and doing NHibernate Save()/Flush() immediately.
Edit:
It's important that any read operation on NHibernate objects may change the session - lazy loading, chilren collection change under certain conditions. So it is really better to have a business object layer on top, which synchronizes all access to NHibernate objects. Considering the database operations take only a minimum time of the threads (mainly occasional status settings), and most is for calculations, watching, web service access and similar, the performance loss by data layer synchronization is negligible.
Firstly, if I understand correctly, different threads may be updating the same objects. In that case, nHibernate or not, you're performing several updates on the same objects concurrently, which may lead to unexpected results.
You may want to tweak your design a bit to ensure that an object can be only updated by (at most) a single thread.
Now, assuming your flow may include having the same threads reading the same data (but writing different data), I'd suggest using different sessions- one per thread, and utilizing 2nd level cache;
2nd level cache is kept at the SessionFactory (rather than in the Session) level, and is therefore shared by all session instances.
The session object is not thread safe, you can't use it over different threads. The SaveOrUpdate in your sepperate threads will most likely crash your program or corrupt your database. However what about creating the data set you want to update and do the SaveOrUpdate actions in your main thread (were your session is created)?
You should observe the following practices when creating NHibernate
Sessions: • Never create more than one concurrent ISession or
ITransaction instance per database connection.
• Be extremely careful when creating more than one ISession per
database per transaction. The ISession itself keeps track of updates
made to loaded objects, so a different ISession might see stale data.
• The ISession is not threadsafe! Never access the same ISession in
two concurrent threads. An ISession is usually only a single
unit-of-work!

Writing to Core Data from a secondary thread

I have an HTTP call to a URL, which returns my data in JSON format, I parse it, then I have to load it in my Core Data context.
Now I am doing it (parsing - entities creation - commit) on the main thread, by using GCD (grand central dispatch) to dispatch a block on the main queue.
The http call is asynchronous, so it's ok, but the db loading is not, so it freezes my user interface: a UITableView backed by a NSFetchedResultsController.
What I'd like to do, is making these last tasks on a secondary thread, but don't know how!
I heard something about creating a second context, using that on the secondary thread, then trash it and "refresh" the "main" context, don't know how to explain.
Maybe is there a wwdc ed. video on this argument, too? I can't find valid documentation.
Can you help me, loading data in asynchronous way, so my table never stop scrolling?
There is a rule: One context for one thread. Create new context in your not main queue and work with it.
Add observer for this context:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(<#Selector name#>)
name:NSManagedObjectContextDidSaveNotification
object:<#A managed object context#>];
After your parser is done and objects in context, save that context which will kick out notification. In main queue, catch this notification and on main queue context call - (void)mergeChangesFromContextDidSaveNotification:(NSNotification *)notification.

NSFetchedResultsController performFetch on background thread

I have to perform a fetch via NSFetchedResultsController on a background thread.
My current solution is structured like that:
dispatch_queue_t fetchQueue = dispatch_queue_create("backgroundfetching", NULL);
dispatch_async(fetchQueue,^{
// 1. Create NSManagedObjectContext
// 2. Create NSFetchRequest
// 3. Create NSFetchedResultsController
// 4. PerformFetch
dispatch_async(dispatch_get_main_queue(),^{
[[self table] reloadData];
});
});
dispatch_release(fetchQueue);
My first tests ran well but is that the appropriate way?
Since the fetched results controller is intended to control the data that defines a tableview, it belongs on the foreground thread/operation that the UI runs on. It's rather pointless to put it on a background thread as you would lose all the advantages of using it in the first place.
I would also be concerned about the effects of sending the FRC delegate messages across asynchronous threads. I'm not sure how reliable that would be.
Having said all that, the sketch of your implementation looks fine as far as it goes.
I believe there is something fundamentally wrong with this approach, as you're sharing managed objects across threads (you're fetching objects on one thread and referencing them on your main thread). In practice it will work, but will sometimes lead to crashes. Because Apple makes it clear that the only ways to share managed objects across threads is using the objectWithID: method or the MOCDidSave notifications.
From the Core Data Programming Guide:
You fetch in one managed object context on a background thread, and
pass the object IDs of the fetched objects to another thread. In the
second thread (typically the application's main thread, so that you
can then display the results), you use the second context to fault in
objects with those object IDs (you use objectWithID: to instantiate
the object).

Resources