Skip to content

Instantly share code, notes, and snippets.

@mikedfunk
Last active January 18, 2019 20:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mikedfunk/5850dbc3cea7cf52f8d6875686a386d8 to your computer and use it in GitHub Desktop.
Save mikedfunk/5850dbc3cea7cf52f8d6875686a386d8 to your computer and use it in GitHub Desktop.
<?php declare(strict_types=1);
namespace MyApp\Adapter\Repository\Couchbase;
use CouchbaseException;
class LockableRepository
{
// ....
/**
* Attempt to acquire lock and return lock data, otherwise return nothing.
*
* This is used by the lock handler to determine whether we were able to
* acquire a lock. If it was locked (return falsey), it waits and tries
* again up to a point. You can see the usage in a class that extends this.
*/
public function attemptToAcquireLock(string $key, int $lockSeconds): array?
{
$bucketName = $this->getBucketName();
try {
$doc = $this->couchbaseCluster->
openBucket($bucketName)->
getAndLock($key, $lockSeconds);
} catch (CouchbaseException $exception) {
$code = $exception->getCode();
if ($code === COUCHBASE_ETMPFAIL) {
return null;
}
throw $exception;
}
return [
'type' => 'couchbase',
'lock_id' => $doc->cas,
'bucket_name' => $bucketName,
'value' => $doc->value,
];
}
/**
* When storing an entity, get the couchbase unlock options.
*
* This is abstracted to shorten an otherwise long store method in each
* repo that extends this. It also allows failing early by returning an
* empty array. If this were not abstracted I'd have to wrap the rest of
* this in an if/else, increasing indentation and code size.
*/
protected function getCouchbaseUnlockOptionsForStore(
string $docKey,
bool $wasLockedDuringCurrentProcess,
bool $dangerouslySkipLocking
): array {
// wait for and acquire lock if something else has locked this
if ($dangerouslySkipLocking) {
return [];
}
if (!$wasLockedDuringCurrentProcess) {
$this->lockAndGetCouchbaseData($docKey);
}
$lockId = $this->lockHandler->getLockId($docKey);
return ['cas' => $lockId];
}
/**
* If it's locked, wait for the lock to be released, then get and lock the
* document and return the json value of the couchbase doc.
*/
protected function lockAndGetCouchbaseData(string $docKey): string
{
// passing a callable to waitAndAcquire. This callable attempts to
// acquire a lock, returning lockData if it succeeds, and returning
// falsey if it fails. See waitAndAcquire method documentation for
// more details.
$lockData = $this->lockHandler->
waitAndAcquire($docKey, [$this, 'attemptToAcquireLock']);
return $lockData['value'];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment