Page MenuHomePhabricator

quibble-vendor-sqlite-php72-docker is broken by AbuseFilter
Closed, ResolvedPublic

Description

https://integration.wikimedia.org/ci/job/quibble-vendor-sqlite-php72-docker/337/console on a no-op gerrit patch - https://gerrit.wikimedia.org/r/#/c/mediawiki/extensions/AbuseFilter/+/594611/

02:24:41 RuntimeException from line 1008 of /workspace/src/includes/libs/rdbms/database/DatabaseSqlite.php: Couldn't retrieve structure for table unittest_abuse_filter
02:24:41 #0 /workspace/src/extensions/AbuseFilter/includes/AbuseFilterHooks.php(856): Wikimedia\Rdbms\DatabaseSqlite->duplicateTableStructure('unittest_abuse_...', 'unittest_extern...', false, 'AbuseFilterHook...')
02:24:41 #1 /workspace/src/includes/HookContainer/HookContainer.php(289): AbuseFilterHooks::onUnitTestsAfterDatabaseSetup(Object(Wikimedia\Rdbms\DatabaseSqlite), 'unittest_')
02:24:41 #2 /workspace/src/includes/HookContainer/HookContainer.php(129): MediaWiki\HookContainer\HookContainer->callLegacyHook('UnitTestsAfterD...', Array, Array, Array)
02:24:41 #3 /workspace/src/includes/Hooks.php(136): MediaWiki\HookContainer\HookContainer->run('UnitTestsAfterD...', Array, Array)
02:24:41 #4 /workspace/src/tests/phpunit/MediaWikiIntegrationTestCase.php(1488): Hooks::run('UnitTestsAfterD...', Array)
02:24:41 #5 /workspace/src/tests/phpunit/MediaWikiIntegrationTestCase.php(1437): MediaWikiIntegrationTestCase::setupTestDB(Object(Wikimedia\Rdbms\DatabaseSqlite), 'unittest_')
02:24:41 #6 /workspace/src/tests/phpunit/MediaWikiIntegrationTestCase.php(406): MediaWikiIntegrationTestCase->setupAllTestDBs()
02:24:41 #7 /workspace/src/vendor/phpunit/phpunit/src/Framework/TestSuite.php(597): MediaWikiIntegrationTestCase->run(Object(PHPUnit\Framework\TestResult))
02:24:41 #8 /workspace/src/vendor/phpunit/phpunit/src/Framework/TestSuite.php(597): PHPUnit\Framework\TestSuite->run(Object(PHPUnit\Framework\TestResult))
02:24:41 #9 /workspace/src/vendor/phpunit/phpunit/src/Framework/TestSuite.php(597): PHPUnit\Framework\TestSuite->run(Object(PHPUnit\Framework\TestResult))
02:24:41 #10 /workspace/src/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(621): PHPUnit\Framework\TestSuite->run(Object(PHPUnit\Framework\TestResult))
02:24:41 #11 /workspace/src/vendor/phpunit/phpunit/src/TextUI/Command.php(204): PHPUnit\TextUI\TestRunner->doRun(Object(PHPUnit\Framework\TestSuite), Array, Array, true)
02:24:41 #12 /workspace/src/tests/phpunit/phpunit.php(71): PHPUnit\TextUI\Command->run(Array, true)
02:24:41 #13 /workspace/src/maintenance/doMaintenance.php(105): PHPUnitMaintClass->execute()
02:24:41 #14 /workspace/src/tests/phpunit/phpunit.php(130): require('/workspace/src/...')
02:24:41 #15 {main}
02:24:41 
02:24:41 Fatal error: Uncaught UnexpectedValueException: Got connection to 'wikidb-unittest_', but expected local domain ('wikidb') in /workspace/src/includes/libs/rdbms/loadbalancer/LoadBalancer.php:1113
02:24:41 Stack trace:
02:24:41 #0 /workspace/src/includes/libs/rdbms/loadbalancer/LoadBalancer.php(929): Wikimedia\Rdbms\LoadBalancer->getLocalConnection(0, 4)
02:24:41 #1 /workspace/src/includes/libs/rdbms/loadbalancer/LoadBalancer.php(901): Wikimedia\Rdbms\LoadBalancer->getServerConnection(0, 'wikidb', 4)
02:24:41 #2 /workspace/src/includes/libs/rdbms/loadbalancer/LoadBalancer.php(1046): Wikimedia\Rdbms\LoadBalancer->getConnection(-2, Array, 'wikidb', 4)
02:24:41 #3 /workspace/src/includes/GlobalFunctions.php(2463): Wikimedia\Rdbms\LoadBalancer->getMaintenanceConnectionRef(-2, Array, 'wikidb')
02:24:41 #4 /workspace/src/extensions/AbuseFilter/includes/AbuseFilterHooks.php(869): wfGetDB(-2)
02:24:41 #5 /workspace/src/includes/HookContainer/HookContainer.php(289): AbuseFilterHooks::onUnitTestsBeforeDatabaseTeardown()
02:24:41 #6 /workspace/src/includes/HookContainer/HookContainer.php(129): MediaWiki\ in /workspace/src/includes/libs/rdbms/loadbalancer/LoadBalancer.php on line 1113
02:24:41 PHP Fatal error:  Uncaught UnexpectedValueException: Got connection to 'wikidb-unittest_', but expected local domain ('wikidb') in /workspace/src/includes/libs/rdbms/loadbalancer/LoadBalancer.php:1113
02:24:41 Stack trace:
02:24:41 #0 /workspace/src/includes/libs/rdbms/loadbalancer/LoadBalancer.php(929): Wikimedia\Rdbms\LoadBalancer->getLocalConnection(0, 4)
02:24:41 #1 /workspace/src/includes/libs/rdbms/loadbalancer/LoadBalancer.php(901): Wikimedia\Rdbms\LoadBalancer->getServerConnection(0, 'wikidb', 4)
02:24:41 #2 /workspace/src/includes/libs/rdbms/loadbalancer/LoadBalancer.php(1046): Wikimedia\Rdbms\LoadBalancer->getConnection(-2, Array, 'wikidb', 4)
02:24:41 #3 /workspace/src/includes/GlobalFunctions.php(2463): Wikimedia\Rdbms\LoadBalancer->getMaintenanceConnectionRef(-2, Array, 'wikidb')
02:24:41 #4 /workspace/src/extensions/AbuseFilter/includes/AbuseFilterHooks.php(869): wfGetDB(-2)
02:24:41 #5 /workspace/src/includes/HookContainer/HookContainer.php(289): AbuseFilterHooks::onUnitTestsBeforeDatabaseTeardown()
02:24:41 #6 /workspace/src/includes/HookContainer/HookContainer.php(129): MediaWiki\ in /workspace/src/includes/libs/rdbms/loadbalancer/LoadBalancer.php on line 1113
02:24:41 INFO:quibble.cmd:<<< Finish: PHPUnit extensions suite (without database or standalone), in 0.797 s

