Skip to content

Commit

Permalink
Don't cache beyond the cache.data capacity
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonkelly committed Mar 31, 2024
1 parent 5572bb7 commit bca6168
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 16 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- The Craft 5 Upgrade utility now shows the upgrade status and latest compatible version for abandoned plugins.
- Fixed a “Double-instantiating a checkbox select on an element” JavaScript warning. ([#14707](https://github.com/craftcms/cms/issues/14707))
- Fixed a bug where `craft\cache\DbCache` was attempting to store values beyond the `cache.data` column’s storage capacity.

## 4.8.6 - 2024-03-26

Expand Down
31 changes: 29 additions & 2 deletions src/cache/DbCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
use craft\helpers\Db;
use Exception;
use PDO;
use Throwable;
use yii\base\InvalidConfigException;
use yii\base\NotSupportedException;
use yii\caching\DbCache as YiiDbCache;
use yii\db\PdoValue;

Expand All @@ -28,8 +31,32 @@ class DbCache extends YiiDbCache
*/
protected function setValue($key, $value, $duration): bool
{
// Copied from yii\caching\DbCache::setValue() except for the added includeAuditColumns=false argument
try {
// Make sure the table exists
$table = $this->db->getTableSchema($this->cacheTable);
if (!$table) {
throw new InvalidConfigException(sprintf(
'The `%s` table doesn’t exist. Run the `setup/db-cache-table` command to create it.',
$this->db->getSchema()->getRawTableName($this->cacheTable),
));
}

// Make sure the data fits within the column
$maxSize = Db::getTextualColumnStorageCapacity(
$table->getColumn('data')->dbType,
$this->db instanceof Connection ? $this->db : null,
);
$valueSize = strlen($value);
if ($maxSize && $valueSize > $maxSize) {
throw new NotSupportedException(sprintf(
'The `%s`.`data` column can only store up to %s bytes. (Attempting to store %s bytes.)',
$this->db->getSchema()->getRawTableName($this->cacheTable),
$maxSize,
$valueSize,
));
}

// Copied from yii\caching\DbCache::setValue() except for the added includeAuditColumns=false argument
$this->db->noCache(function(Connection $db) use ($key, $value, $duration) {
Db::upsert($this->cacheTable, [
'id' => $key,
Expand All @@ -39,7 +66,7 @@ protected function setValue($key, $value, $duration): bool
});
$this->gc();
return true;
} catch (Exception $e) {
} catch (Throwable $e) {
Craft::warning("Unable to update or insert cache data: {$e->getMessage()}", __METHOD__);
return false;
}
Expand Down
22 changes: 8 additions & 14 deletions src/helpers/Db.php
Original file line number Diff line number Diff line change
Expand Up @@ -299,22 +299,16 @@ public static function getTextualColumnStorageCapacity(string $columnType, ?Conn
}

if ($db->getIsMysql()) {
if (isset(self::$_mysqlTextSizes[$shortColumnType])) {
return self::$_mysqlTextSizes[$shortColumnType];
}

// ENUM depends on the options
if ($shortColumnType === MysqlSchema::TYPE_ENUM) {
return null;
}

// ¯\_(ツ)_/¯
return false;
return match ($shortColumnType) {
MysqlSchema::TYPE_ENUM => null, // ENUM depends on the options
'blob' => self::$_mysqlTextSizes[Schema::TYPE_TEXT],
'longblob' => self::$_mysqlTextSizes[MysqlSchema::TYPE_LONGTEXT],
default => self::$_mysqlTextSizes[$shortColumnType] ?? false,
};
}

// PostgreSQL doesn't impose a limit for text fields
if ($shortColumnType === Schema::TYPE_TEXT) {
// TEXT columns are variable-length in 'grez
// Postgres doesn't impose a limit for text/binary fields
if (in_array($shortColumnType, [Schema::TYPE_TEXT, Schema::TYPE_BINARY])) {
return null;
}

Expand Down

0 comments on commit bca6168

Please sign in to comment.