Skip to content
Snippets Groups Projects
Commit 37d64e23 authored by catch's avatar catch
Browse files

Issue #3327118 by Defcon0, acbramley, iSampo, poker10, Nitin shrivastava,...

Issue #3327118 by Defcon0, acbramley, iSampo, poker10, Nitin shrivastava, catch, fgm, longwave: Chunk multiple cache sets into groups of 100 to avoid OOM/max_allowed_packet issues

(cherry picked from commit a04bdb08)
parent ce444178
Branches
Tags
18 merge requests!11628Update file MediaLibraryWidget.php,!7564Revert "Issue #3364773 by roshnichordiya, Chris Matthews, thakurnishant_06,...,!5752Issue #3275828 by joachim, quietone, bradjones1, Berdir: document the reason...,!5627Issue #3261805: Field not saved when change of 0 on string start,!5427Issue #3338518: send credentials in ajax if configured in CORS settings.,!5395Issue #3387916 by fjgarlin, Spokje: Each GitLab job exposes user email,!5217Issue #3386607 by alexpott: Improve spell checking in commit-code-check.sh,!5064Issue #3379522 by finnsky, Gauravvvv, kostyashupenko, smustgrave, Chi: Revert...,!5040SDC ComponentElement: Transform slots scalar values to #plain_text instead of throwing an exception,!4958Issue #3392147: Whitelist IP for a Ban module.,!4894Issue #3280279: Add API to allow sites to opt in to upload SVG images in CKEditor 5,!4857Issue #3336994: StringFormatter always displays links to entity even if the user in context does not have access,!4856Issue #3336994: StringFormatter always displays links to entity even if the user in context does not have access,!4788Issue #3272985: RSS Feed header reverts to text/html when cached,!3679Issue #115801: Allow password on registration without disabling e-mail verification,!3106Issue #3017548: "Filtered HTML" text format does not support manual teaser break (<!--break-->),!925Issue #2339235: Remove taxonomy hard dependency on node module,!872Draft: Issue #3221319: Race condition when creating menu links and editing content deletes menu links
Pipeline #19817 canceled
Pipeline: drupal

#19819

    ......@@ -32,6 +32,11 @@ class DatabaseBackend implements CacheBackendInterface {
    */
    const MAXIMUM_NONE = -1;
    /**
    * The chunk size for inserting cache entities.
    */
    const MAX_ITEMS_PER_CACHE_SET = 100;
    /**
    * The maximum number of rows that this cache bin table is allowed to store.
    *
    ......@@ -215,62 +220,68 @@ public function setMultiple(array $items) {
    * @see \Drupal\Core\Cache\CacheBackendInterface::setMultiple()
    */
    protected function doSetMultiple(array $items) {
    $values = [];
    foreach ($items as $cid => $item) {
    $item += [
    'expire' => CacheBackendInterface::CACHE_PERMANENT,
    'tags' => [],
    ];
    assert(Inspector::assertAllStrings($item['tags']), 'Cache Tags must be strings.');
    $item['tags'] = array_unique($item['tags']);
    // Sort the cache tags so that they are stored consistently in the DB.
    sort($item['tags']);
    $fields = [
    'cid' => $this->normalizeCid($cid),
    'expire' => $item['expire'],
    'created' => round(microtime(TRUE), 3),
    'tags' => implode(' ', $item['tags']),
    'checksum' => $this->checksumProvider->getCurrentChecksum($item['tags']),
    ];
    // Avoid useless writes.
    if ($fields['checksum'] === CacheTagsChecksumInterface::INVALID_CHECKSUM_WHILE_IN_TRANSACTION) {
    continue;
    }
    // Chunk the items as the database might not be able to receive thousands
    // of items in a single query.
    $chunks = array_chunk($items, self::MAX_ITEMS_PER_CACHE_SET, TRUE);
    foreach ($chunks as $chunk_items) {
    $values = [];
    foreach ($chunk_items as $cid => $item) {
    $item += [
    'expire' => CacheBackendInterface::CACHE_PERMANENT,
    'tags' => [],
    ];
    assert(Inspector::assertAllStrings($item['tags']), 'Cache Tags must be strings.');
    $item['tags'] = array_unique($item['tags']);
    // Sort the cache tags so that they are stored consistently in the DB.
    sort($item['tags']);
    $fields = [
    'cid' => $this->normalizeCid($cid),
    'expire' => $item['expire'],
    'created' => round(microtime(TRUE), 3),
    'tags' => implode(' ', $item['tags']),
    'checksum' => $this->checksumProvider->getCurrentChecksum($item['tags']),
    ];
    // Avoid useless writes.
    if ($fields['checksum'] === CacheTagsChecksumInterface::INVALID_CHECKSUM_WHILE_IN_TRANSACTION) {
    continue;
    }
    if (!is_string($item['data'])) {
    $fields['data'] = serialize($item['data']);
    $fields['serialized'] = 1;
    if (!is_string($item['data'])) {
    $fields['data'] = serialize($item['data']);
    $fields['serialized'] = 1;
    }
    else {
    $fields['data'] = $item['data'];
    $fields['serialized'] = 0;
    }
    $values[] = $fields;
    }
    else {
    $fields['data'] = $item['data'];
    $fields['serialized'] = 0;
    // If all $items were useless writes, we may end up with zero writes.
    if (count($values) === 0) {
    return;
    }
    $values[] = $fields;
    }
    // If all $items were useless writes, we may end up with zero writes.
    if (empty($values)) {
    return;
    }
    // Use an upsert query which is atomic and optimized for multiple-row
    // merges.
    $query = $this->connection
    ->upsert($this->bin)
    ->key('cid')
    ->fields(['cid', 'expire', 'created', 'tags', 'checksum', 'data', 'serialized']);
    foreach ($values as $fields) {
    // Only pass the values since the order of $fields matches the order of
    // the insert fields. This is a performance optimization to avoid
    // unnecessary loops within the method.
    $query->values(array_values($fields));
    }
    // Use an upsert query which is atomic and optimized for multiple-row
    // merges.
    $query = $this->connection
    ->upsert($this->bin)
    ->key('cid')
    ->fields(['cid', 'expire', 'created', 'tags', 'checksum', 'data', 'serialized']);
    foreach ($values as $fields) {
    // Only pass the values since the order of $fields matches the order of
    // the insert fields. This is a performance optimization to avoid
    // unnecessary loops within the method.
    $query->values(array_values($fields));
    $query->execute();
    }
    $query->execute();
    }
    /**
    ......
    ......@@ -53,6 +53,15 @@ public function testSetGet() {
    $cached_value_short = $this->randomMachineName();
    $backend->set($cid_short, $cached_value_short);
    $this->assertSame($cached_value_short, $backend->get($cid_short)->data, "Backend contains the correct value for short, non-ASCII cache id.");
    // Set multiple items to test exceeding the chunk size.
    $backend->deleteAll();
    $items = [];
    for ($i = 0; $i <= DatabaseBackend::MAX_ITEMS_PER_CACHE_SET; $i++) {
    $items["test$i"]['data'] = $i;
    }
    $backend->setMultiple($items);
    $this->assertSame(DatabaseBackend::MAX_ITEMS_PER_CACHE_SET + 1, $this->getNumRows());
    }
    /**
    ......
    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Please register or to comment