db4o: Single Object-Container Concurrency
Today nearly every application has some concurrent parts. In a classic desktop-application some work is done in the background to keep the application responsive. In a web-application more than one request are handled concurrently. There’s no escape from the challenges of concurrent programs.
(All posts of this series: the basics, activation, object-identity, transactions, persistent classes, single container concurrency, Queries in Java, C# 2.0, client-server concurrency, transparent persistence, adhoc query tools)
Is a Object Container Thread Safe?
Short answer: Yes it is. You can throw as many threads as you like at a IObjectContainer. A big fat lock protects all operations on the IObjectContainer. It even goes further. You even can grab that lock yourself, but it’s not recommended!
But Your Persistent-Classes Aren’t!
So now we know that the object-container is thread safe. Hurray, so lets throw a quazillion threads at it =). Again a little demonstration. Since you cannot ‘control’ concurrent operations I just simulate some interleaving. Such a simulation never ever shows the real effects of race-conditions, but it’s deterministic to demonstrate the effects.
One thread takes the Price and Amount and calculate the sum and stores it. The other thread doubles the Amount and cuts the Price in half. So the sum should be always the same, right? The start-values are: Amount = 2, Price = 4.
So the first interleaving:
var toUpdate = GetTheUpdateObject(); InOtherThreadThisRanThrought( () => { var toUpdateT2 = GetTheUpdateObject(); toUpdateT2.Amount = toUpdate.Amount * 2; toUpdateT2.Price = toUpdate.Price / 2; db.Store(toUpdateT2); }); var sum = toUpdate.Price; sum = sum * toUpdate.Amount; toUpdate.Sum = sum; Console.Out.WriteLine("Sum is="+toUpdate.Sum+" Expected is 8"); db.Store(toUpdate);
The second interleaving:
var toUpdate = GetTheUpdateObject(); var sum = toUpdate.Price; InOtherThreadThisRanThrought( () => { var toUpdateT2 = GetTheUpdateObject(); toUpdateT2.Amount = toUpdate.Amount * 2; toUpdateT2.Price = toUpdate.Price / 2; db.Store(toUpdateT2); }); sum = sum * toUpdate.Amount; toUpdate.Sum = sum; Console.Out.WriteLine("Sum is=" + toUpdate.Sum + " Expected is 8"); db.Store(toUpdate);
The first interleaving will return the correct result. The second one will return 16, because between the reads of ‘Price’ and ‘Amount’ the ‘Amount’ was changed. (As you remember from previous posts, the object-container caches the objects and returns always the same instances)
Well the object-container is thread-safe, but actually that doesn’t do much for us.
Ok, I Just Synchronize The Persisted Classes
Ok, we could synchronize all properties and methods on the persisted classes. But it won’t fix the problem above. Since the race-condition happens between the reads of the two properties.
Keep It Simple Solution, No Concurrent Operations
Well the simplest solution is to avoid any concurrency on the data-operations. So you never ever touch your persistent objects and database concurrently. This actually will work quite good in a simple single-user CRUD-Application.
However, as soon as your operations on the database and persistent objects takes more time, this isn’t a good solution. (the classic UI-freezes).
Big Fat Lock Solution
This another quite obvious solution. You create a Big-Fat-Lock which protects all operations on the db and persistent objects. So you can ensure that everything stays consistent:
lock(dbLock)
load and store persistent objects
do work with persistent objects
release(dbLock)
Yes, it’s still pretty non-concurrent, but it’s enough for lots of applications. It works fine as long the Application is IO-bound anyway and you don’t to computation-intensive stuff.
This is the way I do it in my application. It allows to run some operations in the background without blocking the rest. Here comes the closure-style API handy:
dataBase.InTransaction(
tx=>{
db.Store(new DemoObject());
});
The InTransaction-Method ensures that the the transaction is either committed or rolled back and that the Big-Fat-Lock is hold. Now I do every manipulation on your persistent-object in such a closure.
Pay attention how you use your persistent-object. For example when you use data-binding (WPF-Binding, Beans-Binding in JAVA). Such binding-mechanisms won’t grab your db-lock. So If you want to just bind the persistent object to the GUI this approach won’t work.
Finer Grained Mechanisms
The solution above a pretty simple, but won’t scale a at all. When you really need more performance you need finer grained lock mechanisms. Like per class-lock, per object-lock or whatever strategies fits your usage.
But What About Client-Server?
So far I’ve talked about stuff isn’t that special to db4o and I assumed that there’s only one object-container around. But what about the db4o client-server-model, which has more than one object-container? I haven’t lost a word about it. So that is the topic of the upcoming blog-post =)
100th Post
Just noticed that this is the 100th post on my blog. Yuppie =)
Demo-Files: InterleavingDemo.cs, SimpleObject.cs
- Free IntelliJ-IDEA and YouTrack Pricing
- db4o: Queries in Java or Queries Without LINQ