Skip to content
This repository was archived by the owner on May 18, 2025. It is now read-only.

Commit f40a4cf

Browse files
committed
📝 cache driver support better explained
✨ better benchmark and cache driver support 🐛 bolt cache
1 parent 63af2ba commit f40a4cf

File tree

15 files changed

+182
-102
lines changed

15 files changed

+182
-102
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
/tests/site/accounts
1515
/tests/site/cache
1616
/tests/site/sessions
17+
/tests/site/plugins/kirby3-sqlite-cachedriver
18+
/tests/site/plugins/kirby3-redis-cachedriver
1719

1820
# files of Composer dependencies that are not needed for the plugin
1921
/vendor/**/.*

README.md

Lines changed: 68 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ If you have a lot of page objects (1000+) with or without relations to each othe
1616
## How does this plugin work?
1717

1818
- It caches all content files and keeps the cache up to date when you add or modify content. This cache will be used when constructing page objects making everything that involves page objects faster (even the Panel).
19+
- It provides a benchmark to help you decide which cachedriver to use.
1920
- It can add an unique ID for page objects that can be used create relations that do not break even if the slug or directory of a page object changes.
2021
- It provides a very fast lookup for page objects via id, diruri or the unique id.
2122
- It provides you with a tiny-url for page objects that have an unique id.
@@ -82,53 +83,87 @@ $page = boost($boostId); // will use fastest internally
8283
$page = modified($somePageId);
8384
```
8485

85-
## Memcached
86+
## Cache
8687

87-
This plugin uses the PHP Memcached extension for optimal performance. Not Memcache, but Memcached. It can also use Kirbys default file cache driver.
88+
### Limitations
8889

89-
### Memcached Setup
90+
How much and if you gain anything regarding performance depends on the hardware. All your content files must fit within the memory limitation. If you run into errors consider increasing the server settings or choose a different cache driver.
9091

91-
**site/config/config.php**
92-
```php
93-
<?php
92+
| Defaults for | Memcached | APCu | Redis | SQLite |
93+
|----|----|----|----|----|
94+
| max memory size | 64MB | 32MB | 0 (none) | 0 (none) |
95+
| size of key/value pair | 1MB | 4MB | 512MB | 0 (none) |
9496

95-
return [
96-
// other options
97-
// use memached
98-
// defaults...
99-
'bnomei.boost.cacheType' => 'memcached', // file
100-
'bnomei.boost.memcached' => [
101-
'host' => '127.0.0.1',
102-
'port' => 11211,
103-
'prefix' => 'boost',
104-
'expire' => 0,
105-
'enforce' => true,
106-
],
107-
];
108-
```
97+
Kirby has [built in support](https://getkirby.com/docs/reference/system/options/cache#cache-driver) for File, Apcu, Memcached and Memory. I have created additional cache drivers for [SQLite](https://github.com/bnomei/kirby3-sqlite-cachedriver) and [Redis](https://github.com/bnomei/kirby3-redis-cachedriver).
10998

11099
### Benchmark
111100

112-
The included benchmark can help you make an educated guess if on your server the memcached cache driver is faster than the file cache driver. This will create and remove 2000 items cached.
101+
The included benchmark can help you make an educated guess which is the faster cache driver. The only way to make sure is measuring in production.
102+
Be aware that this will create and remove 2000 items cached. The benchmark will try to perform as many get operations within given timeframe (default 2 seconds per cache). The higher results are better.
113103

114104
```php
115-
// see numbers
116-
echo \Bnomei\CacheBenchmark::file(2) . PHP_EOL; // seconds
117-
echo \Bnomei\CacheBenchmark::memcached(2) . PHP_EOL; // seconds
118-
// or just
119-
echo \Bnomei\CacheBenchmark::fastest(2) . PHP_EOL;
105+
// use helpers to generate caches to compare
106+
// rough performance level is based on my tests
107+
$caches = [
108+
\Bnomei\BoostCache::memory(), // 100
109+
\Bnomei\BoostCache::sqlite(), // 80
110+
\Bnomei\BoostCache::apcu(), // 40
111+
\Bnomei\BoostCache::redis(), // 30
112+
\Bnomei\BoostCache::file(), // 10
113+
\Bnomei\BoostCache::memcached(), // 4
114+
];
115+
// run the benchmark
116+
var_dump(\Bnomei\CacheBenchmark::run($caches));
120117
```
121118

122-
### Limitations
119+
Memory Cache Driver will probably perform best but caches in memory only for current request and that is not really usefull for this plugin. SQLite Cache Driver will perform very well since everything will be in one file and I optimized the read/write with [pragmas](https://github.com/bnomei/kirby3-sqlite-cachedriver/blob/bc3ccf56cefff7fd6b0908573ce2b4f09365c353/index.php#L20) and [wal journal mode](https://github.com/bnomei/kirby3-sqlite-cachedriver/blob/bc3ccf56cefff7fd6b0908573ce2b4f09365c353/index.php#L34).
120+
121+
But do not take my word for it. Run the benchmark on your production server.
122+
123+
### Config
124+
125+
Once you know which driver you want to use you can set the plugin cache options.
123126

124-
How much and if you gain anything regarding performance depends on the hardware. But on most production servers reading data from RAM (via TCP/IP) should be faster than reading a lot of files (even from SSD disks).
127+
**site/config/config.php**
128+
```php
129+
<?php
130+
131+
return [
132+
// other options
133+
134+
// default is file cache driver
135+
'bnomei.boost.cache' => [
136+
'type' => 'file',
137+
'prefix' => 'boost',
138+
],
139+
140+
// example memached
141+
'bnomei.boost.cache' => [
142+
'type' => 'memcached',
143+
'prefix' => 'boost',
144+
'host' => '127.0.0.1',
145+
'port' => 11211,
146+
],
125147

126-
All your content files must fit within the memory limitation of Memcached Server. If you run into errors consider increasing the server settings.
148+
// example sqlite
149+
// https://github.com/bnomei/kirby3-sqlite-cachedriver
150+
'bnomei.boost.cache' => [
151+
'type' => 'sqlite',
152+
'prefix' => 'boost',
153+
],
127154

128-
| Defaults for | Memcached |
129-
|----|----|
130-
| max memory size | 64MB
131-
| size of key/value pair | 1MB |
155+
// example redis
156+
// https://github.com/bnomei/kirby3-redis-cachedriver
157+
'bnomei.boost.cache' => [
158+
'type' => 'redis',
159+
'prefix' => 'boost',
160+
'host' => function() { return env('REDIS_HOST'); },
161+
'port' => function() { return env('REDIS_PORT'); },
162+
'database' => function() { return env('REDIS_DATABASE'); },
163+
'password' => function() { return env('REDIS_PASSWORD'); },
164+
],
165+
];
166+
```
132167

133168
## Tiny-URL
134169

classes/Bolt.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,12 @@ public function lookup(string $id, bool $cache = true): ?Page
6363
{
6464
$lookup = A::get(static::$idToPage, $id);
6565
if (!$lookup && $cache && static::cache()) {
66-
if ($diruri = static::cache()->get('bolt-' . md5($id))) {
66+
if ($diruri = static::cache()->get(crc32($id) . '-bolt')) {
6767
if ($page = $this->findByID($diruri, false)) {
6868
$this->pushLookup($id, $page);
6969
$lookup = $page;
7070
} else {
71-
$this->cache()->remove(md5($id) . '-bolt');
71+
$this->cache()->remove(crc32($id) . '-bolt');
7272
}
7373
}
7474
}
@@ -78,7 +78,7 @@ public function lookup(string $id, bool $cache = true): ?Page
7878
public function pushLookup(string $id, Page $page): void
7979
{
8080
static::$idToPage[$id] = $page;
81-
$this->cache()->set(md5($id) . '-bolt', $page->diruri(), option('bnomei.boost.bolt.expire'));
81+
$this->cache()->set(crc32($id) . '-bolt', $page->diruri(), option('bnomei.boost.expire'));
8282
}
8383

8484
public static function toArray(): array

classes/BoostCache.php

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,76 @@
66

77
use Kirby\Cache\Cache;
88
use Kirby\Cache\FileCache;
9+
use Kirby\Cache\ApcuCache;
10+
use Kirby\Cache\MemoryCache;
911
use Kirby\Cache\MemCached;
10-
use Kirby\Cms\Page;
1112

1213
final class BoostCache
1314
{
1415
private static $singleton;
1516
public static function singleton(): Cache
1617
{
1718
if (! self::$singleton) {
18-
$config = [
19-
'host' => option('bnomei.boost.memcached.host'),
20-
'port' => option('bnomei.boost.memcached.port'),
21-
'prefix' => option('bnomei.boost.memcached.prefix'),
22-
];
23-
foreach (array_keys($config) as $key) {
24-
if (!is_string($config[$key]) && is_callable($config[$key])) {
25-
$config[$key] = $config[$key]();
26-
}
27-
}
28-
if (class_exists('Memcached') && option('bnomei.boost.cacheType') === 'memcached') {
29-
self::$singleton = new Memcached($config);
30-
} else {
31-
self::$singleton = kirby()->cache('bnomei.boost');
32-
}
19+
self::$singleton = kirby()->cache('bnomei.boost');
3320
}
3421
if (option('debug')) {
3522
self::$singleton->flush();
3623
}
3724

3825
return self::$singleton;
3926
}
27+
28+
public static function file(array $options = []): FileCache
29+
{
30+
return new FileCache(array_merge([
31+
'root' => kirby()->cache('bnomei.boost')->root(),
32+
'prefix' => 'boost',
33+
], $options));
34+
}
35+
36+
public static function apcu(array $options = []): ApcuCache
37+
{
38+
return new ApcuCache(array_merge([
39+
'prefix' => 'boost',
40+
], $options));
41+
}
42+
43+
public static function memcached(array $options = []): MemCached
44+
{
45+
return new Memcached(array_merge([
46+
'prefix' => 'boost',
47+
'host' => '127.0.0.1',
48+
'port' => 11211,
49+
]));
50+
}
51+
52+
public static function memory(array $options = []): MemoryCache
53+
{
54+
return new MemoryCache(array_merge([
55+
'prefix' => 'boost',
56+
], $options));
57+
}
58+
59+
public static function sqlite(array $options = [])//: Cache
60+
{
61+
if (class_exists('Bnomei\\SQLiteCache')) {
62+
$feather = \Bnomei\SQLiteCache::singleton(array_merge([
63+
'prefix' => 'boost',
64+
], $options));
65+
return $feather;
66+
}
67+
return null;
68+
}
69+
70+
public static function redis(array $options = [])//: Cache
71+
{
72+
if (class_exists('Bnomei\\Redis')) {
73+
return new \Bnomei\Redis(array_merge([
74+
'prefix' => 'boost',
75+
'host' => '127.0.0.1',
76+
'port' => 6379,
77+
], $options));
78+
}
79+
return null;
80+
}
4081
}

classes/BoostIndex.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ final class BoostIndex
2020

2121
public function __construct()
2222
{
23-
$this->expire = option('bnomei.boost.index.expire', 0);
23+
$this->expire = option('bnomei.boost.expire', 0);
2424
$this->isDirty = false;
2525

2626
$this->index = $this->read();
@@ -145,7 +145,7 @@ public static function tinyurl($id): string
145145

146146
public static function modified(string $id): ?int
147147
{
148-
return BoostCache::singleton()->get(md5($id) . '-modified');
148+
return BoostCache::singleton()->get(crc32($id) . '-modified');
149149
}
150150

151151
public static function page(string $id): ?Page

classes/CacheBenchmark.php

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
final class CacheBenchmark
1212
{
13-
private static function benchmark(int $seconds, $cache, $count = 2000, $contentLength = 128): int
13+
private static function benchmark($cache, int $seconds = 2, $count = 2000, $contentLength = 128): int
1414
{
1515
for ($i = 0; $i < $count; $i++) {
1616
$cache->set('CacheBenchmark-' . $i, Str::random($contentLength), 0);
@@ -34,29 +34,17 @@ private static function benchmark(int $seconds, $cache, $count = 2000, $contentL
3434
return count($values);
3535
}
3636

37-
public static function file(int $seconds): int
37+
public static function run(array $caches = [], int $seconds = 2, $count = 2000, $contentLength = 128): array
3838
{
39-
$cache = new FileCache([
40-
'root' => kirby()->cache('bnomei.boost')->root(),
41-
]);
42-
return static::benchmark($seconds, $cache);
43-
}
44-
45-
public static function memcached(int $seconds): int
46-
{
47-
$cache = new Memcached(option('bnomei.boost.memcached'));
48-
return static::benchmark($seconds, $cache);
49-
}
39+
$caches = $caches ?? [ BoostCache::file() ];
5040

51-
public static function fastest(int $seconds = 2): string
52-
{
53-
$benchmarks = [
54-
'file' => static::file($seconds),
55-
'memcached' => static::memcached($seconds),
56-
];
57-
foreach ($benchmarks as $key => $val) {
58-
$lastkey = $key;
41+
$benchmarks = [];
42+
foreach ($caches as $cache) {
43+
if (!$cache) continue;
44+
$benchmarks[$cache::class] = static::benchmark($cache, $seconds, $count, $contentLength);
5945
}
60-
return $lastkey;
46+
47+
asort($benchmarks);
48+
return array_reverse($benchmarks, true); // DESC
6149
}
6250
}

classes/PageHasBoost.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public function isContentMemcached(string $languageCode = null): bool
5151

5252
public function memcachedKey(string $languageCode = null): string
5353
{
54-
$key = md5($this->id());
54+
$key = strval(crc32($this->id()));
5555
if (!$languageCode) {
5656
$languageCode = kirby()->languages()->count() ? kirby()->language()->code() : null;
5757
if ($languageCode) {
@@ -121,13 +121,13 @@ public function writeContentCache(array $data, string $languageCode = null): boo
121121
$cache->set(
122122
$this->memcachedKey($languageCode).'-modified',
123123
$this->modified(),
124-
option('bnomei.boost.memcached.expire')
124+
option('bnomei.boost.expire')
125125
);
126126

127127
return $cache->set(
128128
$this->memcachedKey($languageCode),
129129
$data,
130-
option('bnomei.boost.memcached.expire')
130+
option('bnomei.boost.expire')
131131
);
132132
}
133133

composer.json

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "bnomei/kirby3-boost",
33
"type": "kirby-plugin",
4-
"version": "1.0.0",
4+
"version": "1.1.0",
55
"description": "Boost the speed of Kirby by having content files of pages (mem-)cached, with automatic unique ID, fast lookup and Tiny-URL.",
66
"license": "MIT",
77
"authors": [
@@ -25,7 +25,12 @@
2525
"performance",
2626
"tiny-url",
2727
"memcache",
28-
"memcached"
28+
"memcached",
29+
"memory",
30+
"sqlite",
31+
"redis",
32+
"apcu",
33+
"file"
2934
],
3035
"autoload": {
3136
"psr-4": {
@@ -66,5 +71,9 @@
6671
},
6772
"extra": {
6873
"kirby-cms-path": "tests/kirby"
74+
},
75+
"suggest": {
76+
"bnomei/kirby3-redis-cachedriver": "Redis based Cache-Driver",
77+
"bnomei/kirby3-sqlite-cachedriver": "SQLite based Cache-Driver"
6978
}
7079
}

composer.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)