Event Timeline

I reproduce it locally with Quibble 0.0.41:

Bootstrapping:

mkdir workspace
cd workspace
quibble --git-cache="$HOME/projects" --db=sqlite  --skip=all mediawiki/extensions/AbuseFilter

Then run phpunit skipping git, npm/composer install:

rm -f src/LocalSettings.php; quibble --git-cache=/home/hashar/projects --db=sqlite --skip-zuul --skip-deps --run=phpunit mediawiki/extensions/AbuseFilter

From there it should be easy to git bisect it.

I can't do it right know cause PHPUnit on my local machine ends up trapped in some kind of infinite loop calling getpid but that is unrelated.

I thought it was a regression but it is not. Apparently AbuseFilter does not really support sqlite and there is an epic task to bring up to par on sqlite and postgresql: T199544

I guess we never had sqlite enforced by CI on extensions beside Wikibase at some point.

I thought it was a regression but it is not. Apparently AbuseFilter does not really support sqlite and there is an epic task to bring up to par on sqlite and postgresql: T199544

While this is certainly true, I don't think this is an actual incompatibility. The problem reported here only happens in test code -- for some reason, SQLite isn't able to clone the abuse_filter table, because it cannot find its definition.

I went on the build https://integration.wikimedia.org/ci/job/quibble-vendor-sqlite-php72-docker/337/ , downloaded the debug log for cli.

Seems like the table is cloned properly:

