Skip to content

Commit

Permalink
✨ dir inventory cache
Browse files Browse the repository at this point in the history
Signed-off-by: bnomei <b@bnomei.com>
  • Loading branch information
bnomei committed Jun 16, 2024
1 parent 7e567f4 commit eed4259
Show file tree
Hide file tree
Showing 9 changed files with 1,625 additions and 892 deletions.
45 changes: 16 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,22 @@ return [
'bnomei.boost.patch.files' => true, // default: true
```

### Directories Inventory Cache (experimental)

Starting with version 5.0 the plugin will cache the inventory of directories. For that to be possible it will patch the `Kirby\Filesystem\Dir` class. It will automatically flush the cache if you edit pages in the panel, just like the core [pages cache](https://getkirby.com/docs/reference/system/options/cache). It's enabled by default but you can disable it if you want to like the following.

**index.php**
```php
// after bootstrap and before kirby runs
\Bnomei\BoostDirInventory::singleton(['enabled' => false]);
```

You could also flush the cache manually.

```php
\Bnomei\BoostDirInventory::singleton()->flush();
```

### Pages Field Alternative

This plugin provided a pages field alternative based on the multiselect field and optimized for performance.
Expand Down Expand Up @@ -264,35 +280,6 @@ var_dump(\Bnomei\CacheBenchmark::run($caches, 1, site()->index()->count())); //

But do not take my word for it. Download the plugin, set realistic benchmark options and run the benchmark on your production server.

### Interactive Demo

I created an interactive demo to compare various cache drivers and prove how much your website can be boosted. It kind of ended up as a love-letter to the <a class="underline" href="https://github.com/getkirby/kql">KQL Plugin</a> as well. You can find the benchmark and interactive demos here:

- [Benchmark with all Drivers](https://kirby3-boost.bnomei.com)
- [Demo using PHP Cache Driver](https://kirby3-boost-php.bnomei.com)
- [Demo using APCu Cache Driver](https://kirby3-boost-apcu.bnomei.com)
- [Demo using MySQL Cache Driver](https://kirby3-boost-mysql.bnomei.com)
- [Demo using Null Cache Driver](https://kirby3-boost-null.bnomei.com). This setup behaves like having the boost plugin NOT active at all.
- [Demo using Redis Cache Driver](https://kirby3-boost-redis.bnomei.com)
- [Demo using SQLite Cache Driver](https://kirby3-boost-sqlite.bnomei.com)

### Headless Demo

You can either use this interactive playground or a tool like HTTPie, Insomnia, PAW or Postman to connect to the public API of the demos. Queries are sent to the public API endpoint of the <a class="underline" href="https://github.com/getkirby/kql">KQL Plugin</a>. This means you can compare response times between cache drivers easily.

**HTTPie examples**
```shell
# get benchmark comparing the cachedrivers
http POST https://kirby3-boost.bnomei.com/benchmark --json

# get boostmark for a specific cache driver
http POST https://kirby3-boost-apcu.bnomei.com/boostmark --json

# compare apcu and sqlite
http POST https://kirby3-boost-apcu.bnomei.com/api/query -a api@kirby3-boost.bnomei.com:kirby3boost < myquery.json
http POST https://kirby3-boost-sqlite.bnomei.com/api/query -a api@kirby3-boost.bnomei.com:kirby3boost < myquery.json
```

### Config

Once you know which driver you want to use you can set the plugin cache options.
Expand Down
143 changes: 143 additions & 0 deletions classes/BoostDirInventory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
<?php

namespace Bnomei;

use Kirby\Filesystem\Dir;
use Kirby\Toolkit\A;
use ReflectionClass;

class BoostDirInventory
{
private ?array $data;

private bool $enabled;

private bool $isDirty;

public function __construct(array $options = [])
{
$this->enabled = A::get($options, 'enabled', true);
$this->isDirty = false;
$this->data = [];

if ($this->enabled) {
self::patchDirClass();

if (file_exists($this->file())) {
// $this->data = file_exists($this->file()) ? json_decode(file_get_contents($this->file()), true) : [];
$this->data = include $this->file();
}
}
}

public function __destruct()
{
if (! $this->isDirty || ! $this->enabled) {
return;
}

// file_put_contents($this->file(), json_encode($this->data, JSON_PRETTY_PRINT));
file_put_contents(
$this->file(),
'<?php'.PHP_EOL.' return '.var_export($this->data, true).';',
LOCK_EX
) !== false;
if (function_exists('opcache_invalidate')) {
opcache_invalidate($this->file());
}
}

public function file(): string
{
$cache = BoostCache::singleton();
if ($cache && method_exists($cache, 'root')) {
return $cache->root().'/boost-dir-inventory.cache.php';
}

return __DIR__.'/../boost-dir-inventory.cache.php';
}

public function get($key): ?array
{
if (! $this->enabled) {
return null;
}

$key = $this->key($key);

return A::get($this->data, $key);
}

public function set($key, ?array $input = null): void
{
if (! $this->enabled) {
return;
}

$this->isDirty = true;
$key = $this->key($key);
$this->data[$key] = $input;
}

public static function flush(): void
{
$instance = static::singleton();
if (file_exists($instance->file())) {
unlink($instance->file());
}

$instance->data = [];
$instance->isDirty = true;
}

public static function singleton(array $options = []): self
{
static $instance;

return $instance ?: $instance = new self($options);
}

private function key($key): string
{
return is_array($key) ? hash('xxh3', print_r($key, true)) : $key;
}

public static function patchDirClass(): void
{
$reflection = new ReflectionClass(Dir::class);
$file = $reflection->getFileName();

$content = file_get_contents($file);
$head = <<<'CODE'
$items = static::read($dir, $contentIgnore);
CODE;

$head_new = <<<'CODE'
$cacheKey = func_get_args();
if ($cache = \Bnomei\BoostDirInventory::singleton()->get($cacheKey)) {
return $cache;
}
$items = static::read($dir, $contentIgnore);
CODE;
$foot = <<<'CODE'
return $inventory;
}
CODE;
$foot_new = <<<'CODE'
\Bnomei\BoostDirInventory::singleton()->set($cacheKey, $inventory);
return $inventory;
}
CODE;
if (strpos($content, $head_new) !== false) {
return;
}
$content = str_replace($head, $head_new, $content);
$content = str_replace($foot, $foot_new, $content);
file_put_contents($file, $content);

if (function_exists('opcache_invalidate')) {
opcache_invalidate($file);
}
}
}
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "bnomei/kirby3-boost",
"type": "kirby-plugin",
"version": "4.0.4",
"version": "5.0.0",
"description": "Boost the speed of Kirby by having content files of files/pages/users cached, with fast lookup based on uuid.",
"license": "MIT",
"authors": [
Expand Down Expand Up @@ -51,8 +51,8 @@
},
"require-dev": {
"getkirby/cms": "^4.0.0-beta.2",
"larastan/larastan": "^2.9",
"laravel/pint": "^1.13",
"nunomaduro/larastan": "^2.6",
"pestphp/pest": "^2.24",
"pestphp/pest-plugin-type-coverage": "^2.4"
},
Expand Down
Loading

0 comments on commit eed4259

Please sign in to comment.