From c59b8b92a261fec17b3e003c0cf84ff6ce5735fd Mon Sep 17 00:00:00 2001 From: Bruno Meilick Date: Sat, 5 Sep 2020 23:33:23 +0100 Subject: [PATCH] :sparkles: pragmas, transactions Signed-off-by: Bruno Meilick --- README.md | 1 - classes/SQLitePagesDatabase.php | 86 +++++++++++++++++++++++---------- composer.json | 2 +- composer.lock | 2 +- index.php | 39 ++++++++++++++- tests/content/home/default.txt | 4 +- 6 files changed, 101 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 177fe4b..3732520 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,6 @@ $checkIfContentFileIsSQLiteCached = $page->isSQLitePage(); // bool | bnomei.page-sqlite. | Default | Description | |---------------------------|----------------|---------------------------| | file | `callback` | | -| wal | `true` | sqlite WAL for faster IO | ## Dependencies diff --git a/classes/SQLitePagesDatabase.php b/classes/SQLitePagesDatabase.php index db83e23..ef21687 100644 --- a/classes/SQLitePagesDatabase.php +++ b/classes/SQLitePagesDatabase.php @@ -6,49 +6,68 @@ use Kirby\Cache\FileCache; use Kirby\Toolkit\A; +use Kirby\Toolkit\F; use Kirby\Toolkit\Obj; +use SQLite3; final class SQLitePagesDatabase { /** @var \SQLite3 */ private $database; + private $transactionsCount = 0; + private $options; public function __construct(array $options = []) { - $this->options = array_merge([ - 'file' => \option('bnomei.page-sqlite.file'), - ], $options); + $this->setOptions($options); + $this->loadDatabase(); - foreach ($this->options as $key => $call) { - if (!is_string($call) && is_callable($call) && in_array($key, ['file'])) { - $this->options[$key] = $call(); - } - } - - $target = $this->options['file']; - $this->database = new \SQLite3($target); - if (\SQLite3::version() >= 3007001 && \option('bnomei.page-sqlite.wal')) { - $this->database->exec("PRAGMA busy_timeout=1000"); - $this->database->exec("PRAGMA journal_mode = WAL"); - } $this->database->exec("CREATE TABLE IF NOT EXISTS pages (id TEXT primary key unique, modified_at INTEGER, data TEXT)"); - if (\option('debug')) { + if ($this->options['debug']) { $this->flush(); } + + $this->beginTransaction(); } public function __destruct() { - if (\SQLite3::version() >= 3007001 && \option('bnomei.page-sqlite.wal')) { - $this->database()->exec('PRAGMA main.wal_checkpoint(TRUNCATE);'); + $this->endTransaction(); + $this->applyPragmas('pragmas-destruct'); + $this->database->close(); + } + + private function loadDatabase() + { + $file = $this->options['file']; + try { + $this->database = new SQLite3($file); + } catch (\Exception $exception) { + F::remove($file); + F::remove($file . '-wal'); + F::remove($file . '-shm'); + $this->database = new SQLite3($file); + throw new \Exception($exception->getMessage()); + } + } + + private function applyPragmas(string $pragmas) + { + foreach ($this->options[$pragmas] as $pragma) { + $this->database->exec($pragma); } - $this->database()->close(); } - public function databaseFile(): string + private function beginTransaction() { - return $this->options['file']; + $this->database->exec("BEGIN TRANSACTION;"); + $this->transactionsCount++; + } + + private function endTransaction() + { + $this->database->exec("END TRANSACTION;"); } public function database(): \SQLite3 @@ -128,11 +147,6 @@ public function flush(): bool return $this->database->exec("DELETE FROM pages WHERE id != ''") !== false; } - public function root(): string - { - return realpath(__DIR__ . '/../') . '/cache'; - } - public static function cacheFolder(): string { $cache = kirby()->cache('bnomei.page-sqlite'); @@ -143,4 +157,24 @@ public static function cacheFolder(): string return kirby()->roots()->cache(); // @codeCoverageIgnoreEnd } + + private function setOptions(array $options): void + { + $this->options = array_merge([ + 'file' => \option('bnomei.page-sqlite.file'), + 'debug' => \option('debug'), + 'pragmas-construct' => \option('bnomei.page-sqlite.pragmas-construct'), + 'pragmas-destruct' => \option('bnomei.page-sqlite.pragmas-destruct'), + ], $options); + + foreach ($this->options as $key => $call) { + if (!is_string($call) && is_callable($call) && in_array($key, [ + 'file', + 'pragmas-construct', + 'pragmas-destruct', + ])) { + $this->options[$key] = $call(); + } + } + } } diff --git a/composer.json b/composer.json index ec43c30..305340b 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "bnomei/kirby3-page-sqlite", "type": "kirby-plugin", - "version": "1.1.4", + "version": "1.2.0", "description": "Kirby 3 Plugin to cache the content file using SQLite", "license": "MIT", "authors": [ diff --git a/composer.lock b/composer.lock index 0e70240..13575a6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "be9bf152e49b08a4412e6eff73baabd3", + "content-hash": "65584723731eeea00e3e1b3c2f4512a7", "packages": [ { "name": "getkirby/composer-installer", diff --git a/index.php b/index.php index 154c833..f696814 100644 --- a/index.php +++ b/index.php @@ -5,10 +5,45 @@ Kirby::plugin('bnomei/page-sqlite', [ 'options' => [ 'cache' => true, - 'wal' => true, 'file' => function () { return \Bnomei\SQLitePagesDatabase::cacheFolder() . '/page-sqlite-v1-0-1.sqlite'; - } + }, + // https://sqlite.org/pragma.html + 'pragmas-construct' => function () { + $defaults = [ + 'PRAGMA main.cache_size = 10000;', + 'PRAGMA case_sensitive_like = false', + 'PRAGMA main.auto_vacuum = INCREMENTAL;', + 'PRAGMA main.locking_mode = EXCLUSIVE;', + 'PRAGMA main.page_size = 4096;', + 'PRAGMA temp_store = MEMORY;', + ]; + if (SQLite3::version() >= 3007001) { + return array_merge($defaults, [ + 'PRAGMA main.synchronous = NORMAL;', + 'PRAGMA main.journal_mode = WAL;', + ]); + } else { + return array_merge($defaults, [ + 'PRAGMA main.synchronous = OFF;', + 'PRAGMA main.journal_mode = MEMORY;', + ]); + } + }, + 'pragmas-destruct' => function () { + $defaults = [ + 'PRAGMA main.incremental_vacuum;', + ]; + if (SQLite3::version() >= 3007001) { + return array_merge($defaults, [ + 'PRAGMA main.wal_checkpoint(TRUNCATE);', + 'PRAGMA main.synchronous = NORMAL;', + 'PRAGMA main.locking_mode = NORMAL;', + ]); + } else { + return array_merge($defaults, []); + } + }, ], 'pageMethods' => [ 'isSQLitePage' => function () { diff --git a/tests/content/home/default.txt b/tests/content/home/default.txt index 2677a54..4593756 100644 --- a/tests/content/home/default.txt +++ b/tests/content/home/default.txt @@ -1,5 +1,5 @@ -Title: Home 1599160794 +Title: Home 1599344942 ---- -Text: \ No newline at end of file +Text: