Skip to content

Commit

Permalink
Bulk write implementation (#73)
Browse files Browse the repository at this point in the history
  • Loading branch information
KminekMatej authored and dg committed Mar 10, 2024
1 parent a84b4b6 commit 3053707
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 1 deletion.
28 changes: 28 additions & 0 deletions src/Caching/BulkWriter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/

declare(strict_types=1);

namespace Nette\Caching;


/**
* Cache storage with a bulk write support.
*/
interface BulkWriter
{
/**
* Writes to cache in bulk.
* @param array{string, mixed} $items
*/
function bulkWrite(array $items, array $dependencies): void;

/**
* Removes multiple items from cache
*/
function bulkRemove(array $keys): void;
}
39 changes: 39 additions & 0 deletions src/Caching/Cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,45 @@ public function save(mixed $key, mixed $data, ?array $dependencies = null): mixe
}


/**
* Writes multiple items into cache
*/
public function bulkSave(array $items, ?array $dependencies = null): void
{
$write = $remove = [];

if (!$this->storage instanceof BulkWriter) {
foreach ($items as $key => $data) {
$this->save($key, $data, $dependencies);
}
return;
}

$dependencies = $this->completeDependencies($dependencies);
if (isset($dependencies[self::Expire]) && $dependencies[self::Expire] <= 0) {
$this->storage->bulkRemove(array_map(fn($key) => $this->generateKey($key), array_keys($items)));
return;
}

foreach ($items as $key => $data) {
$key = $this->generateKey($key);
if ($data === null) {
$remove[] = $key;
} else {
$write[$key] = $data;
}
}

if ($remove) {
$this->storage->bulkRemove($remove);
}

if ($write) {
$this->storage->bulkWrite($write, $dependencies);
}
}


private function completeDependencies(?array $dp): array
{
// convert expire into relative amount of seconds
Expand Down
44 changes: 43 additions & 1 deletion src/Caching/Storages/MemcachedStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
/**
* Memcached storage using memcached extension.
*/
class MemcachedStorage implements Nette\Caching\Storage, Nette\Caching\BulkReader
class MemcachedStorage implements Nette\Caching\Storage, Nette\Caching\BulkReader, Nette\Caching\BulkWriter
{
/** @internal cache structure */
private const
Expand Down Expand Up @@ -168,12 +168,54 @@ public function write(string $key, $data, array $dp): void
}


public function bulkWrite(array $items, array $dp): void
{
if (isset($dp[Cache::Items])) {
throw new Nette\NotSupportedException('Dependent items are not supported by MemcachedStorage.');
}

$meta = $records = [];
$expire = 0;
if (isset($dp[Cache::Expire])) {
$expire = (int) $dp[Cache::Expire];
if (!empty($dp[Cache::Sliding])) {
$meta[self::MetaDelta] = $expire; // sliding time
}
}

if (isset($dp[Cache::Callbacks])) {
$meta[self::MetaCallbacks] = $dp[Cache::Callbacks];
}

foreach ($items as $key => $meta[self::MetaData]) {
$key = urlencode($this->prefix . $key);
$records[$key] = $meta;

if (isset($dp[Cache::Tags]) || isset($dp[Cache::Priority])) {
if (!$this->journal) {
throw new Nette\InvalidStateException('CacheJournal has not been provided.');
}

$this->journal->write($key, $dp);
}
}

$this->memcached->setMulti($records, $expire);
}


public function remove(string $key): void
{
$this->memcached->delete(urlencode($this->prefix . $key), 0);
}


public function bulkRemove(array $keys): void
{
$this->memcached->deleteMulti(array_map(fn($key) => urlencode($this->prefix . $key), $keys), 0);
}


public function clean(array $conditions): void
{
if (!empty($conditions[Cache::All])) {
Expand Down
45 changes: 45 additions & 0 deletions tests/Caching/Cache.bulkSave.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

/**
* Test: Nette\Caching\Cache save().
*/

declare(strict_types=1);

use Nette\Caching\Cache;
use Tester\Assert;

require __DIR__ . '/../bootstrap.php';
require __DIR__ . '/Cache.php';


test('storage without bulk write support', function () {
$storage = new TestStorage;
$cache = new Cache($storage, 'ns');
$cache->bulkSave([1 => 'value1', 2 => 'value2']);

$data = $cache->bulkLoad([1, 2]);
Assert::same('value1', $data[1]['data']);
Assert::same('value2', $data[2]['data']);
});

test('storage with bulk write support', function () {
$storage = new BulkWriteTestStorage;
$cache = new Cache($storage, 'ns');
$cache->bulkSave([1 => 'value1', 2 => 'value2']);

$data = $cache->bulkLoad([1, 2]);
Assert::same('value1', $data[1]['data']);
Assert::same('value2', $data[2]['data']);
});

test('dependencies', function () {
$storage = new BulkWriteTestStorage;
$cache = new Cache($storage, 'ns');
$dependencies = [Cache::Tags => ['tag']];
$cache->bulkSave([1 => 'value1', 2 => 'value2'], $dependencies);

$data = $cache->bulkLoad([1, 2]);
Assert::same($dependencies, $data[1]['dependencies']);
Assert::same($dependencies, $data[2]['dependencies']);
});
31 changes: 31 additions & 0 deletions tests/Caching/Cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
declare(strict_types=1);

use Nette\Caching\BulkReader;
use Nette\Caching\BulkWriter;
use Nette\Caching\Storage;

class TestStorage implements Storage
Expand Down Expand Up @@ -55,3 +56,33 @@ public function bulkRead(array $keys): array
return $result;
}
}

class BulkWriteTestStorage extends TestStorage implements BulkWriter
{
public function bulkRead(array $keys): array
{
$result = [];
foreach ($keys as $key) {
$data = $this->read($key);
if ($data !== null) {
$result[$key] = $data;
}
}

return $result;
}


public function bulkRemove(array $keys): void
{

}


public function bulkWrite($items, array $dp): void
{
foreach ($items as $key => $data) {
$this->write($key, $data, $dp);
}
}
}
36 changes: 36 additions & 0 deletions tests/Storages/Memcached.bulkWrite.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

/**
* Test: Nette\Caching\Storages\MemcachedStorage and bulkWrite
*/

declare(strict_types=1);

use Nette\Caching\Cache;
use Nette\Caching\Storages\MemcachedStorage;
use Nette\Caching\Storages\SQLiteJournal;
use Tester\Assert;

require __DIR__ . '/../bootstrap.php';


if (!MemcachedStorage::isAvailable()) {
Tester\Environment::skip('Requires PHP extension Memcached.');
}

Tester\Environment::lock('memcached-files', getTempDir());


$storage = new MemcachedStorage('localhost', 11211, '', new SQLiteJournal(getTempDir() . '/journal-memcached.s3db'));
$cache = new Cache($storage);

//standard
$cache->bulkSave(['foo' => 'bar']);
Assert::same(['foo' => 'bar', 'lorem' => null], $cache->bulkLoad(['foo', 'lorem']));

//tags
$dependencies = [Cache::Tags => ['tag']];
$cache->bulkSave(['foo' => 'bar'], $dependencies);
Assert::same(['foo' => 'bar'], $cache->bulkLoad(['foo']));
$cache->clean($dependencies);
Assert::same(['foo' => null], $cache->bulkLoad(['foo']));

0 comments on commit 3053707

Please sign in to comment.