Last active
October 21, 2020 16:20
-
-
Save andybeak/171f4c358debcc304462 to your computer and use it in GitHub Desktop.
Storing large values in Memcached
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* store | |
* | |
* Stores a large array into memcache as a number of key=>values in order to overcome the limitation placed | |
* on memcache. You'll need to use discretion when choosing to do this. | |
* | |
* The principle is to split the data into several smaller chunks. The original key is used to store an | |
* index array which provides retrieveBigCacheValue() with information on where to look for the data. | |
* | |
* See retrieveBigCacheValue for the inverse | |
* | |
* @version 1.0.0 | |
* @author Andy Beak | |
* @since 1.0.0 | |
* @access public | |
* @param $tag | |
* @param $key | |
* @param array $value | |
*/ | |
public function storeBigCacheValue($tag, $key, array $value) { | |
$randomKey = $this->createUid(); | |
$splitSize = 1000 * 900; // 900kb (less than 1 meg) | |
$value = gzcompress(serialize($value)); | |
$chunks = str_split($value, $splitSize); | |
$cacheTime = Config::get('app.cacheLifetime'); | |
foreach ($chunks as $index => $store) { | |
$indexKey = $randomKey.'-'.$index; | |
Log::debug(__METHOD__.' : Storing ' . $indexKey . ' for ' . $cacheTime . ' minutes'); | |
Cache::tags($tag)->put($indexKey, $store, $cacheTime); | |
} | |
if (!Cache::tags($tag)->has($randomKey.'-0')) { | |
throw new \Exception(__METHOD__.' : Could not find first cache entry - Split size is ' . $splitSize . ' bytes'); | |
} | |
$info = [ | |
'key' => $randomKey, | |
'count' => count($chunks) | |
]; | |
Cache::tags($tag)->put($key, $info, $cacheTime); | |
} | |
/** | |
* retrieveBigCacheValue | |
* | |
* Retrieves a big cache value that was stored in a split-up format by storeBigCacheValue | |
* | |
* | |
* @version 1.0.0 | |
* @author Andy Beak | |
* @since 1.0.0 | |
* @access public | |
* @param $tag | |
* @param $key | |
* @return mixed | |
*/ | |
public function retrieveBigCacheValue($tag, $key) { | |
if (!Cache::tags($tag)->has($key)) { | |
Log::debug(__METHOD__.' : Key ' . $key . ' in tag ' . $tag . ' is not present. Has Memcached already removed it?'); | |
return false; | |
} | |
$index = Cache::tags($tag)->get($key); | |
if (!is_array($index) || !isset($index['key']) || !isset($index['count'])) { | |
Log::debug(__METHOD__.' : Key ' . $key . ' in tag ' . $tag . ' is not one stored by storeBigCacheValue'); | |
return false; | |
} | |
$lookupKey = $index['key']; | |
$count = $index['count']; | |
$chunks = []; | |
for ($i = 0; $i < $count; $i++) { | |
if (!Cache::tags($tag)->has($lookupKey.'-'.$i)) { | |
Log::debug(__METHOD__.' : The chunk ' . $i . ' is missing from ' . $key); | |
return false; | |
} | |
$chunks[] = Cache::tags($tag)->get($lookupKey.'-'.$i); | |
} | |
$compressed = implode('', $chunks); | |
unset($chunks); | |
return unserialize(gzuncompress($compressed)); | |
} | |
/** | |
* createUid | |
* | |
* Pattern defines the number of characters in each part of the uuid. We loop through the | |
* pattern and replace the number with the proper amount of random hex chars. | |
* | |
* @version 1.0.0 | |
* @author Andy Beak | |
* @since 1.0.0 | |
* @access public | |
* @return string | |
*/ | |
public function createUid() { | |
$pattern = [8, 4, 4, 4, 12]; | |
foreach ($pattern as $key => $length) { | |
$bytes = openssl_random_pseudo_bytes($length / 2, $cstrong); | |
if (false === $cstrong) { | |
Log::warning(__METHOD__.' : Host has insecure crypto method - try updating'); | |
} | |
$pattern[$key] = bin2hex($bytes); | |
} | |
return implode('-', $pattern); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment