Skip to content

Commit

Permalink
feat: use a single cache for all bins
Browse files Browse the repository at this point in the history
  • Loading branch information
pgautier404 committed Oct 18, 2023
1 parent fabe186 commit 657b18d
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 48 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
}
},
"require": {
"momentohq/client-sdk-php": "dev-add-flush-cache"
"momentohq/client-sdk-php": "1.5.0"
}
}
19 changes: 2 additions & 17 deletions src/Client/MomentoClientFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Drupal\Core\DependencyInjection\ContainerNotInitializedException;
use Drupal\Core\Site\Settings;
use Drupal\Core\Logger\LoggerChannelTrait;
use Drupal\momento_cache\MomentoCacheBackendFactory;
use Momento\Auth\StringMomentoTokenProvider;
use Momento\Cache\CacheClient;
use Momento\Config\Configurations\Laptop;
Expand All @@ -23,29 +24,13 @@ public function __construct() {
$authToken = array_key_exists('api_token', $settings) ?
$settings['api_token'] : getenv("MOMENTO_API_TOKEN");
$this->cachePrefix = array_key_exists('cache_name_prefix', $settings) ?
$settings['cache_name_prefix'] : 'drupal-';
$settings['cache_name_prefix'] : '';
$this->authProvider = new StringMomentoTokenProvider($authToken);
}

public function get() {
if (!$this->client) {
$this->client = new CacheClient(Laptop::latest(), $this->authProvider, 30);
// Ensure "container" cache exists
if (!$this->containerCacheCreated) {
$createResponse = $this->client->createCache($this->cachePrefix . 'container');
if ($createResponse->asSuccess()) {
$this->containerCacheCreated = true;
} elseif ($createResponse->asError()) {
try {
// TODO: unify logging interface and direct this message to the logfile.
$this->getLogger('momento_cache')->error(
"Error getting Momento client: " . $createResponse->asError()->message()
);
} catch(ContainerNotInitializedException $e) {
// we don't have access to getLogger() until the container is initialized
}
}
}
}
return $this->client;
}
Expand Down
94 changes: 71 additions & 23 deletions src/MomentoCacheBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class MomentoCacheBackend implements CacheBackendInterface
private $backendName = "momento-cache";
private $bin;
private $binTag;
private $lastBinDeletionTime;
private $client;
private $checksumProvider;
private $MAX_TTL;
Expand All @@ -27,19 +28,52 @@ public function __construct(
$client,
CacheTagsChecksumInterface $checksum_provider
) {
$start = hrtime(true);
$this->MAX_TTL = intdiv(PHP_INT_MAX, 1000);
$this->client = $client;
$this->bin = $bin;
$this->checksumProvider = $checksum_provider;
$this->binTag = "$this->backendName:$this->bin";

$settings = Settings::get('momento_cache', []);
$cacheNamePrefix =
array_key_exists('cache_name_prefix', $settings) ? $settings['cache_name_prefix'] : "drupal-";
$this->cacheName = $cacheNamePrefix . $this->bin;
$this->cacheName = MomentoCacheBackendFactory::getCacheName();
$this->logFile =
array_key_exists('logfile', $settings) ? $settings['logfile'] : null;

if ($bin == 'container') {
$this->log("My bin is container");
}

$this->ensureLastBinDeletionTimeIsSet();
}

private function ensureLastBinDeletionTimeIsSet() {
if (!$this->lastBinDeletionTime) {
$getRequest = $this->client->get($this->cacheName, $this->bin);
if ($getRequest->asError()) {
$this->log(
"ERROR getting last deletion time for bin $this->bin: " .
$getRequest->asError()->message() .
"\nCache name is $this->cacheName"
);
} elseif ($getRequest->asMiss()) {
$this->log("Setting last deletion time for $this->bin");
$this->setLastBinDeletionTime();
} else {
$this->lastBinDeletionTime = intval($getRequest->asHit()->valueString());
}
}
}

private function setLastBinDeletionTime() {
$this->lastBinDeletionTime = round(microtime(TRUE), 3);
$setRequest = $this->client->set($this->cacheName, $this->bin, $this->lastBinDeletionTime);
if ($setRequest->asError()) {
$this->log(
"ERROR getting last deletion time for bin $this->bin: " . $setRequest->asError()->message()
);
} else {
$this->Log("Set last deletion time for $this->bin to $this->lastBinDeletionTime");
}
}

public function get($cid, $allow_invalid = FALSE)
Expand All @@ -49,20 +83,29 @@ public function get($cid, $allow_invalid = FALSE)
return reset($recs);
}

private function getCidForBin($cid) {
return "$this->bin:$cid";
}

public function getMultiple(&$cids, $allow_invalid = FALSE)
{
$start = $this->startStopwatch();
$fetched = [];
foreach (array_chunk($cids, 100) as $cidChunk) {
$futures = [];
foreach ($cidChunk as $cid) {
$futures[$cid] = $this->client->getAsync($this->cacheName, $cid);
$futures[$cid] = $this->client->getAsync($this->cacheName, $this->getCidForBin($cid));
}

foreach ($futures as $cid => $future) {
$getResponse = $future->wait();
if ($getResponse->asHit()) {
$result = unserialize($getResponse->asHit()->valueString());

if ($result->created <= $this->lastBinDeletionTime) {
continue;
}

if ($allow_invalid || $this->valid($result)) {
$fetched[$cid] = $result;
}
Expand Down Expand Up @@ -107,11 +150,13 @@ public function set($cid, $data, $expire = CacheBackendInterface::CACHE_PERMANEN
}
$item->expire = $expire;

$setResponse = $this->client->set($this->cacheName, $cid, serialize($item), $ttl);
$setResponse = $this->client->set($this->cacheName, $this->getCidForBin($cid), serialize($item), $ttl);
if ($setResponse->asError()) {
$this->log("SET response error for bin $this->bin: " . $setResponse->asError()->message());
} else {
$this->stopStopwatch($start, "SET cid $cid in bin $this->bin with ttl $ttl");
$this->stopStopwatch(
$start, "SET cid $cid in bin $this->bin with ttl $ttl (real cid = " .$this->getCidForBin($cid) . ")"
);
}
}

Expand All @@ -130,7 +175,7 @@ public function setMultiple(array $items)
public function delete($cid)
{
$start = $this->startStopwatch();
$deleteResponse = $this->client->delete($this->cacheName, $cid);
$deleteResponse = $this->client->delete($this->cacheName, $this->getCidForBin($cid));
if ($deleteResponse->asError()) {
$this->log("DELETE response error for $cid in bin $this->bin: " . $deleteResponse->asError()->message());
} else {
Expand All @@ -149,15 +194,17 @@ public function deleteMultiple(array $cids)

public function deleteAll()
{
$start = $this->startStopwatch();
$flushResponse = $this->client->flushCache($this->cacheName);
if ($flushResponse->asError()) {
$this->log(
"FLUSH_CACHE response error for $this->cacheName: " . $flushResponse->asError()->message()
);
} else {
$this->stopStopwatch($start, "FLUSH_CACHE for $this->bin");
}
// TODO: set bin lastDeletionTime
$this->setLastBinDeletionTime();
// $start = $this->startStopwatch();
// $flushResponse = $this->client->flushCache($this->cacheName);
// if ($flushResponse->asError()) {
// $this->log(
// "FLUSH_CACHE response error for $this->cacheName: " . $flushResponse->asError()->message()
// );
// } else {
// $this->stopStopwatch($start, "FLUSH_CACHE for $this->bin");
// }
}

public function invalidate($cid)
Expand Down Expand Up @@ -195,12 +242,13 @@ public function invalidateTags(array $tags)

public function removeBin()
{
$start = $this->startStopwatch();
$deleteResponse = $this->client->deleteCache($this->cacheName);
if ($deleteResponse->asError()) {
$this->log("DELETE_CACHE response error for bin $this->cacheName: " . $deleteResponse->asError()->message());
}
$this->stopStopwatch($start, "REMOVE_BIN for $this->bin");
$this->setLastBinDeletionTime();
// $start = $this->startStopwatch();
// $deleteResponse = $this->client->deleteCache($this->cacheName);
// if ($deleteResponse->asError()) {
// $this->log("DELETE_CACHE response error for bin $this->cacheName: " . $deleteResponse->asError()->message());
// }
// $this->stopStopwatch($start, "REMOVE_BIN for $this->bin");
}

public function garbageCollection()
Expand Down
16 changes: 9 additions & 7 deletions src/MomentoCacheBackendFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,12 @@ class MomentoCacheBackendFactory implements CacheFactoryInterface {
private $checksumProvider;
private $timestampInvalidator;

private static $cacheName = 'momento-drupal';
private $client;
private $caches;
private $cacheListGoodForSeconds = 3;
private $cacheListTimespamp;
private $authProvider;
private $cacheNamePrefix;
private $tagsCacheId = '_momentoTags';
private $tagsCacheName;
private $backends = [];


Expand All @@ -33,12 +31,16 @@ public function __construct(
) {
$this->momentoFactory = $momento_factory;
$this->checksumProvider = $checksum_provider;
$settings = Settings::get('momento_cache', []);
$this->cacheNamePrefix = array_key_exists('cache_name_prefix', $settings) ?
$settings['cache_name_prefix'] : "drupal-";
$this->client = $this->momentoFactory->get();
}

public static function getCacheName() {
$settings = Settings::get('momento_cache', []);
$cacheNamePrefix = array_key_exists('cache_name_prefix', $settings) ?
$settings['cache_name_prefix'] : '';
return $cacheNamePrefix . static::$cacheName;
}

public function get($bin)
{
if (array_key_exists($bin, $this->backends)) {
Expand All @@ -52,7 +54,7 @@ public function get($bin)
$this->populateCacheList();
}

$cacheName = $this->cacheNamePrefix . $bin;
$cacheName = static::getCacheName();
if (!in_array($cacheName, $this->caches)) {
$this->createCache($cacheName);
}
Expand Down

0 comments on commit 657b18d

Please sign in to comment.