Core Data threading fun
Core Data threading fun
- Subject: Core Data threading fun
- From: Luke Evans <email@hidden>
- Date: Mon, 21 Sep 2009 01:13:54 -0700
I have a server app that responds to network requests, making use of a
Core Data database to serve responses.
Some requests update the database. I have chosen to allow requests to
arrive on multiple threads, and intend for these threads to use Core
Data directly.
In keeping with Core Data's doc related to threading, I have one
Managed Object Context per thread, and these all share a common
Persistent Store Coordinator that is managing a SQLite data file.
It's my understanding that this scenario is an acceptable
configuration, indeed it appears in the Core Data notes on threading
as the 'preferred option' (q.v. http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/CoreData/Articles/cdMultiThreading.html
).
OK, here comes the problem (!)...
One type of request can change the name of an object. The handler for
this request (using its thread's own MOC):
1. Fetches the required object by persistent ID (i.e. stringified
NSManagedObjectID).
2. Changes the value of the 'name' property on the fetched object
3. Commits the change by calling 'save' on the MOC
Unfortunately, while this works some of the time, I have a situation
where a subsequent other request (possibly on another thread) sees the
old name of this object. This request gets 'all' the entities of this
type and sends properties (such as name) back.
I'm pretty sure my MOC handling per thread is good. I be checking
that, but lets assume this is the case.
I have a number of questions arising from both the Core Data docs, and
my own ignorance as to what exactly Core Data will be doing in a "MOC-
per-thread, shared coordinator" configuration.
1. Sharing the Coordinator
This seems to be 'encouraged' (or at least permitted as the 'preferred
option'). However, there are some passages in the docs that, while
not exactly contradictory, cause me to wonder what the 'best' thing to
do is:
...
A persistent store coordinator provides to its managed object contexts
the façade of one virtual store.
For completely concurrent operations you need a different coordinator
for each thread.
...
There are three patterns you can adopt to support multi-threading in a
Core Data application; in order of preference they are:
Create a separate managed object context for each thread and share a
single persistent store coordinator.
If you need to “pass” managed objects between threads, you just pass
their object IDs.
If you want to aggregate a number of operations in one context
together as if a virtual single transaction, you can lock the
persistent store coordinator to prevent other managed object contexts
using the persistent store coordinator over the scope of several
operations.
...
Should I have a PSC per thread too? If so, will they behave correctly
talking to the same SQLite data file?
With a shared PSC, should I lock this whenever a write is being
performed (at least)?
2. Locking the MOC
There is talk that locking the MOC, even one that is private to a
thread, will engender 'thread friendly' behaviour in the PSC:
...
Typically you lock the context or coordinator using tryLock or lock.
If you do this, the framework will ensure that what it does behind the
scenes is also thread-safe. For example, if you create one context per
thread, but all pointing to the same persistent store coordinator,
Core Data takes care of accessing the coordinator in a thread-safe way
(NSManagedObjectContext's lock and unlockmethods handle recursivity).
...
Should I lock the MOC, or just the PSC (see 1)?
Would this really fix my experience of changes not appearing on other
threads anyway?
3. Changes propagating back to other MOCs looking at the same data
I assume Core Data is smart enough to realise that an attribute value
change in a Managed Object cached in one thread's MOC, should be
reflected on (or at least invalidate old data in) another Managed
Object instance representing the same data in another MOC. At the
very least, I think I'd expect a new fetch request that has this
object in the result set would cause data changed in the persistent
store to show up properly on the object.
The best guess I have right now is that some kind of MOC-related
caching is causing problems. Both queries have separate MOCs, and do
fresh look-ups when a request comes in, yet (apparently) a
successfully saved change in one MOC (the method succeeds without
error) is not reflected in a different MOC. There is actually no
system pressure when I'm experiencing the problem at the moment, I'm
the only user although my serial requests are being handled by
different threads (ergo MOCs).
Is there anything that comes to mind that I'm apparently not doing,
which would be needed for states cached in different MOCs to be
properly synchronised?
Well, thanks if you're taken the time to read that little lot.
If you have any insights too I'd be obliged!
Cheers
-- Luke
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden