diff --git a/app/DB.php b/app/DB.php
index 5cd19ecc4..9148c8670 100644
--- a/app/DB.php
+++ b/app/DB.php
@@ -186,8 +186,9 @@ class DB extends Base {
SELECT count(*)
FROM performance_schema.processlist
WHERE COMMAND NOT IN ('Sleep')
- AND TIME > 1200;
- ");
+ AND TIME > ?
+ ", MYSQL_SLOW_QUERY_TIMEOUT
+ );
}
public static function lookupDirection(string $direction): Direction {
diff --git a/app/DB/MysqlTable.php b/app/DB/MysqlTable.php
index 7d208e724..707877d3c 100644
--- a/app/DB/MysqlTable.php
+++ b/app/DB/MysqlTable.php
@@ -87,6 +87,17 @@ class MysqlTable extends AbstractTable {
);
}
+ public function recentUpdate(int $seconds): bool {
+ return (bool)self::$db->scalar("
+ SELECT 1
+ FROM information_schema.tables
+ WHERE table_schema = ?
+ AND table_name = ?
+ AND update_time > now() - INTERVAL ? SECOND
+ ", MYSQL_DB, $this->name, $seconds
+ );
+ }
+
public function stats(): array {
return self::$db->rowAssoc("
SELECT t.TABLE_ROWS,
diff --git a/app/Tracker.php b/app/Tracker.php
index f33b6702e..dc9d365e3 100644
--- a/app/Tracker.php
+++ b/app/Tracker.php
@@ -341,4 +341,14 @@ class Tracker extends Base {
");
return self::$db->to_array(false, MYSQLI_ASSOC);
}
+
+ public function recentUpdate(int $seconds): bool {
+ return (bool)self::$db->scalar("
+ SELECT 1
+ FROM xbt_files_users
+ WHERE mtime > unix_timestamp(now() - INTERVAL ? SECOND)
+ LIMIT 1
+ ", $seconds
+ );
+ }
}
diff --git a/app/User/Activity.php b/app/User/Activity.php
index ee8b5703f..d48239a73 100644
--- a/app/User/Activity.php
+++ b/app/User/Activity.php
@@ -75,22 +75,6 @@ class Activity extends \Gazelle\BaseUser {
return $this;
}
- public function setDb(\Gazelle\DB $dbMan): static {
- if ($this->user->permitted('admin_site_debug')) {
- $longRunning = $dbMan->longRunning();
- if ($longRunning > 0) {
- $message = "$longRunning long-running DB operation" . plural($longRunning);
- $this->setAlert("DB");
- }
- // If Ocelot can no longer write to xbt_files_users, it will drain after an hour
- // Look for database locks and check the Ocelot log
- if (!self::$db->scalar('SELECT fid FROM xbt_files_users LIMIT 1')) {
- $this->setAlert('Ocelot not updating!');
- }
- }
- return $this;
- }
-
public function setPayment(\Gazelle\Manager\Payment $payMan): static {
if ($this->user->permitted('admin_manage_payments')) {
$soon = $payMan->soon();
diff --git a/app/View.php b/app/View.php
index d0db9c2e1..e113982a3 100644
--- a/app/View.php
+++ b/app/View.php
@@ -48,7 +48,6 @@ class View extends Base {
->setReport(new Stats\Report())
->setPayment($payMan)
->setApplicant(new Manager\Applicant())
- ->setDb(new DB())
->setScheduler(new TaskScheduler())
->setSSLHost(new Manager\SSLHost())
->setAutoReport(
@@ -58,6 +57,23 @@ class View extends Base {
)
);
+ if ($user->permitted('admin_site_debug')) {
+ $longRunning = new DB()->longRunning();
+ if ($longRunning > 0) {
+ $message = "$longRunning long-running DB operation" . plural($longRunning);
+ $activity->setAlert("DB");
+ }
+ // Check that Ocelot is still writing to xbt_files_users.
+ // If not, look for database locks and check the Ocelot log
+
+ if (!new Tracker()->recentUpdate(TRACKER_REFRESH_TIMEOUT)) {
+ $activity->setAlert('TRACKER'
+ );
+ }
+ }
+
$threshold = new Manager\SiteOption()
->findValueByName('download-warning-threshold');
if ($threshold) {
diff --git a/lib/config.php b/lib/config.php
index 3b3c2f29d..d348675b3 100644
--- a/lib/config.php
+++ b/lib/config.php
@@ -194,6 +194,9 @@ defined('MYSQL_RO_USER') or define('MYSQL_RO_USER', 'gazro');
// The password of the above account.
defined('MYSQL_RO_PASS') or define('MYSQL_RO_PASS', 'passro');
+// Warn if there is a query that has been running for too long
+defined('MYSQL_SLOW_QUERY_TIMEOUT') or define('MYSQL_SLOW_QUERY_TIMEOUT', 1200);
+
// The username of the Phinx account (used for schema modifications).
// In production, this account will have a different set of grants compared
// to the website account (so that if the website account is compromised, it
@@ -292,6 +295,12 @@ defined('TRACKER_NAME') or define('TRACKER_NAME', '127.0.0.1' . ":" . TRACKER_PO
// be exactly 32 alphanumeric characters.
defined('TRACKER_SECRET') or define('TRACKER_SECRET', '00000000000000000000000000000000');
+// How long to wait in the absense of an update from the tracker to warn of a
+// problem? Busy with millions of peears receive multiple updates per second.
+// Sites with a few thousand peers or less may not see an update for several
+// seconds.
+defined('TRACKER_REFRESH_TIMEOUT') or define('TRACKER_REFRESH_TIMEOUT', 5);
+
// Second shared secret that is compiled into Ocelot (see config.cpp). Must
// be exactly 32 alphanumeric characters.
defined('TRACKER_REPORTKEY') or define('TRACKER_REPORTKEY', '00000000000000000000000000000000');
diff --git a/tests/phpunit/DbTest.php b/tests/phpunit/DbTest.php
index 187449b44..a88b6777a 100644
--- a/tests/phpunit/DbTest.php
+++ b/tests/phpunit/DbTest.php
@@ -363,6 +363,8 @@ class DbTest extends TestCase {
),
'mysql-fkey-list'
);
+ // not a problem that the table does not exist, the SQL is being tested
+ $this->assertFalse($bad->recentUpdate(600), 'mysql-table-recent-update');
}
public function testPgTable(): void {
diff --git a/tests/phpunit/TrackerTest.php b/tests/phpunit/TrackerTest.php
index 67e911d48..ff97f430e 100644
--- a/tests/phpunit/TrackerTest.php
+++ b/tests/phpunit/TrackerTest.php
@@ -182,4 +182,21 @@ class TrackerTest extends TestCase {
$this->assertEquals(1, $tracker->expireFreeleechTokens("$userId:$torrentId,$userId:$fakeId"), 'tracker-expire-tokens');
$downloader->remove();
}
+
+ public function testTrackerUpdate(): void {
+ $this->user = Helper::makeUser('trkfree.' . randomString(10), 'tracker');
+ $this->user->requestContext()->setViewer($this->user);
+ $this->torrent = Helper::makeTorrentMusic(
+ Helper::makeTGroupMusic(
+ name: 'tracker ' . randomString(10),
+ artistName: [[ARTIST_MAIN], ['Tracker Girl ' . randomString(12)]],
+ tagName: ['trap'],
+ user: $this->user,
+ ),
+ user: $this->user,
+ title: 'tracker ' . randomString(10),
+ );
+ Helper::generateTorrentSeed($this->torrent, $this->user);
+ $this->assertTrue(new Tracker()->recentUpdate(60), 'tracker-recent-update');
+ }
}
diff --git a/tests/phpunit/UserActivityTest.php b/tests/phpunit/UserActivityTest.php
index 31a804936..ed0a0ca77 100644
--- a/tests/phpunit/UserActivityTest.php
+++ b/tests/phpunit/UserActivityTest.php
@@ -30,7 +30,6 @@ class UserActivityTest extends TestCase {
$this->assertInstanceOf(User\Activity::class, $activity->configure(), 'user-activity-configure');
$this->assertInstanceOf(User\Activity::class, $activity->setApplicant(new Manager\Applicant()), 'user-activity-applicant');
- $this->assertInstanceOf(User\Activity::class, $activity->setDb(new DB()), 'user-activity-db');
$this->assertInstanceOf(User\Activity::class, $activity->setPayment(new Manager\Payment()), 'user-activity-payment');
$this->assertInstanceOf(User\Activity::class, $activity->setReferral(new Manager\Referral()), 'user-activity-referral');
$this->assertInstanceOf(User\Activity::class, $activity->setReport(new Stats\Report()), 'user-activity-report');