core/lib/Drupal/Core/Cache/DatabaseBackend.php | 44 ++++++++++++++++++++++++++ core/modules/system/system.install | 11 +++++++ 2 files changed, 55 insertions(+) diff --git a/core/lib/Drupal/Core/Cache/DatabaseBackend.php b/core/lib/Drupal/Core/Cache/DatabaseBackend.php index d53c51c..da1ee6d 100644 --- a/core/lib/Drupal/Core/Cache/DatabaseBackend.php +++ b/core/lib/Drupal/Core/Cache/DatabaseBackend.php @@ -17,6 +17,16 @@ class DatabaseBackend implements CacheBackendInterface { /** + * The megabytes of disk space this cache bin is allowed to use. + * + * @todo Bikeshed the size limit. + * @todo Make configurable per-bin. + * + * @var int + */ + protected static $boundedSizeMegabytes = 10; + + /** * @var string */ protected $bin; @@ -326,6 +336,34 @@ public function invalidateAll() { */ public function garbageCollection() { try { + + $connection_info = $this->connection->getConnectionOptions(); + if ($connection_info['driver'] === 'mysql') { + $query = "SELECT round(((`data_length` + `index_length`) / 1024 / 1024), 2) AS `size` FROM `information_schema`.`TABLES` + WHERE (`TABLE_SCHEMA` = '" . $connection_info['database'] . "') + AND (`TABLE_NAME` = '{" . $this->bin. "}')"; + $results = $this->connection->query($query); + $size = (float) $results->fetchField(); + + if ($size > static::$boundedSizeMegabytes) { + $query = $this->connection->select($this->bin); + $query->addExpression('COUNT(bounded_id)', 'bounded_id'); + $row_count = (int) $query->execute()->fetchField(); + + $average_row_size = $size / $row_count; + + $desired_reduction = $size - static::$boundedSizeMegabytes; + $estimated_number_of_rows_to_delete = round($desired_reduction / $average_row_size); + + // Note that we cannot use $this->connection->delete() because that does not support order by … + $this->connection->query('DELETE FROM {' . $this->bin . '} ORDER BY bounded_id ASC LIMIT ' . $estimated_number_of_rows_to_delete) + ->execute(); + } + } + else { + // @todo what about non-mysql??? + } + $this->connection->delete($this->bin) ->condition('expire', Cache::PERMANENT, '<>') ->condition('expire', REQUEST_TIME, '<') @@ -469,11 +507,17 @@ public function schemaDefinition() { 'length' => 255, 'not null' => TRUE, ], + 'bounded_id' => [ + 'type' => 'serial', + ], ], 'indexes' => [ 'expire' => ['expire'], ], 'primary key' => ['cid'], + 'unique keys' => [ + 'bounded_id' => ['bounded_id'], + ] ]; return $schema; } diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 90e6291..e0c9633 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -1965,3 +1965,14 @@ function system_update_8400(&$sandbox) { $sandbox['#finished'] = $sandbox['current'] == $sandbox['max']; } + +/** + * Delete all cache_* tables. They are recreated on demand with the new schema. + */ +function system_update_8401() { + foreach (\Drupal\Core\Cache\Cache::getBins() as $bin => $cache_backend) { + if ($cache_backend instanceof \Drupal\Core\Cache\DatabaseBackend) { + db_drop_table("cache_$bin"); + } + } +}