Browse Source

Basic database maintenance

Closes #169
microsub
J. King 5 years ago
parent
commit
f7240301e4
  1. 3
      CHANGELOG
  2. 10
      lib/Database.php
  3. 6
      lib/Db/Driver.php
  4. 13
      lib/Db/MySQL/Driver.php
  5. 6
      lib/Db/PostgreSQL/Driver.php
  6. 6
      lib/Db/SQLite3/Driver.php
  7. 9
      lib/Service.php
  8. 4
      tests/cases/Database/SeriesMiscellany.php
  9. 5
      tests/cases/Db/BaseDriver.php
  10. 5
      tests/cases/Db/BaseUpdate.php

3
CHANGELOG

@ -13,6 +13,9 @@ Bug fixes:
- Sort Tiny Tiny RSS special feeds according to special ordering
- Invalidate sessions when passwords are changed
Changes:
- Perform regular database maintenance to improve long-term performance
Version 0.7.1 (2019-03-25)
==========================

10
lib/Database.php

@ -110,6 +110,11 @@ class Database {
return $this->db->charsetAcceptable();
}
/** Performs maintenance on the database to ensure good performance */
public function driverMaintenance(): bool {
return $this->db->maintenance();
}
/** Computes the column and value text of an SQL "SET" clause, validating arbitrary input against a whitelist
*
* Returns an indexed array containing the clause text, an array of types, and another array of values
@ -1788,10 +1793,11 @@ class Database {
$limitUnread = Date::sub(Arsse::$conf->purgeArticlesUnread);
}
$feeds = $this->db->query("SELECT id, size from arsse_feeds")->getAll();
$deleted = 0;
foreach ($feeds as $feed) {
$query->run($feed['id'], $feed['size'], $feed['id'], $limitUnread, $limitRead);
$deleted += $query->run($feed['id'], $feed['size'], $feed['id'], $limitUnread, $limitRead)->changes();
}
return true;
return (bool) $deleted;
}
/** Ensures the specified article exists and raises an exception otherwise

6
lib/Db/Driver.php

@ -82,4 +82,10 @@ interface Driver {
* This functionality should be avoided in favour of using statement parameters whenever possible
*/
public function literalString(string $str): string;
/** Performs implementation-specific database maintenance to ensure good performance
*
* This should be restricted to quick maintenance; in SQLite terms it might include ANALYZE, but not VACUUM
*/
public function maintenance(): bool;
}

13
lib/Db/MySQL/Driver.php

@ -216,4 +216,17 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver {
public function literalString(string $str): string {
return "'".$this->db->real_escape_string($str)."'";
}
public function maintenance(): bool {
// with MySQL each table must be analyzed separately, so we first have to get a list of tables
foreach ($this->query("SHOW TABLES like 'arsse\\_%'") as $table) {
$table = array_pop($table);
if (!preg_match("/^arsse_[a-z_]+$/", $table)) {
// table is not one of ours
continue; // @codeCoverageIgnore
}
$this->query("ANALYZE TABLE $table");
}
return true;
}
}

6
lib/Db/PostgreSQL/Driver.php

@ -225,4 +225,10 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver {
public function literalString(string $str): string {
return pg_escape_literal($this->db, $str);
}
public function maintenance(): bool {
// analyze the database
$this->exec("ANALYZE");
return true;
}
}

6
lib/Db/SQLite3/Driver.php

@ -188,4 +188,10 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver {
public function literalString(string $str): string {
return "'".\SQLite3::escapeString($str)."'";
}
public function maintenance(): bool {
// analyze the database then checkpoint and truncate the write-ahead log
$this->exec("ANALYZE; PRAGMA wal_checkpoint(truncate)");
return true;
}
}

9
lib/Service.php

@ -92,7 +92,12 @@ class Service {
}
public static function cleanupPost(): bool {
// delete old articles, according to configured threasholds
return Arsse::$db->articleCleanup();
// delete old articles, according to configured thresholds
$deleted = Arsse::$db->articleCleanup();
// if any articles were deleted, perform database maintenance
if ($deleted) {
Arsse::$db->driverMaintenance();
}
return true;
}
}

4
tests/cases/Database/SeriesMiscellany.php

@ -44,4 +44,8 @@ trait SeriesMiscellany {
public function testCheckCharacterSetAcceptability() {
$this->assertInternalType("bool", Arsse::$db->driverCharsetAcceptable());
}
public function testPerformMaintenance() {
$this->assertTrue(Arsse::$db->driverMaintenance());
}
}

5
tests/cases/Db/BaseDriver.php

@ -382,4 +382,9 @@ abstract class BaseDriver extends \JKingWeb\Arsse\Test\AbstractTest {
public function testProduceAStringLiteral() {
$this->assertSame("'It''s a string!'", $this->drv->literalString("It's a string!"));
}
public function testPerformMaintenance() {
// this performs maintenance in the absence of tables; see BaseUpdate.php for another test with tables
$this->assertTrue($this->drv->maintenance());
}
}

5
tests/cases/Db/BaseUpdate.php

@ -130,4 +130,9 @@ class BaseUpdate extends \JKingWeb\Arsse\Test\AbstractTest {
$this->assertException("updateTooNew", "Db");
$this->drv->schemaUpdate(-1, $this->base);
}
public function testPerformMaintenance() {
$this->drv->schemaUpdate(Database::SCHEMA_VERSION);
$this->assertTrue($this->drv->maintenance());
}
}

Loading…
Cancel
Save