Skip to content
Snippets Groups Projects
Commit 1181ff42 authored by Claus Due's avatar Claus Due Committed by Christian Kuhn
Browse files

[TASK] Make MemcachedBackend a transient backend

Allows passing non-string values to the backend, which
is perfectly allowed for this type of backend. The change
means that VariableFrontends used with this backend will
store non-string values without serializing, thus optimising
performance and transparency.

Key changes:

* Exceptions are no longer thrown when a non-string is passed
* Chunk-splitting does not happen on non-strings; entries
   exceeding the maximum bucket size get logged and ignored.
* Serializer decision is delegated to memcached configuration.

Change-Id: Ie11736be621a2dd27bfde60b82cd5f6b3a04d981
Resolves: #80246
Releases: master
Reviewed-on: https://review.typo3.org/52015


Tested-by: default avatarTYPO3com <no-reply@typo3.com>
Reviewed-by: default avatarClaus Due <claus@phpmind.net>
Tested-by: default avatarClaus Due <claus@phpmind.net>
Reviewed-by: default avatarElmar Hinz <t3elmar@gmail.com>
Tested-by: default avatarElmar Hinz <t3elmar@gmail.com>
Reviewed-by: default avatarChristian Kuhn <lolli@schwarzbu.ch>
Tested-by: default avatarChristian Kuhn <lolli@schwarzbu.ch>
parent 2de9fb40
Branches
Tags
No related merge requests found
......@@ -44,7 +44,7 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
* This file is a backport from FLOW3 by Ingo Renner.
* @api
*/
class MemcachedBackend extends AbstractBackend implements TaggableBackendInterface
class MemcachedBackend extends AbstractBackend implements TaggableBackendInterface, TransientBackendInterface
{
/**
* Max bucket size, (1024*1024)-42 bytes
......@@ -234,42 +234,26 @@ class MemcachedBackend extends AbstractBackend implements TaggableBackendInterfa
if (!$this->cache instanceof FrontendInterface) {
throw new Exception('No cache frontend has been set yet via setCache().', 1207149215);
}
if (!is_string($data)) {
throw new Exception\InvalidDataException('The specified data is of type "' . gettype($data) . '" but a string is expected.', 1207149231);
}
$tags[] = '%MEMCACHEBE%' . $this->cacheIdentifier;
$expiration = $lifetime !== null ? $lifetime : $this->defaultLifetime;
$memcacheIsUsed = $this->usedPeclModule === 'memcache';
// Memcached consideres values over 2592000 sec (30 days) as UNIX timestamp
// thus $expiration should be converted from lifetime to UNIX timestamp
if ($expiration > 2592000) {
$expiration += $GLOBALS['EXEC_TIME'];
}
try {
if (strlen($data) > self::MAX_BUCKET_SIZE) {
if (is_string($data) && strlen($data) > self::MAX_BUCKET_SIZE) {
$data = str_split($data, 1024 * 1000);
$success = true;
$chunkNumber = 1;
foreach ($data as $chunk) {
if ($memcacheIsUsed) {
$success = $success && $this->memcache->set($this->identifierPrefix . $entryIdentifier . '_chunk_' . $chunkNumber, $chunk, $this->flags, $expiration);
} else {
$success = $success && $this->memcache->set($this->identifierPrefix . $entryIdentifier . '_chunk_' . $chunkNumber, $chunk, $expiration);
}
$success = $success && $this->setInternal($entryIdentifier . '_chunk_' . $chunkNumber, $chunk, $expiration);
$chunkNumber++;
}
if ($memcacheIsUsed) {
$success = $success && $this->memcache->set($this->identifierPrefix . $entryIdentifier, 'TYPO3*chunked:' . $chunkNumber, $this->flags, $expiration);
} else {
$success = $success && $this->memcache->set($this->identifierPrefix . $entryIdentifier, 'TYPO3*chunked:' . $chunkNumber, $expiration);
}
$success = $success && $this->setInternal($entryIdentifier, 'TYPO3*chunked:' . $chunkNumber, $expiration);
} else {
if ($memcacheIsUsed) {
$success = $this->memcache->set($this->identifierPrefix . $entryIdentifier, $data, $this->flags, $expiration);
} else {
$success = $this->memcache->set($this->identifierPrefix . $entryIdentifier, $data, $expiration);
}
$success = $this->setInternal($entryIdentifier, $data, $expiration);
}
if ($success === true) {
$this->removeIdentifierFromAllTags($entryIdentifier);
......@@ -282,6 +266,23 @@ class MemcachedBackend extends AbstractBackend implements TaggableBackendInterfa
}
}
/**
* Stores the actual data inside memcache/memcached
*
* @param string $entryIdentifier
* @param mixed $data
* @param int $expiration
* @return bool
*/
protected function setInternal($entryIdentifier, $data, $expiration)
{
if ($this->usedPeclModule === 'memcache') {
return $this->memcache->set($this->identifierPrefix . $entryIdentifier, $data, $this->flags, $expiration);
} else {
return $this->memcache->set($this->identifierPrefix . $entryIdentifier, $data, $expiration);
}
}
/**
* Loads data from the cache.
*
......@@ -292,7 +293,7 @@ class MemcachedBackend extends AbstractBackend implements TaggableBackendInterfa
public function get($entryIdentifier)
{
$value = $this->memcache->get($this->identifierPrefix . $entryIdentifier);
if (substr($value, 0, 14) === 'TYPO3*chunked:') {
if (is_string($value) && substr($value, 0, 14) === 'TYPO3*chunked:') {
list(, $chunkCount) = explode(':', $value);
$value = '';
for ($chunkNumber = 1; $chunkNumber < $chunkCount; $chunkNumber++) {
......
.. include:: ../../Includes.txt
=====================================================
Important: #80246 - MemcachedBackend marked transient
=====================================================
See :issue:`80246`
Description
===========
The Memcached cache backend is marked transient. This has the following effect:
* The backend now supports non-string values (Memcached serializes and compresses data internally, configured in php.ini)
An Exception is no longer raised if a custom cache frontend attempts to store non-strings in a Memcached backend.
* Unnecessary serialization and unserialization is prevented, slightly improving performance.
There is a single side effect: when used with a VariableFrontend and attempting to store data whose serialized and
compressed representation exceeds the Memcached limit (~1MB), the cache operation fails silently and logs a warning.
The system keeps operating as normal and will log such failures every time it happens.
The side effect only applies to VariableFrontend and only when passing non-string values. When you pass a string bigger
than ~1MB the backend performs chunk-split exactly as before, regardless if string was passed through a VariableFrontend.
.. index:: PHP-API
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment