From 9ee1d63dcef54b2b755bfc07ee04067800d00377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Fri, 11 Oct 2024 21:31:54 +0200 Subject: [PATCH] sqlite: use WAL journal mode by default --- .../SQLiteDatabaseManager.php | 24 ++++++++++++++- tests/TenantDatabaseManagerTest.php | 30 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/Database/TenantDatabaseManagers/SQLiteDatabaseManager.php b/src/Database/TenantDatabaseManagers/SQLiteDatabaseManager.php index ada5d642..d69d19c4 100644 --- a/src/Database/TenantDatabaseManagers/SQLiteDatabaseManager.php +++ b/src/Database/TenantDatabaseManagers/SQLiteDatabaseManager.php @@ -4,6 +4,8 @@ declare(strict_types=1); namespace Stancl\Tenancy\Database\TenantDatabaseManagers; +use AssertionError; +use PDO; use Stancl\Tenancy\Database\Contracts\TenantDatabaseManager; use Stancl\Tenancy\Database\Contracts\TenantWithDatabase; use Throwable; @@ -15,10 +17,30 @@ class SQLiteDatabaseManager implements TenantDatabaseManager */ public static string|null $path = null; + /** + * Should the WAL journal mode be used for newly created databases. + * + * @see https://www.sqlite.org/pragma.html#pragma_journal_mode + */ + public static bool $WAL = true; + public function createDatabase(TenantWithDatabase $tenant): bool { try { - return (bool) file_put_contents($this->getPath($tenant->database()->getName()), ''); + if (file_put_contents($path = $this->getPath($tenant->database()->getName()), '') === false) { + return false; + } + + if (static::$WAL) { + $pdo = new PDO('sqlite:' . $path); + $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + assert($pdo->query("pragma journal_mode = wal")->fetch(PDO::FETCH_ASSOC)['journal_mode'] === 'wal', "Unable to set journal mode to wal."); + } + + return true; + } catch (AssertionError $e) { + throw $e; } catch (Throwable) { return false; } diff --git a/tests/TenantDatabaseManagerTest.php b/tests/TenantDatabaseManagerTest.php index 559b40b5..0b5376d9 100644 --- a/tests/TenantDatabaseManagerTest.php +++ b/tests/TenantDatabaseManagerTest.php @@ -145,6 +145,36 @@ test('db name is prefixed with db path when sqlite is used', function () { expect(database_path('foodb'))->toBe(config('database.connections.tenant.database')); }); +test('sqlite databases use the WAL journal mode by default', function (bool|null $wal) { + $expected = $wal ? 'wal' : 'delete'; + if ($wal !== null) { + SQLiteDatabaseManager::$WAL = $wal; + } else { + // default behavior + $expected = 'wal'; + } + + Event::listen(TenantCreated::class, JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) { + return $event->tenant; + })->toListener()); + + $tenant = Tenant::create([ + 'tenancy_db_connection' => 'sqlite', + ]); + + $dbPath = database_path($tenant->database()->getName()); + + expect(file_exists($dbPath))->toBeTrue(); + + $db = new PDO('sqlite:' . $dbPath); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + expect($db->query('pragma journal_mode')->fetch(PDO::FETCH_ASSOC)['journal_mode'])->toBe($expected); + + // cleanup + SQLiteDatabaseManager::$WAL = true; +})->with([true, false, null]); + test('schema manager uses schema to separate tenant dbs', function () { config([ 'tenancy.database.managers.pgsql' => PostgreSQLSchemaManager::class,