feat: add SQLite vacuum maintenance action and schedule (#2760)

Co-authored-by: Alex Justesen <1144087+alexjustesen@users.noreply.github.com>
This commit is contained in:
Alex Justesen
2026-04-18 11:14:03 -04:00
committed by GitHub
parent ac94ad71f0
commit e32a6dd5b8
3 changed files with 96 additions and 0 deletions
+42
View File
@@ -0,0 +1,42 @@
<?php
namespace App\Actions;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Lorisleiva\Actions\Concerns\AsAction;
class VacuumDatabase
{
use AsAction;
/**
* Reclaim unused pages and refresh query planner stats on SQLite databases.
* No-op for other drivers, which handle this internally.
*/
public function handle(): void
{
$connection = DB::connection();
if ($connection->getDriverName() !== 'sqlite') {
return;
}
// VACUUM cannot run inside a transaction. Bail out rather than fail hard
// if one is somehow active (e.g. tests wrapped in RefreshDatabase).
if ($connection->transactionLevel() > 0) {
Log::warning('Skipping SQLite maintenance: active transaction detected.');
return;
}
$start = microtime(true);
$connection->statement('PRAGMA optimize;');
$connection->statement('VACUUM;');
Log::info('SQLite maintenance completed', [
'duration_ms' => round((microtime(true) - $start) * 1000, 2),
]);
}
}
+11
View File
@@ -1,6 +1,7 @@
<?php
use App\Actions\CheckForScheduledSpeedtests;
use App\Actions\VacuumDatabase;
use Illuminate\Support\Facades\Schedule;
/**
@@ -28,3 +29,13 @@ Schedule::everyMinute()
->group(function () {
Schedule::call(fn () => CheckForScheduledSpeedtests::run());
});
/**
* Weekly SQLite maintenance (no-op on other drivers).
*/
Schedule::call(fn () => VacuumDatabase::run())
->weekly()
->sundays()
->at('03:15')
->name('sqlite-vacuum')
->onOneServer();
@@ -0,0 +1,43 @@
<?php
use App\Actions\VacuumDatabase;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
describe('VacuumDatabase', function () {
it('skips silently when the default connection is not sqlite', function () {
Log::spy();
VacuumDatabase::run();
Log::shouldNotHaveReceived('info');
Log::shouldNotHaveReceived('warning');
});
it('runs PRAGMA optimize and VACUUM on a sqlite connection', function () {
Config::set('database.connections.sqlite_vacuum_test', [
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => '',
'foreign_key_constraints' => true,
]);
Config::set('database.default', 'sqlite_vacuum_test');
DB::purge();
$statements = [];
DB::listen(function ($query) use (&$statements) {
$statements[] = $query->sql;
});
Log::spy();
VacuumDatabase::run();
expect($statements)->toContain('PRAGMA optimize;');
expect($statements)->toContain('VACUUM;');
Log::shouldHaveReceived('info')
->with('SQLite maintenance completed', Mockery::type('array'))
->once();
});
});