function CacheCollectorTest::testUpdateCacheConcurrentWriteSameTimestamp

Same name and namespace in other branches
  1. main core/tests/Drupal/Tests/Core/Cache/CacheCollectorTest.php \Drupal\Tests\Core\Cache\CacheCollectorTest::testUpdateCacheConcurrentWriteSameTimestamp()

Tests that a concurrent write in the same millisecond is detected.

The previous implementation compared the cache item's millisecond-precision creation timestamp, which could not distinguish two writes that happened within the same millisecond. The content fingerprint detects this case: here the item loaded at the start of the request and the item present when the cache is written share an identical creation timestamp but contain different data, so the write must back out rather than overwrite a concurrent request's data.

File

core/tests/Drupal/Tests/Core/Cache/CacheCollectorTest.php, line 390

Class

CacheCollectorTest
Tests Drupal\Core\Cache\CacheCollector.

Namespace

Drupal\Tests\Core\Cache

Code

public function testUpdateCacheConcurrentWriteSameTimestamp() : void {
  $this->setUpMockCacheBackend();
  $this->setUpMockLockBackend();
  $key = $this->randomMachineName();
  $value = $this->randomMachineName();
  $created = (int) $_SERVER['REQUEST_TIME'];
  $this->collector
    ->setCacheMissData($key, $value);
  $this->cacheBackend
    ->expects($this->exactly(2))
    ->method('get')
    ->with($this->cid)
    ->willReturnOnConsecutiveCalls((object) [
    'data' => [
      'shared' => 'original',
    ],
    'created' => $created,
  ], (object) [
    'data' => [
      'shared' => 'concurrent',
    ],
    'created' => $created,
  ]);
  $this->collector
    ->get($key);
  $this->lock
    ->expects($this->once())
    ->method('acquire')
    ->with($this->cid . ':Drupal\\Core\\Cache\\CacheCollector')
    ->willReturn(TRUE);
  // The data differs, so even though the creation timestamps are identical
  // the write is detected as a conflict: the cache is neither overwritten nor
  // deleted.
  $this->cacheBackend
    ->expects($this->never())
    ->method('set');
  $this->cacheBackend
    ->expects($this->never())
    ->method('delete');
  $this->lock
    ->expects($this->once())
    ->method('release')
    ->with($this->cid . ':Drupal\\Core\\Cache\\CacheCollector');
  // Destruct the object to trigger the update data process.
  $this->collector
    ->destruct();
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.