The resulting behavior is that (assuming that two code sections tried to operate on the same meter concurrently), one would get there first and the second would be blocked until the first called the OSUnlock. At that time, the second would run and I would be assured that the code sections would always run in sequence for any given meter (as opposed to concurrently).
In Ruby/Rails, I see that there are mutexes, but they seem to be applicable to multiple threads running on the same in memory objects. In my case, the user request is going to load its copy of the meter from the database and the cron task is going to load its copy, so - mutexes don’t help. Transactions and pessimistic locking seemed promising, but from my experimentation with them they will only prevent concurrent operations on the specific object that is locked. Operations on associated objects, even if they are inside transaction blocks, can still run concurrently. So - it seems that the executing code is not waiting, rather, the database access, for the locked object only, is waiting.
I need to block the cron task from executing any further than where the lock is taken until the user task releases the lock (and vice versa). How do I do that?
You can still use a database lock on the meter as the gatekeeper to modifying either the meter or its associated objects (although obviously you need to do this in the webapp side too). In fact you don’t necessarily need a long lived database lock - it is sometimes easier to have a locked / locked_by attribute on the model, and only use an actual database lock (or optimistic locking) when writing to it.
Actually blocking for a long time on something is not something you really want to do in a web app (assuming this background processing is lengthy) - you’re better off failing fast and telling the use to try later