CloneDatabase::cloneTableStructure duplicating abuse_filter to unittest_abuse_filter
[DBQuery] Wikimedia\Rdbms\DatabaseSqlite::duplicateTableStructure [0s] localhost: SELECT sql FROM sqlite_master WHERE tbl_name='abuse_filter' AND type='table'
[DBQuery] Wikimedia\Rdbms\DatabaseSqlite::duplicateTableStructure [0s] localhost: CREATE TEMPORARY TABLE "unittest_abuse_filter" (
 af_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
 af_pattern BLOB NOT NULL,
 af_user INTEGER  NOT NULL,
 af_user_text BLOB NOT NULL,
 af_timestamp BLOB NOT NULL,
 af_enabled INTEGER not null default 1,
 af_comments BLOB,
 af_public_comments BLOB,
 af_hidden INTEGER not null default 0,
 af_hit_count INTEGER not null default 0,
 af_throttled INTEGER NOT NULL default 0,
 af_deleted INTEGER NOT NULL DEFAULT 0,
 af_actions BLOB NOT NULL DEFAULT '',
 af_global INTEGER NOT NULL DEFAULT 0,
 af_group BLOB NOT NULL DEFAULT 'default'
 )
[DBQuery] Wikimedia\Rdbms\DatabaseSqlite::duplicateTableStructure [0s] localhost: PRAGMA INDEX_LIST('abuse_filter')

The last lines in the debug log:

[DBQuery] Wikimedia\Rdbms\DatabaseSqlite::tableExists [0s] localhost: SELECT 1 FROM sqlite_master WHERE type='table' AND name='unittest_external_abuse_filter'
[DBQuery] AbuseFilterHooks::onUnitTestsAfterDatabaseSetup [0s] localhost: SELECT sql FROM sqlite_master WHERE tbl_name='unittest_abuse_filter' AND type='table'

That comes from:

/**
 * Setup tables to emulate global filters, used in AbuseFilterConsequencesTest.
 *
 * @param IMaintainableDatabase $db
 * @param string $prefix The prefix used in unit tests
 * @suppress PhanUndeclaredClassConstant AbuseFilterConsequencesTest is in AutoloadClasses
 * @suppress PhanUndeclaredClassStaticProperty AbuseFilterConsequencesTest is in AutoloadClasses
 */
public static function onUnitTestsAfterDatabaseSetup( IMaintainableDatabase $db, $prefix ) {
    $externalPrefix = AbuseFilterConsequencesTest::DB_EXTERNAL_PREFIX;
    if ( $db->tableExists( $externalPrefix . AbuseFilterConsequencesTest::$externalTables[0] ) ) {
        // Check a random table to avoid unnecessary table creations. See T155147.
        return;
    }

    foreach ( AbuseFilterConsequencesTest::$externalTables as $table ) {
        // Don't create them as temporary, as we'll access the DB via another connection
        $db->duplicateTableStructure(
            "$prefix$table",
            "$prefix$externalPrefix$table",
            false,
            __METHOD__
        );
    }
}

Could it be that:

  • the hook is passed a different database object which would thus lack the temporary tables
  • the table being temporary the schema is not retrievable by duplicateTableStructure. Eg SELECT sql FROM sqlite_master WHERE tbl_name='unittest_abuse_filter' AND type='table' returns nothing. Maybe because a temporary table has a different type?

I'll look into this tomorrowish. However, note that this is working as intended for MySQL...

Change 596862 had a related patch set uploaded (by Daimona Eaytoy; owner: Daimona Eaytoy):
[mediawiki/extensions/AbuseFilter@master] Make tests pass on SQLite

https://gerrit.wikimedia.org/r/596862

I had to debug this using WMF CI because I don't have SQLite installed, but I figured it out pretty quickly, and confirmed within 6 tries. Although in tests table are created with a test prefix (usually 'unittest_'), the SQLite schema definition in sqlite_master doesn't have that prefix (or perhaps it's because it doesn't hold temporary tables), hence cloning will fail although the prefixed table is there. As I said, this is different with MySQL, where cloning a prefixed/temporary table works just fine, so it should be fixed in core, but this goes beyond the scope of this task.

The quick fix here is the patch above.

Ah, obviously the build still fails due to other issues. Mainly in CheckUser, but there's a faulty AF test as well.

Change 653121 had a related patch set uploaded (by Jforrester; owner: Jforrester):
[integration/config@master] zuul: [mediawiki/extensions/AbuseFilter] Make sqlite tests voting

https://gerrit.wikimedia.org/r/653121

Change 596862 merged by jenkins-bot:
[mediawiki/extensions/AbuseFilter@master] Make tests pass on SQLite

https://gerrit.wikimedia.org/r/596862

Change 653121 merged by jenkins-bot:
[integration/config@master] zuul: [mediawiki/extensions/AbuseFilter] Make sqlite tests voting

https://gerrit.wikimedia.org/r/653121

Mentioned in SAL (#wikimedia-releng) [2021-01-01T18:13:33Z] <James_F> zuul: [mediawiki/extensions/AbuseFilter] Make sqlite tests voting T251967