I’d call this an 0.5 release. It’s now in the code repo that we at Automattic use to put out little open source tools.
I’ve fixed several bugs with the code since the first time that I posted about it. It can handle at least 2k active connections, and at least 100k shared and exclusive locks (split 50/50) and can handle every single connection orphaning its locks at the same time for both shared and exclusive locks.
It’s a pretty good bit of code… not bad for under a week in my spare time.
I suppose that I should explain what it is and does. And why I care.
The idea behind lockd was to build a network oriented daemon which would handle arbitrary locking of… things. The idea was to mix the ease of Memcached with the atomic chewy goodness of MySQls named locks. It’s really handy to have something like this that can be depended upon when you have a very large distributed environment to keep things from stepping on its own toes.
Only one client can have an exclusive lock, and only the lock owner can release it. any client can inspect that exclusive lock to see if it’s held or not. If the owner disconnects from the server then all of the owned locks are released.
Any number of clients can acquire a shared lock, and the result of the locking action includes the number of other owners sharing that lock. The response for the first lock request would be 1 lock acquired, while the second lock request for the lock would be 2 lock acquired (i.e. two people have this lock.) Likewise releasing the lock decrements it, and inspecting the lock shows the number of open handles to that lock. All of an owners shared locks are also released on disconnect.
Oh, did I mention that it also keeps stats for you to use in your munin graphs? Yea. That too.
So… some obvious questions I’m sure you’re wondering:
1: why not just use Memcached? Well Memcached has no knowledge of the state of your connection. I want a lock that goes away if the parent process disconnects for any reason. You could do this with timed keys in Memcached but you run two risks: the first being that you might not get around to updating the lock and resetting its ttl before it expires leaving another process able to lock erroneously, and the second being that given enough data flowing through the cache your lock might simply be pushed off the back end of the cache by newer data — something that I don’t want. Also shared locks would be difficult here.
2. Why not just use MySQL named locks? You can only hold one named lock per connection, and there is no concept of shared named locks.
3. Why not use filesystem locks? Those tend to be difficult for people to code properly, depend on various standards implementations, cant do counted shared locks, and most importantly aren’t network enabled.
4. Whats the big deal about shared locks? They’re super powerful — great for rate limiting, etc.
5. Wasn’t there already something out there for this? I’m not going to say “no,” but I will say “not that I saw when I looked”.
6. Why did you rewrite it in erlang, was the PHP version bad? Yes, sort of, the PHP version played some interesting tricks to achieve a thread-like operational state, but I believe that there are timing issues because of those tricks that cause it to crash in as-of-yet unknown circumstances at high load. The PHP version is also slow when there are a very high number of clients or locks. Erlang was, essentially, born for this particular purpose since it sports great concurrency models immutable variables, and the way that the gen_* things work out I get atomicity built in even with all these concurrent clients grabbing at stuff.
7. Whats the API look like? It looks nice and clean…
“g $key
” — get exclusive $key
“r $key
” — release exclusive $key
“i $key
” — inspect exclusive $key
“sg $key
” — get shared $key
“sr $key
” — release shared $key
“si $key
” — inspect shared $key
“q
” — show stats