That way we don’t have to worry about extra front-end handling at all.
If something is locked, we just wait until it’s released and then continue on.
In order to handle this on the backend, we need to know if something else is working on a document.
If so, we wait until it is written before reading it and starting the next operation that writes.
In the above example, we can do this by locking cart access when we retrieve it.
When we attempt to retrieve it, we loop through and wait until it’s unlocked before we retrieve it and lock it.
If it takes too long, we time out and throw a specific exception.
We use Couchbase for storage in this context.
Couchbase has locking built in… sort of.
You can lock a document with $bucket->getAndLock($key) which will return the value and a lock id (Couchbase calls it a CAS value).
If you try to persist or delete the document without the right lock id, it will throw an exception.
If you try to getAndLock($key) while it’s locked, it will immediately throw an exception.
Couchbase doesn’t have a built-in approach to wait until the doc is unlocked and then return the document.
Some goals to keep in mind while adding locking:Track and untrack locks acquired during the current processRelease all locks acquired during the current process if an exception is thrown and not caughtRelease any remaining locks acquired during the current process on ending middleware in case something was missedEnsure the repository is responsible for the couchbase specifics of attempting to acquire the lockOur backend is in PHP.
We already use the repository pattern, which will apply here.
Here is the class structure:A means Abstract Class, C means classThe abstractLockableRepository provides methods to deal with locking.
It uses the LockHandler to do this.
The LockHandler is like a locking service used by the LockableRepository abstract class to handle everything with locking.
It waits for and acquires a lock and provides passthrough methods to interact with the LockTracker .
The LockTracker is a singleton with a private constructor.
It keeps track of locks placed during the current process so they can be released and untracked later.
This is the only thing that needs to be a singleton.
The LockReleaser only deals with releasing all locks acquired by the current process, a function that is not needed by repositories.
Let’s focus in on some of the more important methods here:LockHandler::waitAndAcquire()LockableRepository::attemptToAcquireLock()CartRepository::getCart() and storeCart()LockReleaser::releaseLocksFromCurrentProcess()Let’s start with LockHandler::waitAndAcquire():As you can see, we pass in a callable that attempts to acquire a lock.
This method exists on the LockableRepository to ensure Couchbase access all stays in the repository.
If it fails, we try again until the document is unlocked and we can acquire a new lock.
If it’s taking too long, we throw an exception.
Here’s how we make use of this in the LockableRepository:In the concrete repository we just use lockAndGetCouchbaseData.
This returns the lock id, the document value, and a few other things.
Examples in the CartRepository show how we use it to apply locking:Here we finally make use of the LockHandler methods to wait and acquire a lock and retrieve the document, mark the lock as released, check if a lock was tracked by the current process, etc.
When we’re all finished, if we missed releasing any locks obtained by the current process, or if an exception is not caught, we releaseLocksFromCurrentProcess like so:This is used in the exception handler and the release tracked locks middleware.
This design allows us to achieve all of our starting goals along with a few extra perks:If we were to lock other kinds of data, we could still make use of the LockHandler, LockTracker, and add a new type of LockReleaser to clean upRepositories maintain responsibility for couchbase operations, while the looping, time limits, and error handling are the responsibility of the LockHandlerThe LockTracker can track any kind of lock, and can store any arbitrary lock data in the value array for each lock key.
This keeps it loosely coupled to couchbase.
Finish strong!I hope this was helpful.
If you have any feedback, I’d love to hear what you have to say in the comments.
See you next time!.