Merge "resourceloader: Map group names to integers internally"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Sat, 24 Aug 2019 22:40:59 +0000 (22:40 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Sat, 24 Aug 2019 22:40:59 +0000 (22:40 +0000)
36 files changed:
includes/libs/filebackend/FileBackend.php
includes/libs/filebackend/FileBackendMultiWrite.php
includes/libs/filebackend/FileOpBatch.php
includes/libs/objectcache/APCBagOStuff.php
includes/libs/objectcache/APCUBagOStuff.php
includes/libs/objectcache/BagOStuff.php
includes/libs/objectcache/CachedBagOStuff.php
includes/libs/objectcache/EmptyBagOStuff.php
includes/libs/objectcache/HashBagOStuff.php
includes/libs/objectcache/MediumSpecificBagOStuff.php
includes/libs/objectcache/MemcachedPeclBagOStuff.php
includes/libs/objectcache/MemcachedPhpBagOStuff.php
includes/libs/objectcache/MultiWriteBagOStuff.php
includes/libs/objectcache/RESTBagOStuff.php
includes/libs/objectcache/RedisBagOStuff.php
includes/libs/objectcache/ReplicatedBagOStuff.php
includes/libs/objectcache/WinCacheBagOStuff.php
includes/libs/rdbms/database/Database.php
includes/libs/rdbms/database/DatabasePostgres.php
includes/libs/rdbms/database/DatabaseSqlite.php
includes/libs/rdbms/database/IDatabase.php
includes/libs/rdbms/lbfactory/LBFactory.php
includes/libs/rdbms/lbfactory/LBFactoryMulti.php
includes/libs/rdbms/lbfactory/LBFactorySimple.php
includes/libs/rdbms/loadbalancer/LoadBalancer.php
includes/libs/rdbms/loadbalancer/LoadBalancerSingle.php
includes/objectcache/SqlBagOStuff.php
maintenance/namespaceDupes.php
maintenance/rebuildtextindex.php
maintenance/sql.php
maintenance/sqlite.php
tests/phpunit/includes/Revision/RevisionStoreDbTestBase.php
tests/phpunit/includes/db/DatabasePostgresTest.php
tests/phpunit/includes/db/LBFactoryTest.php
tests/phpunit/includes/libs/objectcache/BagOStuffTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseMysqlBaseTest.php

index b187fe5..4cacb7a 100644 (file)
@@ -420,7 +420,7 @@ abstract class FileBackend implements LoggerAwareInterface {
         *
         * The StatusValue will be "OK" unless:
         *   - a) unexpected operation errors occurred (network partitions, disk full...)
-        *   - b) significant operation errors occurred and 'force' was not set
+        *   - b) predicted operation errors occurred and 'force' was not set
         *
         * @param array $ops List of operations to execute in order
         * @param array $opts Batch operation options
index f92d5fa..27ad870 100644 (file)
@@ -88,7 +88,7 @@ class FileBackendMultiWrite extends FileBackend {
         *                      any checks from "syncChecks" are still synchronous.
         *
         * @param array $config
-        * @throws FileBackendError
+        * @throws LogicException
         */
        public function __construct( array $config ) {
                parent::__construct( $config );
@@ -178,9 +178,8 @@ class FileBackendMultiWrite extends FileBackend {
                $masterStatus = $mbe->doOperations( $realOps, $opts );
                $status->merge( $masterStatus );
                // Propagate the operations to the clone backends if there were no unexpected errors
-               // and if there were either no expected errors or if the 'force' option was used.
-               // However, if nothing succeeded at all, then don't replicate any of the operations.
-               // If $ops only had one operation, this might avoid backend sync inconsistencies.
+               // and everything didn't fail due to predicted errors. If $ops only had one operation,
+               // this might avoid backend sync inconsistencies.
                if ( $masterStatus->isOK() && $masterStatus->successCount > 0 ) {
                        foreach ( $this->backends as $index => $backend ) {
                                if ( $index === $this->masterIndex ) {
@@ -271,7 +270,10 @@ class FileBackendMultiWrite extends FileBackend {
                                                        continue;
                                                }
                                        }
-                                       if ( ( $this->syncChecks & self::CHECK_SHA1 ) && $cBackend->getFileSha1Base36( $cParams ) !== $mSha1 ) { // wrong SHA1
+                                       if (
+                                               ( $this->syncChecks & self::CHECK_SHA1 ) &&
+                                               $cBackend->getFileSha1Base36( $cParams ) !== $mSha1
+                                       ) { // wrong SHA1
                                                $status->fatal( 'backend-fail-synced', $path );
                                                continue;
                                        }
index 540961e..bbcda08 100644 (file)
@@ -46,7 +46,7 @@ class FileOpBatch {
         *
         * The resulting StatusValue will be "OK" unless:
         *   - a) unexpected operation errors occurred (network partitions, disk full...)
-        *   - b) significant operation errors occurred and 'force' was not set
+        *   - b) predicted operation errors occurred and 'force' was not set
         *
         * @param FileOp[] $performOps List of FileOp operations
         * @param array $opts Batch operation options
index aa83b1f..e9bd7be 100644 (file)
@@ -87,11 +87,11 @@ class APCBagOStuff extends MediumSpecificBagOStuff {
                return true;
        }
 
-       public function incr( $key, $value = 1 ) {
+       public function incr( $key, $value = 1, $flags = 0 ) {
                return apc_inc( $key . self::KEY_SUFFIX, $value );
        }
 
-       public function decr( $key, $value = 1 ) {
+       public function decr( $key, $value = 1, $flags = 0 ) {
                return apc_dec( $key . self::KEY_SUFFIX, $value );
        }
 }
index 80383d1..2b26500 100644 (file)
@@ -85,7 +85,7 @@ class APCUBagOStuff extends MediumSpecificBagOStuff {
                return true;
        }
 
-       public function incr( $key, $value = 1 ) {
+       public function incr( $key, $value = 1, $flags = 0 ) {
                // https://github.com/krakjoe/apcu/issues/166
                if ( apcu_exists( $key . self::KEY_SUFFIX ) ) {
                        return apcu_inc( $key . self::KEY_SUFFIX, $value );
@@ -94,7 +94,7 @@ class APCUBagOStuff extends MediumSpecificBagOStuff {
                }
        }
 
-       public function decr( $key, $value = 1 ) {
+       public function decr( $key, $value = 1, $flags = 0 ) {
                // https://github.com/krakjoe/apcu/issues/166
                if ( apcu_exists( $key . self::KEY_SUFFIX ) ) {
                        return apcu_dec( $key . self::KEY_SUFFIX, $value );
index 73904e4..42da5f0 100644 (file)
@@ -362,31 +362,36 @@ abstract class BagOStuff implements IExpiringStore, IStoreKeyEncoder, LoggerAwar
         * Increase stored value of $key by $value while preserving its TTL
         * @param string $key Key to increase
         * @param int $value Value to add to $key (default: 1) [optional]
+        * @param int $flags Bit field of class WRITE_* constants [optional]
         * @return int|bool New value or false on failure
         */
-       abstract public function incr( $key, $value = 1 );
+       abstract public function incr( $key, $value = 1, $flags = 0 );
 
        /**
         * Decrease stored value of $key by $value while preserving its TTL
         * @param string $key
         * @param int $value Value to subtract from $key (default: 1) [optional]
+        * @param int $flags Bit field of class WRITE_* constants [optional]
         * @return int|bool New value or false on failure
         */
-       abstract public function decr( $key, $value = 1 );
+       abstract public function decr( $key, $value = 1, $flags = 0 );
 
        /**
-        * Increase stored value of $key by $value while preserving its TTL
+        * Increase the value of the given key (no TTL change) if it exists or create it otherwise
         *
-        * This will create the key with value $init and TTL $ttl instead if not present
+        * This will create the key with the value $init and TTL $ttl instead if not present.
+        * Callers should make sure that both ($init - $value) and $ttl are invariants for all
+        * operations to any given key. The value of $init should be at least that of $value.
         *
-        * @param string $key
-        * @param int $ttl
-        * @param int $value
-        * @param int $init
+        * @param string $key Key built via makeKey() or makeGlobalKey()
+        * @param int $exptime Time-to-live (in seconds) or a UNIX timestamp expiration
+        * @param int $value Amount to increase the key value by [default: 1]
+        * @param int|null $init Value to initialize the key to if it does not exist [default: $value]
+        * @param int $flags Bit field of class WRITE_* constants [optional]
         * @return int|bool New value or false on failure
         * @since 1.24
         */
-       abstract public function incrWithInit( $key, $ttl, $value = 1, $init = 1 );
+       abstract public function incrWithInit( $key, $exptime, $value = 1, $init = null, $flags = 0 );
 
        /**
         * Get the "last error" registered; clearLastError() should be called manually
index b1d2b6c..8b4c9c6 100644 (file)
@@ -87,6 +87,7 @@ class CachedBagOStuff extends BagOStuff {
 
        public function set( $key, $value, $exptime = 0, $flags = 0 ) {
                $this->procCache->set( $key, $value, $exptime, $flags );
+
                if ( !$this->fieldHasFlags( $flags, self::WRITE_CACHE_ONLY ) ) {
                        $this->backend->set( $key, $value, $exptime, $flags );
                }
@@ -96,6 +97,7 @@ class CachedBagOStuff extends BagOStuff {
 
        public function delete( $key, $flags = 0 ) {
                $this->procCache->delete( $key, $flags );
+
                if ( !$this->fieldHasFlags( $flags, self::WRITE_CACHE_ONLY ) ) {
                        $this->backend->delete( $key, $flags );
                }
@@ -194,22 +196,22 @@ class CachedBagOStuff extends BagOStuff {
                return true;
        }
 
-       public function incr( $key, $value = 1 ) {
+       public function incr( $key, $value = 1, $flags = 0 ) {
                $this->procCache->delete( $key );
 
-               return $this->backend->incr( $key, $value );
+               return $this->backend->incr( $key, $value, $flags );
        }
 
-       public function decr( $key, $value = 1 ) {
+       public function decr( $key, $value = 1, $flags = 0 ) {
                $this->procCache->delete( $key );
 
-               return $this->backend->decr( $key, $value );
+               return $this->backend->decr( $key, $value, $flags );
        }
 
-       public function incrWithInit( $key, $ttl, $value = 1, $init = 1 ) {
+       public function incrWithInit( $key, $exptime, $value = 1, $init = null, $flags = 0 ) {
                $this->procCache->delete( $key );
 
-               return $this->backend->incrWithInit( $key, $ttl, $value, $init );
+               return $this->backend->incrWithInit( $key, $exptime, $value, $init, $flags );
        }
 
        public function addBusyCallback( callable $workCallback ) {
index b2613b2..9723cad 100644 (file)
@@ -45,11 +45,15 @@ class EmptyBagOStuff extends MediumSpecificBagOStuff {
                return true;
        }
 
-       public function incr( $key, $value = 1 ) {
+       public function incr( $key, $value = 1, $flags = 0 ) {
                return false;
        }
 
-       public function incrWithInit( $key, $ttl, $value = 1, $init = 1 ) {
+       public function decr( $key, $value = 1, $flags = 0 ) {
+               return false;
+       }
+
+       public function incrWithInit( $key, $exptime, $value = 1, $init = null, $flags = 0 ) {
                return false; // faster
        }
 
index b4087be..6d0940b 100644 (file)
@@ -108,10 +108,10 @@ class HashBagOStuff extends MediumSpecificBagOStuff {
                return true;
        }
 
-       public function incr( $key, $value = 1 ) {
+       public function incr( $key, $value = 1, $flags = 0 ) {
                $n = $this->get( $key );
                if ( $this->isInteger( $n ) ) {
-                       $n = max( $n + intval( $value ), 0 );
+                       $n = max( $n + (int)$value, 0 );
                        $this->bag[$key][self::KEY_VAL] = $n;
 
                        return $n;
@@ -120,6 +120,10 @@ class HashBagOStuff extends MediumSpecificBagOStuff {
                return false;
        }
 
+       public function decr( $key, $value = 1, $flags = 0 ) {
+               return $this->incr( $key, -$value, $flags );
+       }
+
        /**
         * Clear all values in cache
         */
index 1242501..cb3f621 100644 (file)
@@ -208,7 +208,7 @@ abstract class MediumSpecificBagOStuff extends BagOStuff {
                        $mainValue->{SerializedValueContainer::SEGMENTED_HASHES}
                );
 
-               return $this->deleteMulti( $orderedKeys, $flags );
+               return $this->deleteMulti( $orderedKeys, $flags & ~self::WRITE_PRUNE_SEGMENTS );
        }
 
        /**
@@ -269,12 +269,12 @@ abstract class MediumSpecificBagOStuff extends BagOStuff {
        final protected function mergeViaCas( $key, callable $callback, $exptime, $attempts, $flags ) {
                $attemptsLeft = $attempts;
                do {
-                       $casToken = null; // passed by reference
+                       $token = null; // passed by reference
                        // Get the old value and CAS token from cache
                        $this->clearLastError();
                        $currentValue = $this->resolveSegments(
                                $key,
-                               $this->doGet( $key, self::READ_LATEST, $casToken )
+                               $this->doGet( $key, $flags, $token )
                        );
                        if ( $this->getLastError() ) {
                                // Don't spam slow retries due to network problems (retry only on races)
@@ -302,7 +302,7 @@ abstract class MediumSpecificBagOStuff extends BagOStuff {
                                $success = $this->add( $key, $value, $exptime, $flags );
                        } else {
                                // Try to update the key, failing if it gets changed in the meantime
-                               $success = $this->cas( $casToken, $key, $value, $exptime, $flags );
+                               $success = $this->cas( $token, $key, $value, $exptime, $flags );
                        }
                        if ( $this->getLastError() ) {
                                // Don't spam slow retries due to network problems (retry only on races)
@@ -671,37 +671,16 @@ abstract class MediumSpecificBagOStuff extends BagOStuff {
                return $res;
        }
 
-       /**
-        * Decrease stored value of $key by $value while preserving its TTL
-        * @param string $key
-        * @param int $value Value to subtract from $key (default: 1) [optional]
-        * @return int|bool New value or false on failure
-        */
-       public function decr( $key, $value = 1 ) {
-               return $this->incr( $key, -$value );
-       }
-
-       /**
-        * Increase stored value of $key by $value while preserving its TTL
-        *
-        * This will create the key with value $init and TTL $ttl instead if not present
-        *
-        * @param string $key
-        * @param int $ttl
-        * @param int $value
-        * @param int $init
-        * @return int|bool New value or false on failure
-        * @since 1.24
-        */
-       public function incrWithInit( $key, $ttl, $value = 1, $init = 1 ) {
+       public function incrWithInit( $key, $exptime, $value = 1, $init = null, $flags = 0 ) {
+               $init = is_int( $init ) ? $init : $value;
                $this->clearLastError();
-               $newValue = $this->incr( $key, $value );
+               $newValue = $this->incr( $key, $value, $flags );
                if ( $newValue === false && !$this->getLastError() ) {
                        // No key set; initialize
-                       $newValue = $this->add( $key, (int)$init, $ttl ) ? $init : false;
+                       $newValue = $this->add( $key, (int)$init, $exptime, $flags ) ? $init : false;
                        if ( $newValue === false && !$this->getLastError() ) {
                                // Raced out initializing; increment
-                               $newValue = $this->incr( $key, $value );
+                               $newValue = $this->incr( $key, $value, $flags );
                        }
                }
 
@@ -771,26 +750,6 @@ abstract class MediumSpecificBagOStuff extends BagOStuff {
                $this->lastError = $err;
        }
 
-       /**
-        * Let a callback be run to avoid wasting time on special blocking calls
-        *
-        * The callbacks may or may not be called ever, in any particular order.
-        * They are likely to be invoked when something WRITE_SYNC is used used.
-        * They should follow a caching pattern as shown below, so that any code
-        * using the work will get it's result no matter what happens.
-        * @code
-        *     $result = null;
-        *     $workCallback = function () use ( &$result ) {
-        *         if ( !$result ) {
-        *             $result = ....
-        *         }
-        *         return $result;
-        *     }
-        * @endcode
-        *
-        * @param callable $workCallback
-        * @since 1.28
-        */
        final public function addBusyCallback( callable $workCallback ) {
                $this->busyCallbacks[] = $workCallback;
        }
@@ -922,14 +881,6 @@ abstract class MediumSpecificBagOStuff extends BagOStuff {
                return ( $value === (string)$integer );
        }
 
-       /**
-        * Construct a cache key.
-        *
-        * @param string $keyspace
-        * @param array $args
-        * @return string Colon-delimited list of $keyspace followed by escaped components of $args
-        * @since 1.27
-        */
        public function makeKeyInternal( $keyspace, $args ) {
                $key = $keyspace;
                foreach ( $args as $arg ) {
@@ -971,18 +922,10 @@ abstract class MediumSpecificBagOStuff extends BagOStuff {
                return $this->attrMap[$flag] ?? self::QOS_UNKNOWN;
        }
 
-       /**
-        * @return int|float The chunk size, in bytes, of segmented objects (INF for no limit)
-        * @since 1.34
-        */
        public function getSegmentationSize() {
                return $this->segmentationSize;
        }
 
-       /**
-        * @return int|float Maximum total segmented object size in bytes (INF for no limit)
-        * @since 1.34
-        */
        public function getSegmentedValueMaxSize() {
                return $this->segmentedValueMaxSize;
        }
index 12c1a7a..9bf3f42 100644 (file)
@@ -250,7 +250,7 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
                return $this->checkResult( $key, $result );
        }
 
-       public function incr( $key, $value = 1 ) {
+       public function incr( $key, $value = 1, $flags = 0 ) {
                $this->debug( "incr($key)" );
 
                $result = $this->acquireSyncClient()->increment( $key, $value );
@@ -258,7 +258,7 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
                return $this->checkResult( $key, $result );
        }
 
-       public function decr( $key, $value = 1 ) {
+       public function decr( $key, $value = 1, $flags = 0 ) {
                $this->debug( "decr($key)" );
 
                $result = $this->acquireSyncClient()->decrement( $key, $value );
index 8144231..fc6deef 100644 (file)
@@ -93,13 +93,13 @@ class MemcachedPhpBagOStuff extends MemcachedBagOStuff {
                );
        }
 
-       public function incr( $key, $value = 1 ) {
+       public function incr( $key, $value = 1, $flags = 0 ) {
                $n = $this->client->incr( $this->validateKeyEncoding( $key ), $value );
 
                return ( $n !== false && $n !== null ) ? $n : false;
        }
 
-       public function decr( $key, $value = 1 ) {
+       public function decr( $key, $value = 1, $flags = 0 ) {
                $n = $this->client->decr( $this->validateKeyEncoding( $key ), $value );
 
                return ( $n !== false && $n !== null ) ? $n : false;
index 31d73e1..d0aa380 100644 (file)
@@ -123,9 +123,10 @@ class MultiWriteBagOStuff extends BagOStuff {
                        $missIndexes[] = $i;
                }
 
-               if ( $value !== false
-                       && $missIndexes
-                       && $this->fieldHasFlags( $flags, self::READ_VERIFIED )
+               if (
+                       $value !== false &&
+                       $this->fieldHasFlags( $flags, self::READ_VERIFIED ) &&
+                       $missIndexes
                ) {
                        // Backfill the value to the higher (and often faster/smaller) cache tiers
                        $this->doWrite(
@@ -265,7 +266,7 @@ class MultiWriteBagOStuff extends BagOStuff {
                );
        }
 
-       public function incr( $key, $value = 1 ) {
+       public function incr( $key, $value = 1, $flags = 0 ) {
                return $this->doWrite(
                        $this->cacheIndexes,
                        $this->asyncWrites,
@@ -274,7 +275,7 @@ class MultiWriteBagOStuff extends BagOStuff {
                );
        }
 
-       public function decr( $key, $value = 1 ) {
+       public function decr( $key, $value = 1, $flags = 0 ) {
                return $this->doWrite(
                        $this->cacheIndexes,
                        $this->asyncWrites,
@@ -283,7 +284,7 @@ class MultiWriteBagOStuff extends BagOStuff {
                );
        }
 
-       public function incrWithInit( $key, $ttl, $value = 1, $init = 1 ) {
+       public function incrWithInit( $key, $exptime, $value = 1, $init = null, $flags = 0 ) {
                return $this->doWrite(
                        $this->cacheIndexes,
                        $this->asyncWrites,
index b8ce38b..82b5ac0 100644 (file)
@@ -188,11 +188,11 @@ class RESTBagOStuff extends MediumSpecificBagOStuff {
                return $this->handleError( "Failed to delete $key", $rcode, $rerr, $rhdrs, $rbody );
        }
 
-       public function incr( $key, $value = 1 ) {
+       public function incr( $key, $value = 1, $flags = 0 ) {
                // @TODO: make this atomic
                $n = $this->get( $key, self::READ_LATEST );
                if ( $this->isInteger( $n ) ) { // key exists?
-                       $n = max( $n + intval( $value ), 0 );
+                       $n = max( $n + (int)$value, 0 );
                        // @TODO: respect $exptime
                        return $this->set( $key, $n ) ? $n : false;
                }
@@ -200,6 +200,10 @@ class RESTBagOStuff extends MediumSpecificBagOStuff {
                return false;
        }
 
+       public function decr( $key, $value = 1, $flags = 0 ) {
+               return $this->incr( $key, -$value, $flags );
+       }
+
        /**
         * Processes the response body.
         *
index 252b1fa..57a2507 100644 (file)
@@ -364,7 +364,7 @@ class RedisBagOStuff extends MediumSpecificBagOStuff {
                return $result;
        }
 
-       public function incr( $key, $value = 1 ) {
+       public function incr( $key, $value = 1, $flags = 0 ) {
                $conn = $this->getConnection( $key );
                if ( !$conn ) {
                        return false;
@@ -386,6 +386,28 @@ class RedisBagOStuff extends MediumSpecificBagOStuff {
                return $result;
        }
 
+       public function decr( $key, $value = 1, $flags = 0 ) {
+               $conn = $this->getConnection( $key );
+               if ( !$conn ) {
+                       return false;
+               }
+
+               try {
+                       if ( !$conn->exists( $key ) ) {
+                               return false;
+                       }
+                       // @FIXME: on races, the key may have a 0 TTL
+                       $result = $conn->decrBy( $key, $value );
+               } catch ( RedisException $e ) {
+                       $result = false;
+                       $this->handleException( $conn, $e );
+               }
+
+               $this->logRequest( 'decr', $key, $conn->getServer(), $result );
+
+               return $result;
+       }
+
        protected function doChangeTTL( $key, $exptime, $flags ) {
                $conn = $this->getConnection( $key );
                if ( !$conn ) {
index e3ced0d..0b5ac46 100644 (file)
@@ -135,16 +135,16 @@ class ReplicatedBagOStuff extends BagOStuff {
                return $this->writeStore->changeTTLMulti( $keys, $exptime, $flags );
        }
 
-       public function incr( $key, $value = 1 ) {
-               return $this->writeStore->incr( $key, $value );
+       public function incr( $key, $value = 1, $flags = 0 ) {
+               return $this->writeStore->incr( $key, $value, $flags );
        }
 
-       public function decr( $key, $value = 1 ) {
-               return $this->writeStore->decr( $key, $value );
+       public function decr( $key, $value = 1, $flags = 0 ) {
+               return $this->writeStore->decr( $key, $value, $flags );
        }
 
-       public function incrWithInit( $key, $ttl, $value = 1, $init = 1 ) {
-               return $this->writeStore->incrWithInit( $key, $ttl, $value, $init );
+       public function incrWithInit( $key, $exptime, $value = 1, $init = null, $flags = 0 ) {
+               return $this->writeStore->incrWithInit( $key, $exptime, $value, $init, $flags );
        }
 
        public function getLastError() {
index 3c4efbb..5b38628 100644 (file)
@@ -100,14 +100,6 @@ class WinCacheBagOStuff extends MediumSpecificBagOStuff {
                return true;
        }
 
-       /**
-        * Construct a cache key.
-        *
-        * @since 1.27
-        * @param string $keyspace
-        * @param array $args
-        * @return string
-        */
        public function makeKeyInternal( $keyspace, $args ) {
                // WinCache keys have a maximum length of 150 characters. From that,
                // subtract the number of characters we need for the keyspace and for
@@ -136,13 +128,7 @@ class WinCacheBagOStuff extends MediumSpecificBagOStuff {
                return $keyspace . ':' . implode( ':', $args );
        }
 
-       /**
-        * Increase stored value of $key by $value while preserving its original TTL
-        * @param string $key Key to increase
-        * @param int $value Value to add to $key (Default 1)
-        * @return int|bool New value or false on failure
-        */
-       public function incr( $key, $value = 1 ) {
+       public function incr( $key, $value = 1, $flags = 0 ) {
                if ( !wincache_lock( $key ) ) { // optimize with FIFO lock
                        return false;
                }
@@ -160,4 +146,8 @@ class WinCacheBagOStuff extends MediumSpecificBagOStuff {
 
                return $n;
        }
+
+       public function decr( $key, $value = 1, $flags = 0 ) {
+               return $this->incr( $key, -$value, $flags );
+       }
 }
index 0318022..f1b2ce2 100644 (file)
@@ -174,6 +174,8 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        /** @var float Query rount trip time estimate */
        private $lastRoundTripEstimate = 0.0;
 
+       /** @var string Whether the database is a file on disk */
+       const ATTR_DB_IS_FILE = 'db-is-file';
        /** @var string Lock granularity is on the level of the entire database */
        const ATTR_DB_LEVEL_LOCKING = 'db-level-locking';
        /** @var string The SCHEMA keyword refers to a grouping of tables in a database */
@@ -245,13 +247,6 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                $this->cliMode = $params['cliMode'];
                $this->agent = $params['agent'];
                $this->flags = $params['flags'];
-               if ( $this->flags & self::DBO_DEFAULT ) {
-                       if ( $this->cliMode ) {
-                               $this->flags &= ~self::DBO_TRX;
-                       } else {
-                               $this->flags |= self::DBO_TRX;
-                       }
-               }
                $this->nonNativeInsertSelectBatchSize = $params['nonNativeInsertSelectBatchSize'] ?? 10000;
 
                $this->srvCache = $params['srvCache'] ?? new HashBagOStuff();
@@ -425,6 +420,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         */
        final public static function attributesFromType( $dbType, $driver = null ) {
                static $defaults = [
+                       self::ATTR_DB_IS_FILE => false,
                        self::ATTR_DB_LEVEL_LOCKING => false,
                        self::ATTR_SCHEMAS_AS_TABLE_GROUPS => false
                ];
index a7ebc86..197cdcc 100644 (file)
@@ -95,8 +95,8 @@ class DatabasePostgres extends Database {
                $this->password = $password;
 
                $connectVars = [
-                       // pg_connect() user $user as the default database. Since a database is required,
-                       // then pick a "don't care" database that is more likely to exist than that one.
+                       // A database must be specified in order to connect to Postgres. If $dbName is not
+                       // specified, then use the standard "postgres" database that should exist by default.
                        'dbname' => strlen( $dbName ) ? $dbName : 'postgres',
                        'user' => $user,
                        'password' => $password
@@ -1442,7 +1442,7 @@ SQL;
                return $row ? ( strtolower( $row->default_transaction_read_only ) === 'on' ) : false;
        }
 
-       public static function getAttributes() {
+       protected static function getAttributes() {
                return [ self::ATTR_SCHEMAS_AS_TABLE_GROUPS => true ];
        }
 
index b1521dc..8d417e6 100644 (file)
@@ -68,21 +68,21 @@ class DatabaseSqlite extends Database {
         *   - dbDirectory : directory containing the DB and the lock file directory
         *   - dbFilePath  : use this to force the path of the DB file
         *   - trxMode     : one of (deferred, immediate, exclusive)
-        * @param array $p
+        * @param array $params
         */
-       public function __construct( array $p ) {
-               if ( isset( $p['dbFilePath'] ) ) {
-                       $this->dbPath = $p['dbFilePath'];
-                       if ( !strlen( $p['dbname'] ) ) {
-                               $p['dbname'] = self::generateDatabaseName( $this->dbPath );
+       public function __construct( array $params ) {
+               if ( isset( $params['dbFilePath'] ) ) {
+                       $this->dbPath = $params['dbFilePath'];
+                       if ( !strlen( $params['dbname'] ) ) {
+                               $params['dbname'] = self::generateDatabaseName( $this->dbPath );
                        }
-               } elseif ( isset( $p['dbDirectory'] ) ) {
-                       $this->dbDir = $p['dbDirectory'];
+               } elseif ( isset( $params['dbDirectory'] ) ) {
+                       $this->dbDir = $params['dbDirectory'];
                }
 
-               parent::__construct( $p );
+               parent::__construct( $params );
 
-               $this->trxMode = strtoupper( $p['trxMode'] ?? '' );
+               $this->trxMode = strtoupper( $params['trxMode'] ?? '' );
 
                $lockDirectory = $this->getLockFileDirectory();
                if ( $lockDirectory !== null ) {
@@ -96,7 +96,10 @@ class DatabaseSqlite extends Database {
        }
 
        protected static function getAttributes() {
-               return [ self::ATTR_DB_LEVEL_LOCKING => true ];
+               return [
+                       self::ATTR_DB_IS_FILE => true,
+                       self::ATTR_DB_LEVEL_LOCKING => true
+               ];
        }
 
        /**
index 8768213..a64078a 100644 (file)
@@ -1082,8 +1082,9 @@ interface IDatabase {
         *
         * In systems like mysql/mariadb, different databases can easily be referenced on a single
         * connection merely by name, even in a single query via JOIN. On the other hand, Postgres
-        * treats databases as fully separate, only allowing mechanisms like postgres_fdw to
-        * effectively "mount" foreign DBs. This is true even among DBs on the same server.
+        * treats databases as logically separate, with different database users, requiring special
+        * mechanisms like postgres_fdw to "mount" foreign DBs. This is true even among DBs on the
+        * same server. Changing the selected database via selectDomain() requires a new connection.
         *
         * @return bool
         * @since 1.29
@@ -2032,11 +2033,11 @@ interface IDatabase {
        public function setSessionOptions( array $options );
 
        /**
-        * Set variables to be used in sourceFile/sourceStream, in preference to the
-        * ones in $GLOBALS. If an array is set here, $GLOBALS will not be used at
-        * all. If it's set to false, $GLOBALS will be used.
+        * Set schema variables to be used when streaming commands from SQL files or stdin
         *
-        * @param bool|array $vars Mapping variable name to value.
+        * Variables appear as SQL comments and are substituted by their corresponding values
+        *
+        * @param array|null $vars Map of (variable => value) or null to use the defaults
         */
        public function setSchemaVars( $vars );
 
index 4426654..c2e4c38 100644 (file)
@@ -155,8 +155,9 @@ abstract class LBFactory implements ILBFactory {
                $this->defaultGroup = $conf['defaultGroup'] ?? null;
                $this->secret = $conf['secret'] ?? '';
 
-               $this->id = mt_rand();
-               $this->ticket = mt_rand();
+               static $nextId, $nextTicket;
+               $this->id = $nextId = ( is_int( $nextId ) ? $nextId++ : mt_rand() );
+               $this->ticket = $nextTicket = ( is_int( $nextTicket ) ? $nextTicket++ : mt_rand() );
        }
 
        public function destroy() {
index f675b58..1a3495e 100644 (file)
@@ -312,13 +312,11 @@ class LBFactoryMulti extends LBFactory {
                foreach ( $loads as $serverName => $load ) {
                        $serverInfo = $template;
                        if ( $master ) {
-                               $serverInfo['master'] = true;
                                if ( $this->masterTemplateOverrides ) {
                                        $serverInfo = $this->masterTemplateOverrides + $serverInfo;
                                }
                                $master = false;
                        } else {
-                               $serverInfo['replica'] = true;
                        }
                        if ( isset( $this->templateOverridesByServer[$serverName] ) ) {
                                $serverInfo = $this->templateOverridesByServer[$serverName] + $serverInfo;
index fd76d88..eb47802 100644 (file)
@@ -58,14 +58,6 @@ class LBFactorySimple extends LBFactory {
                parent::__construct( $conf );
 
                $this->servers = $conf['servers'] ?? [];
-               foreach ( $this->servers as $i => $server ) {
-                       if ( $i == 0 ) {
-                               $this->servers[$i]['master'] = true;
-                       } else {
-                               $this->servers[$i]['replica'] = true;
-                       }
-               }
-
                $this->externalClusters = $conf['externalClusters'] ?? [];
                $this->loadMonitorClass = $conf['loadMonitorClass'] ?? 'LoadMonitor';
        }
index d088aa9..955e3d8 100644 (file)
@@ -121,6 +121,8 @@ class LoadBalancer implements ILoadBalancer {
        /** @var bool Whether any connection has been attempted yet */
        private $connectionAttempted = false;
 
+       /** var int An identifier for this class instance */
+       private $id;
        /** @var int|null Integer ID of the managing LBFactory instance or null if none */
        private $ownerId;
        /** @var string|bool Explicit DBO_TRX transaction round active or false if none */
@@ -171,11 +173,6 @@ class LoadBalancer implements ILoadBalancer {
                        if ( ++$listKey !== $i ) {
                                throw new UnexpectedValueException( 'List expected for "servers" parameter' );
                        }
-                       if ( $i == 0 ) {
-                               $server['master'] = true;
-                       } else {
-                               $server['replica'] = true;
-                       }
                        $this->servers[$i] = $server;
                        foreach ( ( $server['groupLoads'] ?? [] ) as $group => $ratio ) {
                                $this->groupLoads[$group][$i] = $ratio;
@@ -238,6 +235,8 @@ class LoadBalancer implements ILoadBalancer {
                $group = $params['defaultGroup'] ?? self::GROUP_GENERIC;
                $this->defaultGroup = isset( $this->groupLoads[$group] ) ? $group : self::GROUP_GENERIC;
 
+               static $nextId;
+               $this->id = $nextId = ( is_int( $nextId ) ? $nextId++ : mt_rand() );
                $this->ownerId = $params['ownerId'] ?? null;
        }
 
@@ -957,17 +956,7 @@ class LoadBalancer implements ILoadBalancer {
                $serverIndex = $conn->getLBInfo( 'serverIndex' );
                $refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
                if ( $serverIndex === null || $refCount === null ) {
-                       /**
-                        * This can happen in code like:
-                        *   foreach ( $dbs as $db ) {
-                        *     $conn = $lb->getConnection( $lb::DB_REPLICA, [], $db );
-                        *     ...
-                        *     $lb->reuseConnection( $conn );
-                        *   }
-                        * When a connection to the local DB is opened in this way, reuseConnection()
-                        * should be ignored
-                        */
-                       return;
+                       return; // non-foreign connection; no domain-use tracking to update
                } elseif ( $conn instanceof DBConnRef ) {
                        // DBConnRef already handles calling reuseConnection() and only passes the live
                        // Database instance to this method. Any caller passing in a DBConnRef is broken.
@@ -1064,47 +1053,45 @@ class LoadBalancer implements ILoadBalancer {
         *
         * @note If disable() was called on this LoadBalancer, this method will throw a DBAccessError.
         *
-        * @param int $i Server index
+        * @param int $i Specific server index
         * @param int $flags Class CONN_* constant bitfield
         * @return Database
         * @throws InvalidArgumentException When the server index is invalid
         * @throws UnexpectedValueException When the DB domain of the connection is corrupted
         */
        private function getLocalConnection( $i, $flags = 0 ) {
+               $autoCommit = ( ( $flags & self::CONN_TRX_AUTOCOMMIT ) == self::CONN_TRX_AUTOCOMMIT );
                // Connection handles required to be in auto-commit mode use a separate connection
                // pool since the main pool is effected by implicit and explicit transaction rounds
-               $autoCommit = ( ( $flags & self::CONN_TRX_AUTOCOMMIT ) == self::CONN_TRX_AUTOCOMMIT );
-
                $connKey = $autoCommit ? self::KEY_LOCAL_NOROUND : self::KEY_LOCAL;
+
                if ( isset( $this->conns[$connKey][$i][0] ) ) {
                        $conn = $this->conns[$connKey][$i][0];
                } else {
-                       // Open a new connection
-                       $server = $this->getServerInfoStrict( $i );
-                       $server['serverIndex'] = $i;
-                       $server['autoCommitOnly'] = $autoCommit;
-                       $conn = $this->reallyOpenConnection( $server, $this->localDomain );
-                       $host = $this->getServerName( $i );
+                       $conn = $this->reallyOpenConnection(
+                               $i,
+                               $this->localDomain,
+                               [ 'autoCommitOnly' => $autoCommit ]
+                       );
                        if ( $conn->isOpen() ) {
-                               $this->connLogger->debug(
-                                       __METHOD__ . ": connected to database $i at '$host'." );
+                               $this->connLogger->debug( __METHOD__ . ": opened new connection for $i" );
                                $this->conns[$connKey][$i][0] = $conn;
                        } else {
-                               $this->connLogger->warning(
-                                       __METHOD__ . ": failed to connect to database $i at '$host'." );
+                               $this->connLogger->warning( __METHOD__ . ": connection error for $i" );
                                $this->errorConnection = $conn;
                                $conn = false;
                        }
                }
 
-               // Final sanity check to make sure the right domain is selected
+               // Sanity check to make sure that the right domain is selected
                if (
                        $conn instanceof IDatabase &&
                        !$this->localDomain->isCompatible( $conn->getDomainID() )
                ) {
                        throw new UnexpectedValueException(
                                "Got connection to '{$conn->getDomainID()}', " .
-                               "but expected local domain ('{$this->localDomain}')" );
+                               "but expected local domain ('{$this->localDomain}')"
+                       );
                }
 
                return $conn;
@@ -1126,7 +1113,7 @@ class LoadBalancer implements ILoadBalancer {
         *
         * @note If disable() was called on this LoadBalancer, this method will throw a DBAccessError.
         *
-        * @param int $i Server index
+        * @param int $i Specific server index
         * @param string $domain Domain ID to open
         * @param int $flags Class CONN_* constant bitfield
         * @return Database|bool Returns false on connection error
@@ -1136,10 +1123,9 @@ class LoadBalancer implements ILoadBalancer {
         */
        private function getForeignConnection( $i, $domain, $flags = 0 ) {
                $domainInstance = DatabaseDomain::newFromId( $domain );
+               $autoCommit = ( ( $flags & self::CONN_TRX_AUTOCOMMIT ) == self::CONN_TRX_AUTOCOMMIT );
                // Connection handles required to be in auto-commit mode use a separate connection
                // pool since the main pool is effected by implicit and explicit transaction rounds
-               $autoCommit = ( ( $flags & self::CONN_TRX_AUTOCOMMIT ) == self::CONN_TRX_AUTOCOMMIT );
-
                if ( $autoCommit ) {
                        $connFreeKey = self::KEY_FOREIGN_FREE_NOROUND;
                        $connInUseKey = self::KEY_FOREIGN_INUSE_NOROUND;
@@ -1192,26 +1178,28 @@ class LoadBalancer implements ILoadBalancer {
                }
 
                if ( !$conn ) {
-                       // Open a new connection
-                       $server = $this->getServerInfoStrict( $i );
-                       $server['serverIndex'] = $i;
-                       $server['foreignPoolRefCount'] = 0;
-                       $server['foreign'] = true;
-                       $server['autoCommitOnly'] = $autoCommit;
-                       $conn = $this->reallyOpenConnection( $server, $domainInstance );
-                       if ( !$conn->isOpen() ) {
-                               $this->connLogger->warning( __METHOD__ . ": connection error for $i/$domain" );
-                               $this->errorConnection = $conn;
-                               $conn = false;
-                       } else {
+                       $conn = $this->reallyOpenConnection(
+                               $i,
+                               $domainInstance,
+                               [
+                                       'autoCommitOnly' => $autoCommit,
+                                       'foreign' => true,
+                                       'foreignPoolRefCount' => 0
+                               ]
+                       );
+                       if ( $conn->isOpen() ) {
                                // Note that if $domain is an empty string, getDomainID() might not match it
                                $this->conns[$connInUseKey][$i][$conn->getDomainID()] = $conn;
                                $this->connLogger->debug( __METHOD__ . ": opened new connection for $i/$domain" );
+                       } else {
+                               $this->connLogger->warning( __METHOD__ . ": connection error for $i/$domain" );
+                               $this->errorConnection = $conn;
+                               $conn = false;
                        }
                }
 
                if ( $conn instanceof IDatabase ) {
-                       // Final sanity check to make sure the right domain is selected
+                       // Sanity check to make sure that the right domain is selected
                        if ( !$domainInstance->isCompatible( $conn->getDomainID() ) ) {
                                throw new UnexpectedValueException(
                                        "Got connection to '{$conn->getDomainID()}', but expected '$domain'" );
@@ -1246,94 +1234,101 @@ class LoadBalancer implements ILoadBalancer {
         *
         * Returns a Database object whether or not the connection was successful.
         *
-        * @param array $server
+        * @param int $i Specific server index
         * @param DatabaseDomain $domain Domain the connection is for, possibly unspecified
+        * @param array $lbInfo Additional information for setLBInfo()
         * @return Database
         * @throws DBAccessError
         * @throws InvalidArgumentException
         */
-       protected function reallyOpenConnection( array $server, DatabaseDomain $domain ) {
+       protected function reallyOpenConnection( $i, DatabaseDomain $domain, array $lbInfo ) {
                if ( $this->disabled ) {
                        throw new DBAccessError();
                }
 
-               if ( $domain->getDatabase() === null ) {
-                       // The database domain does not specify a DB name and some database systems require a
-                       // valid DB specified on connection. The $server configuration array contains a default
-                       // DB name to use for connections in such cases.
-                       if ( $server['type'] === 'mysql' ) {
-                               // For MySQL, DATABASE and SCHEMA are synonyms, connections need not specify a DB,
-                               // and the DB name in $server might not exist due to legacy reasons (the default
-                               // domain used to ignore the local LB domain, even when mismatched).
-                               $server['dbname'] = null;
-                       }
-               } else {
-                       $server['dbname'] = $domain->getDatabase();
-               }
-
-               if ( $domain->getSchema() !== null ) {
-                       $server['schema'] = $domain->getSchema();
-               }
-
-               // It is always possible to connect with any prefix, even the empty string
-               $server['tablePrefix'] = $domain->getTablePrefix();
-
-               // Let the handle know what the cluster master is (e.g. "db1052")
-               $masterName = $this->getServerName( $this->getWriterIndex() );
-               $server['clusterMasterHost'] = $masterName;
-
-               $server['srvCache'] = $this->srvCache;
-               // Set loggers and profilers
-               $server['connLogger'] = $this->connLogger;
-               $server['queryLogger'] = $this->queryLogger;
-               $server['errorLogger'] = $this->errorLogger;
-               $server['deprecationLogger'] = $this->deprecationLogger;
-               $server['profiler'] = $this->profiler;
-               $server['trxProfiler'] = $this->trxProfiler;
-               // Use the same agent and PHP mode for all DB handles
-               $server['cliMode'] = $this->cliMode;
-               $server['agent'] = $this->agent;
-               // Use DBO_DEFAULT flags by default for LoadBalancer managed databases. Assume that the
-               // application calls LoadBalancer::commitMasterChanges() before the PHP script completes.
-               $server['flags'] = $server['flags'] ?? IDatabase::DBO_DEFAULT;
-
-               // Create a live connection object
+               $server = $this->getServerInfoStrict( $i );
+               $server = array_merge( $server, [
+                       // Use the database specified in $domain (null means "none or entrypoint DB");
+                       // fallback to the $server default if the RDBMs is an embedded library using a file
+                       // on disk since there would be nothing to access to without a DB/file name.
+                       'dbname' => $this->getServerAttributes( $i )[Database::ATTR_DB_IS_FILE]
+                               ? ( $domain->getDatabase() ?? $server['dbname'] ?? null )
+                               : $domain->getDatabase(),
+                       // Override the $server default schema with that of $domain if specified
+                       'schema' => $domain->getSchema() ?? $server['schema'] ?? null,
+                       // Use the table prefix specified in $domain
+                       'tablePrefix' => $domain->getTablePrefix(),
+                       // Participate in transaction rounds if $server does not specify otherwise
+                       'flags' => $server['flags'] ?? IDatabase::DBO_DEFAULT,
+                       // Inject the PHP execution mode and the agent string
+                       'cliMode' => $this->cliMode,
+                       'agent' => $this->agent,
+                       // Inject object and callback dependencies
+                       'srvCache' => $this->srvCache,
+                       'connLogger' => $this->connLogger,
+                       'queryLogger' => $this->queryLogger,
+                       'errorLogger' => $this->errorLogger,
+                       'deprecationLogger' => $this->deprecationLogger,
+                       'profiler' => $this->profiler,
+                       'trxProfiler' => $this->trxProfiler
+               ] );
+
+               $lbInfo = array_merge( $lbInfo, [
+                       'ownerId' => $this->id,
+                       'serverIndex' => $i,
+                       'master' => ( $i === $this->getWriterIndex() ),
+                       'replica' => ( $i !== $this->getWriterIndex() ),
+                       // Name of the master server of the relevant DB cluster (e.g. "db1052")
+                       'clusterMasterHost' => $this->getServerName( $this->getWriterIndex() )
+               ] );
+
+               $conn = Database::factory( $server['type'], $server, Database::NEW_UNCONNECTED );
                try {
-                       $db = Database::factory( $server['type'], $server );
-                       // Log when many connection are made on requests
+                       $conn->initConnection();
                        ++$this->connectionCounter;
-                       $currentConnCount = $this->getCurrentConnectionCount();
-                       if ( $currentConnCount >= self::CONN_HELD_WARN_THRESHOLD ) {
-                               $this->perfLogger->warning(
-                                       __METHOD__ . ": {connections}+ connections made (master={masterdb})",
-                                       [ 'connections' => $currentConnCount, 'masterdb' => $masterName ]
-                               );
-                       }
                } catch ( DBConnectionError $e ) {
-                       // FIXME: This is probably the ugliest thing I have ever done to
-                       // PHP. I'm half-expecting it to segfault, just out of disgust. -- TS
-                       $db = $e->db;
+                       // ignore; let the DB handle the logging
                }
 
-               $db->setLBInfo( $server );
-               $db->setLazyMasterHandle(
-                       $this->getLazyConnectionRef( self::DB_MASTER, [], $db->getDomainID() )
-               );
-               $db->setTableAliases( $this->tableAliases );
-               $db->setIndexAliases( $this->indexAliases );
-
-               if ( $server['serverIndex'] === $this->getWriterIndex() ) {
+               $conn->setLBInfo( $lbInfo );
+               if ( $conn->getFlag( $conn::DBO_DEFAULT ) ) {
+                       if ( $this->cliMode ) {
+                               $conn->clearFlag( $conn::DBO_TRX );
+                       } else {
+                               $conn->setFlag( $conn::DBO_TRX );
+                       }
+               }
+               if ( $i === $this->getWriterIndex() ) {
                        if ( $this->trxRoundId !== false ) {
-                               $this->applyTransactionRoundFlags( $db );
+                               $this->applyTransactionRoundFlags( $conn );
                        }
                        foreach ( $this->trxRecurringCallbacks as $name => $callback ) {
-                               $db->setTransactionListener( $name, $callback );
+                               $conn->setTransactionListener( $name, $callback );
                        }
                }
+               $conn->setTableAliases( $this->tableAliases );
+               $conn->setIndexAliases( $this->indexAliases );
+
+               $conn->setLazyMasterHandle(
+                       $this->getLazyConnectionRef( self::DB_MASTER, [], $conn->getDomainID() )
+               );
 
                $this->lazyLoadReplicationPositions(); // session consistency
 
-               return $db;
+               // Log when many connection are made on requests
+               $count = $this->getCurrentConnectionCount();
+               if ( $count >= self::CONN_HELD_WARN_THRESHOLD ) {
+                       $this->perfLogger->warning(
+                               __METHOD__ . ": {connections}+ connections made (master={masterdb})",
+                               [
+                                       'connections' => $count,
+                                       'dbserver' => $conn->getServer(),
+                                       'masterdb' => $conn->getLBInfo( 'clusterMasterHost' )
+                               ]
+                       );
+               }
+
+               return $conn;
        }
 
        /**
@@ -2109,8 +2104,8 @@ class LoadBalancer implements ILoadBalancer {
        public function waitForMasterPos( IDatabase $conn, $pos = false, $timeout = null ) {
                $timeout = max( 1, $timeout ?: $this->waitTimeout );
 
-               if ( $this->getServerCount() <= 1 || !$conn->getLBInfo( 'replica' ) ) {
-                       return true; // server is not a replica DB
+               if ( $conn->getLBInfo( 'serverIndex' ) === $this->getWriterIndex() ) {
+                       return true; // not a replica DB server
                }
 
                if ( !$pos ) {
@@ -2226,9 +2221,9 @@ class LoadBalancer implements ILoadBalancer {
                ) );
 
                // Update the prefix for all local connections...
-               $this->forEachOpenConnection( function ( IDatabase $db ) use ( $prefix ) {
-                       if ( !$db->getLBInfo( 'foreign' ) ) {
-                               $db->tablePrefix( $prefix );
+               $this->forEachOpenConnection( function ( IDatabase $conn ) use ( $prefix ) {
+                       if ( !$conn->getLBInfo( 'foreign' ) ) {
+                               $conn->tablePrefix( $prefix );
                        }
                } );
        }
index 4c68833..2ad24b9 100644 (file)
@@ -83,7 +83,7 @@ class LoadBalancerSingle extends LoadBalancer {
                ) );
        }
 
-       protected function reallyOpenConnection( array $server, DatabaseDomain $domain ) {
+       protected function reallyOpenConnection( $i, DatabaseDomain $domain, array $lbInfo = [] ) {
                return $this->db;
        }
 
index a1820ab..db4838f 100644 (file)
@@ -22,6 +22,7 @@
  */
 
 use MediaWiki\MediaWikiServices;
+use Wikimedia\AtEase\AtEase;
 use Wikimedia\Rdbms\Database;
 use Wikimedia\Rdbms\IDatabase;
 use Wikimedia\Rdbms\DBError;
@@ -169,7 +170,7 @@ class SqlBagOStuff extends MediumSpecificBagOStuff {
         * @return IMaintainableDatabase
         * @throws MWException
         */
-       private function getDB( $shardIndex ) {
+       private function getConnection( $shardIndex ) {
                if ( $shardIndex >= $this->numServerShards ) {
                        throw new MWException( __METHOD__ . ": Invalid server index \"$shardIndex\"" );
                }
@@ -290,7 +291,7 @@ class SqlBagOStuff extends MediumSpecificBagOStuff {
                $dataRows = [];
                foreach ( $keysByTable as $shardIndex => $serverKeys ) {
                        try {
-                               $db = $this->getDB( $shardIndex );
+                               $db = $this->getConnection( $shardIndex );
                                foreach ( $serverKeys as $tableName => $tableKeys ) {
                                        $res = $db->select( $tableName,
                                                [ 'keyname', 'value', 'exptime' ],
@@ -321,7 +322,7 @@ class SqlBagOStuff extends MediumSpecificBagOStuff {
                                $this->debug( "get: retrieved data; expiry time is " . $row->exptime );
                                $db = null; // in case of connection failure
                                try {
-                                       $db = $this->getDB( $row->shardIndex );
+                                       $db = $this->getConnection( $row->shardIndex );
                                        if ( $this->isExpired( $db, $row->exptime ) ) { // MISS
                                                $this->debug( "get: key has expired" );
                                        } else { // HIT
@@ -364,7 +365,7 @@ class SqlBagOStuff extends MediumSpecificBagOStuff {
                foreach ( $keysByTable as $shardIndex => $serverKeys ) {
                        $db = null; // in case of connection failure
                        try {
-                               $db = $this->getDB( $shardIndex );
+                               $db = $this->getConnection( $shardIndex );
                                $this->occasionallyGarbageCollect( $db ); // expire old entries if any
                                $dbExpiry = $exptime ? $db->timestamp( $exptime ) : $this->getMaxDateTime( $db );
                        } catch ( DBError $e ) {
@@ -479,7 +480,7 @@ class SqlBagOStuff extends MediumSpecificBagOStuff {
                $silenceScope = $this->silenceTransactionProfiler();
                $db = null; // in case of connection failure
                try {
-                       $db = $this->getDB( $shardIndex );
+                       $db = $this->getConnection( $shardIndex );
                        // (T26425) use a replace if the db supports it instead of
                        // delete/insert to avoid clashes with conflicting keynames
                        $db->update(
@@ -525,7 +526,7 @@ class SqlBagOStuff extends MediumSpecificBagOStuff {
                return $this->modifyMulti( [ $key => null ], 0, $flags, self::$OP_DELETE );
        }
 
-       public function incr( $key, $step = 1 ) {
+       public function incr( $key, $step = 1, $flags = 0 ) {
                list( $shardIndex, $tableName ) = $this->getTableByKey( $key );
 
                $newCount = false;
@@ -533,7 +534,7 @@ class SqlBagOStuff extends MediumSpecificBagOStuff {
                $silenceScope = $this->silenceTransactionProfiler();
                $db = null; // in case of connection failure
                try {
-                       $db = $this->getDB( $shardIndex );
+                       $db = $this->getConnection( $shardIndex );
                        $encTimestamp = $db->addQuotes( $db->timestamp() );
                        $db->update(
                                $tableName,
@@ -559,6 +560,10 @@ class SqlBagOStuff extends MediumSpecificBagOStuff {
                return $newCount;
        }
 
+       public function decr( $key, $value = 1, $flags = 0 ) {
+               return $this->incr( $key, -$value, $flags );
+       }
+
        public function changeTTLMulti( array $keys, $exptime, $flags = 0 ) {
                return $this->modifyMulti(
                        array_fill_keys( $keys, null ),
@@ -647,7 +652,7 @@ class SqlBagOStuff extends MediumSpecificBagOStuff {
                foreach ( $shardIndexes as $numServersDone => $shardIndex ) {
                        $db = null; // in case of connection failure
                        try {
-                               $db = $this->getDB( $shardIndex );
+                               $db = $this->getConnection( $shardIndex );
                                $this->deleteServerObjectsExpiringBefore(
                                        $db,
                                        $timestamp,
@@ -752,7 +757,7 @@ class SqlBagOStuff extends MediumSpecificBagOStuff {
                for ( $shardIndex = 0; $shardIndex < $this->numServerShards; $shardIndex++ ) {
                        $db = null; // in case of connection failure
                        try {
-                               $db = $this->getDB( $shardIndex );
+                               $db = $this->getConnection( $shardIndex );
                                for ( $i = 0; $i < $this->numTableShards; $i++ ) {
                                        $db->delete( $this->getTableNameByShard( $i ), '*', __METHOD__ );
                                }
@@ -779,7 +784,7 @@ class SqlBagOStuff extends MediumSpecificBagOStuff {
 
                $db = null; // in case of connection failure
                try {
-                       $db = $this->getDB( $shardIndex );
+                       $db = $this->getConnection( $shardIndex );
                        $ok = $db->lock( $key, __METHOD__, $timeout );
                        if ( $ok ) {
                                $this->locks[$key] = [ 'class' => $rclass, 'depth' => 1 ];
@@ -811,7 +816,7 @@ class SqlBagOStuff extends MediumSpecificBagOStuff {
 
                        $db = null; // in case of connection failure
                        try {
-                               $db = $this->getDB( $shardIndex );
+                               $db = $this->getConnection( $shardIndex );
                                $ok = $db->unlock( $key, __METHOD__ );
                                if ( !$ok ) {
                                        $this->logger->warning(
@@ -862,9 +867,9 @@ class SqlBagOStuff extends MediumSpecificBagOStuff {
                }
 
                if ( function_exists( 'gzinflate' ) ) {
-                       Wikimedia\suppressWarnings();
+                       AtEase::suppressWarnings();
                        $decomp = gzinflate( $serial );
-                       Wikimedia\restoreWarnings();
+                       AtEase::restoreWarnings();
 
                        if ( $decomp !== false ) {
                                $serial = $decomp;
@@ -947,7 +952,7 @@ class SqlBagOStuff extends MediumSpecificBagOStuff {
         */
        public function createTables() {
                for ( $shardIndex = 0; $shardIndex < $this->numServerShards; $shardIndex++ ) {
-                       $db = $this->getDB( $shardIndex );
+                       $db = $this->getConnection( $shardIndex );
                        if ( $db->getType() !== 'mysql' ) {
                                throw new MWException( __METHOD__ . ' is not supported on this DB server' );
                        }
index 5024395..ea12e42 100644 (file)
@@ -28,7 +28,6 @@ require_once __DIR__ . '/Maintenance.php';
 
 use MediaWiki\Linker\LinkTarget;
 use MediaWiki\MediaWikiServices;
-use MediaWiki\Storage\RevisionRecord;
 use Wikimedia\Rdbms\IResultWrapper;
 use Wikimedia\Rdbms\IMaintainableDatabase;
 
@@ -73,8 +72,6 @@ class NamespaceDupes extends Maintenance {
        }
 
        public function execute() {
-               $this->db = $this->getDB( DB_MASTER );
-
                $options = [
                        'fix' => $this->hasOption( 'fix' ),
                        'merge' => $this->hasOption( 'merge' ),
@@ -254,8 +251,8 @@ class NamespaceDupes extends Maintenance {
                foreach ( $targets as $row ) {
                        // Find the new title and determine the action to take
 
-                       $newTitle = $this->getDestinationTitle( $ns, $name,
-                               $row->page_namespace, $row->page_title, $options );
+                       $newTitle = $this->getDestinationTitle(
+                               $ns, $name, $row->page_namespace, $row->page_title );
                        $logStatus = false;
                        if ( !$newTitle ) {
                                $logStatus = 'invalid title';
@@ -338,18 +335,20 @@ class NamespaceDupes extends Maintenance {
        private function checkLinkTable( $table, $fieldPrefix, $ns, $name, $options,
                $extraConds = []
        ) {
+               $dbw = $this->getDB( DB_MASTER );
+
                $batchConds = [];
                $fromField = "{$fieldPrefix}_from";
                $namespaceField = "{$fieldPrefix}_namespace";
                $titleField = "{$fieldPrefix}_title";
                $batchSize = 500;
                while ( true ) {
-                       $res = $this->db->select(
+                       $res = $dbw->select(
                                $table,
                                [ $fromField, $namespaceField, $titleField ],
                                array_merge( $batchConds, $extraConds, [
                                        $namespaceField => 0,
-                                       $titleField . $this->db->buildLike( "$name:", $this->db->anyString() )
+                                       $titleField . $dbw->buildLike( "$name:", $dbw->anyString() )
                                ] ),
                                __METHOD__,
                                [
@@ -364,8 +363,8 @@ class NamespaceDupes extends Maintenance {
                        foreach ( $res as $row ) {
                                $logTitle = "from={$row->$fromField} ns={$row->$namespaceField} " .
                                        "dbk={$row->$titleField}";
-                               $destTitle = $this->getDestinationTitle( $ns, $name,
-                                       $row->$namespaceField, $row->$titleField, $options );
+                               $destTitle = $this->getDestinationTitle(
+                                       $ns, $name, $row->$namespaceField, $row->$titleField );
                                $this->totalLinks++;
                                if ( !$destTitle ) {
                                        $this->output( "$table $logTitle *** INVALID\n" );
@@ -378,7 +377,7 @@ class NamespaceDupes extends Maintenance {
                                        continue;
                                }
 
-                               $this->db->update( $table,
+                               $dbw->update( $table,
                                        // SET
                                        [
                                                $namespaceField => $destTitle->getNamespace(),
@@ -396,8 +395,8 @@ class NamespaceDupes extends Maintenance {
                                $this->output( "$table $logTitle -> " .
                                        $destTitle->getPrefixedDBkey() . "\n" );
                        }
-                       $encLastTitle = $this->db->addQuotes( $row->$titleField );
-                       $encLastFrom = $this->db->addQuotes( $row->$fromField );
+                       $encLastTitle = $dbw->addQuotes( $row->$titleField );
+                       $encLastFrom = $dbw->addQuotes( $row->$fromField );
 
                        $batchConds = [
                                "$titleField > $encLastTitle " .
@@ -433,6 +432,8 @@ class NamespaceDupes extends Maintenance {
         * @return IResultWrapper
         */
        private function getTargetList( $ns, $name, $options ) {
+               $dbw = $this->getDB( DB_MASTER );
+
                if (
                        $options['move-talk'] &&
                        MediaWikiServices::getInstance()->getNamespaceInfo()->isSubject( $ns )
@@ -442,7 +443,7 @@ class NamespaceDupes extends Maintenance {
                        $checkNamespaces = NS_MAIN;
                }
 
-               return $this->db->select( 'page',
+               return $dbw->select( 'page',
                        [
                                'page_id',
                                'page_title',
@@ -450,7 +451,7 @@ class NamespaceDupes extends Maintenance {
                        ],
                        [
                                'page_namespace' => $checkNamespaces,
-                               'page_title' . $this->db->buildLike( "$name:", $this->db->anyString() ),
+                               'page_title' . $dbw->buildLike( "$name:", $dbw->anyString() ),
                        ],
                        __METHOD__
                );
@@ -462,10 +463,9 @@ class NamespaceDupes extends Maintenance {
         * @param string $name The conflicting prefix
         * @param int $sourceNs The source namespace
         * @param int $sourceDbk The source DB key (i.e. page_title)
-        * @param array $options Associative array of validated command-line options
         * @return Title|false
         */
-       private function getDestinationTitle( $ns, $name, $sourceNs, $sourceDbk, $options ) {
+       private function getDestinationTitle( $ns, $name, $sourceNs, $sourceDbk ) {
                $dbk = substr( $sourceDbk, strlen( "$name:" ) );
                if ( $ns == 0 ) {
                        // An interwiki; try an alternate encoding with '-' for ':'
@@ -518,7 +518,9 @@ class NamespaceDupes extends Maintenance {
         * @return bool
         */
        private function movePage( $id, LinkTarget $newLinkTarget ) {
-               $this->db->update( 'page',
+               $dbw = $this->getDB( DB_MASTER );
+
+               $dbw->update( 'page',
                        [
                                "page_namespace" => $newLinkTarget->getNamespace(),
                                "page_title" => $newLinkTarget->getDBkey(),
@@ -535,7 +537,7 @@ class NamespaceDupes extends Maintenance {
                        [ 'imagelinks', 'il' ] ];
                foreach ( $fromNamespaceTables as $tableInfo ) {
                        list( $table, $fieldPrefix ) = $tableInfo;
-                       $this->db->update( $table,
+                       $dbw->update( $table,
                                // SET
                                [ "{$fieldPrefix}_from_namespace" => $newLinkTarget->getNamespace() ],
                                // WHERE
@@ -559,12 +561,8 @@ class NamespaceDupes extends Maintenance {
         * @return bool
         */
        private function canMerge( $id, LinkTarget $linkTarget, &$logStatus ) {
-               $latestDest = Revision::newFromTitle(
-                       $linkTarget, 0, RevisionRecord::READ_LATEST
-               );
-               $latestSource = Revision::newFromPageId(
-                       $id, 0, RevisionRecord::READ_LATEST
-               );
+               $latestDest = Revision::newFromTitle( $linkTarget, 0, Revision::READ_LATEST );
+               $latestSource = Revision::newFromPageId( $id, 0, Revision::READ_LATEST );
                if ( $latestSource->getTimestamp() > $latestDest->getTimestamp() ) {
                        $logStatus = 'cannot merge since source is later';
                        return false;
@@ -581,6 +579,8 @@ class NamespaceDupes extends Maintenance {
         * @return bool
         */
        private function mergePage( $row, Title $newTitle ) {
+               $dbw = $this->getDB( DB_MASTER );
+
                $id = $row->page_id;
 
                // Construct the WikiPage object we will need later, while the
@@ -592,17 +592,17 @@ class NamespaceDupes extends Maintenance {
                $wikiPage->loadPageData( 'fromdbmaster' );
 
                $destId = $newTitle->getArticleID();
-               $this->beginTransaction( $this->db, __METHOD__ );
-               $this->db->update( 'revision',
+               $this->beginTransaction( $dbw, __METHOD__ );
+               $dbw->update( 'revision',
                        // SET
                        [ 'rev_page' => $destId ],
                        // WHERE
                        [ 'rev_page' => $id ],
                        __METHOD__ );
 
-               $this->db->delete( 'page', [ 'page_id' => $id ], __METHOD__ );
+               $dbw->delete( 'page', [ 'page_id' => $id ], __METHOD__ );
 
-               $this->commitTransaction( $this->db, __METHOD__ );
+               $this->commitTransaction( $dbw, __METHOD__ );
 
                /* Call LinksDeletionUpdate to delete outgoing links from the old title,
                 * and update category counts.
index 2e4cc88..7cc7575 100644 (file)
@@ -27,7 +27,6 @@
 
 require_once __DIR__ . '/Maintenance.php';
 
-use Wikimedia\Rdbms\IMaintainableDatabase;
 use Wikimedia\Rdbms\DatabaseSqlite;
 
 /**
@@ -38,11 +37,6 @@ use Wikimedia\Rdbms\DatabaseSqlite;
 class RebuildTextIndex extends Maintenance {
        const RTI_CHUNK_SIZE = 500;
 
-       /**
-        * @var IMaintainableDatabase
-        */
-       private $db;
-
        public function __construct() {
                parent::__construct();
                $this->addDescription( 'Rebuild search index table from scratch' );
@@ -54,23 +48,19 @@ class RebuildTextIndex extends Maintenance {
 
        public function execute() {
                // Shouldn't be needed for Postgres
-               $this->db = $this->getDB( DB_MASTER );
-               if ( $this->db->getType() == 'postgres' ) {
+               $dbw = $this->getDB( DB_MASTER );
+               if ( $dbw->getType() == 'postgres' ) {
                        $this->fatalError( "This script is not needed when using Postgres.\n" );
                }
 
-               if ( $this->db->getType() == 'sqlite' ) {
+               if ( $dbw->getType() == 'sqlite' ) {
                        if ( !DatabaseSqlite::getFulltextSearchModule() ) {
                                $this->fatalError( "Your version of SQLite module for PHP doesn't "
                                        . "support full-text search (FTS3).\n" );
                        }
-                       if ( !$this->db->checkForEnabledSearch() ) {
-                               $this->fatalError( "Your database schema is not configured for "
-                                       . "full-text search support. Run update.php.\n" );
-                       }
                }
 
-               if ( $this->db->getType() == 'mysql' ) {
+               if ( $dbw->getType() == 'mysql' ) {
                        $this->dropMysqlTextIndex();
                        $this->clearSearchIndex();
                        $this->populateSearchIndex();
@@ -87,8 +77,9 @@ class RebuildTextIndex extends Maintenance {
         * Populates the search index with content from all pages
         */
        protected function populateSearchIndex() {
-               $res = $this->db->select( 'page', 'MAX(page_id) AS count' );
-               $s = $this->db->fetchObject( $res );
+               $dbw = $this->getDB( DB_MASTER );
+               $res = $dbw->select( 'page', 'MAX(page_id) AS count' );
+               $s = $dbw->fetchObject( $res );
                $count = $s->count;
                $this->output( "Rebuilding index fields for {$count} pages...\n" );
                $n = 0;
@@ -101,7 +92,7 @@ class RebuildTextIndex extends Maintenance {
                        }
                        $end = $n + self::RTI_CHUNK_SIZE - 1;
 
-                       $res = $this->db->select(
+                       $res = $dbw->select(
                                $revQuery['tables'],
                                $revQuery['fields'],
                                [ "page_id BETWEEN $n AND $end", 'page_latest = rev_id' ],
@@ -131,11 +122,12 @@ class RebuildTextIndex extends Maintenance {
         * (MySQL only) Drops fulltext index before populating the table.
         */
        private function dropMysqlTextIndex() {
-               $searchindex = $this->db->tableName( 'searchindex' );
-               if ( $this->db->indexExists( 'searchindex', 'si_title', __METHOD__ ) ) {
+               $dbw = $this->getDB( DB_MASTER );
+               $searchindex = $dbw->tableName( 'searchindex' );
+               if ( $dbw->indexExists( 'searchindex', 'si_title', __METHOD__ ) ) {
                        $this->output( "Dropping index...\n" );
                        $sql = "ALTER TABLE $searchindex DROP INDEX si_title, DROP INDEX si_text";
-                       $this->db->query( $sql, __METHOD__ );
+                       $dbw->query( $sql, __METHOD__ );
                }
        }
 
@@ -143,11 +135,12 @@ class RebuildTextIndex extends Maintenance {
         * (MySQL only) Adds back fulltext index after populating the table.
         */
        private function createMysqlTextIndex() {
-               $searchindex = $this->db->tableName( 'searchindex' );
+               $dbw = $this->getDB( DB_MASTER );
+               $searchindex = $dbw->tableName( 'searchindex' );
                $this->output( "\nRebuild the index...\n" );
                foreach ( [ 'si_title', 'si_text' ] as $field ) {
                        $sql = "ALTER TABLE $searchindex ADD FULLTEXT $field ($field)";
-                       $this->db->query( $sql, __METHOD__ );
+                       $dbw->query( $sql, __METHOD__ );
                }
        }
 
@@ -155,8 +148,9 @@ class RebuildTextIndex extends Maintenance {
         * Deletes everything from search index.
         */
        private function clearSearchIndex() {
+               $dbw = $this->getDB( DB_MASTER );
                $this->output( 'Clearing searchindex table...' );
-               $this->db->delete( 'searchindex', '*', __METHOD__ );
+               $dbw->delete( 'searchindex', '*', __METHOD__ );
                $this->output( "Done\n" );
        }
 }
index 21d8b2d..612c092 100644 (file)
@@ -67,7 +67,7 @@ class MwSql extends Maintenance {
                $replicaDB = $this->getOption( 'replicadb', $this->getOption( 'slave', '' ) );
                if ( $replicaDB === 'any' ) {
                        $index = DB_REPLICA;
-               } elseif ( $replicaDB != '' ) {
+               } elseif ( $replicaDB !== '' ) {
                        $index = null;
                        $serverCount = $lb->getServerCount();
                        for ( $i = 0; $i < $serverCount; ++$i ) {
@@ -76,7 +76,7 @@ class MwSql extends Maintenance {
                                        break;
                                }
                        }
-                       if ( $index === null ) {
+                       if ( $index === null || $index === $lb->getWriterIndex() ) {
                                $this->fatalError( "No replica DB server configured with the name '$replicaDB'." );
                        }
                } else {
index bfd4d97..b9d5792 100644 (file)
  * @ingroup Maintenance
  */
 
+use MediaWiki\MediaWikiServices;
+
+use Wikimedia\Rdbms\DatabaseSqlite;
+
 require_once __DIR__ . '/Maintenance.php';
 
 /**
@@ -59,37 +63,37 @@ class SqliteMaintenance extends Maintenance {
                        return;
                }
 
-               $this->db = $this->getDB( DB_MASTER );
-
-               if ( $this->db->getType() != 'sqlite' ) {
+               $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+               $dbw = $lb->getConnection( DB_MASTER );
+               if ( !( $dbw instanceof DatabaseSqlite ) ) {
                        $this->error( "This maintenance script requires a SQLite database.\n" );
 
                        return;
                }
 
                if ( $this->hasOption( 'vacuum' ) ) {
-                       $this->vacuum();
+                       $this->vacuum( $dbw );
                }
 
                if ( $this->hasOption( 'integrity' ) ) {
-                       $this->integrityCheck();
+                       $this->integrityCheck( $dbw );
                }
 
                if ( $this->hasOption( 'backup-to' ) ) {
-                       $this->backup( $this->getOption( 'backup-to' ) );
+                       $this->backup( $dbw, $this->getOption( 'backup-to' ) );
                }
        }
 
-       private function vacuum() {
-               $prevSize = filesize( $this->db->getDbFilePath() );
+       private function vacuum( DatabaseSqlite $dbw ) {
+               $prevSize = filesize( $dbw->getDbFilePath() );
                if ( $prevSize == 0 ) {
                        $this->fatalError( "Can't vacuum an empty database.\n" );
                }
 
                $this->output( 'VACUUM: ' );
-               if ( $this->db->query( 'VACUUM' ) ) {
+               if ( $dbw->query( 'VACUUM' ) ) {
                        clearstatcache();
-                       $newSize = filesize( $this->db->getDbFilePath() );
+                       $newSize = filesize( $dbw->getDbFilePath() );
                        $this->output( sprintf( "Database size was %d, now %d (%.1f%% reduction).\n",
                                $prevSize, $newSize, ( $prevSize - $newSize ) * 100.0 / $prevSize ) );
                } else {
@@ -97,9 +101,9 @@ class SqliteMaintenance extends Maintenance {
                }
        }
 
-       private function integrityCheck() {
+       private function integrityCheck( DatabaseSqlite $dbw ) {
                $this->output( "Performing database integrity checks:\n" );
-               $res = $this->db->query( 'PRAGMA integrity_check' );
+               $res = $dbw->query( 'PRAGMA integrity_check' );
 
                if ( !$res || $res->numRows() == 0 ) {
                        $this->error( "Error: integrity check query returned nothing.\n" );
@@ -112,10 +116,10 @@ class SqliteMaintenance extends Maintenance {
                }
        }
 
-       private function backup( $fileName ) {
+       private function backup( DatabaseSqlite $dbw, $fileName ) {
                $this->output( "Backing up database:\n   Locking..." );
-               $this->db->query( 'BEGIN IMMEDIATE TRANSACTION', __METHOD__ );
-               $ourFile = $this->db->getDbFilePath();
+               $dbw->query( 'BEGIN IMMEDIATE TRANSACTION', __METHOD__ );
+               $ourFile = $dbw->getDbFilePath();
                $this->output( "   Copying database file $ourFile to $fileName... " );
                Wikimedia\suppressWarnings();
                if ( !copy( $ourFile, $fileName ) ) {
@@ -124,7 +128,7 @@ class SqliteMaintenance extends Maintenance {
                }
                Wikimedia\restoreWarnings();
                $this->output( "   Releasing lock...\n" );
-               $this->db->query( 'COMMIT TRANSACTION', __METHOD__ );
+               $dbw->query( 'COMMIT TRANSACTION', __METHOD__ );
        }
 
        private function checkSyntax() {
index d4393dd..ba2d28e 100644 (file)
@@ -23,6 +23,7 @@ use MediaWiki\Storage\SqlBlobStore;
 use MediaWiki\User\UserIdentityValue;
 use MediaWikiTestCase;
 use PHPUnit_Framework_MockObject_MockObject;
+use Psr\Log\NullLogger;
 use Revision;
 use TestUserRegistry;
 use Title;
@@ -146,7 +147,7 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
                        ->getMock();
 
                $lb->method( 'reallyOpenConnection' )->willReturnCallback(
-                       function ( array $server, $dbNameOverride ) {
+                       function () use ( $server ) {
                                return $this->getDatabaseMock( $server );
                        }
                );
@@ -207,10 +208,11 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
                                'cliMode' => true,
                                'agent' => '',
                                'load' => 100,
+                               'srvCache' => new HashBagOStuff(),
                                'profiler' => null,
                                'trxProfiler' => new TransactionProfiler(),
-                               'connLogger' => new \Psr\Log\NullLogger(),
-                               'queryLogger' => new \Psr\Log\NullLogger(),
+                               'connLogger' => new NullLogger(),
+                               'queryLogger' => new NullLogger(),
                                'errorLogger' => function () {
                                },
                                'deprecationLogger' => function () {
index e0b8111..31a6343 100644 (file)
@@ -184,6 +184,8 @@ class DatabasePostgresTest extends MediaWikiTestCase {
         * @covers \Wikimedia\Rdbms\DatabasePostgres::getAttributes
         */
        public function testAttributes() {
-               $this->assertTrue( DatabasePostgres::getAttributes()[Database::ATTR_SCHEMAS_AS_TABLE_GROUPS] );
+               $this->assertTrue(
+                       Database::attributesFromType( 'postgres' )[Database::ATTR_SCHEMAS_AS_TABLE_GROUPS]
+               );
        }
 }
index 1016f28..1296bb6 100644 (file)
@@ -23,6 +23,7 @@
  * @copyright © 2013 Wikimedia Foundation Inc.
  */
 
+use Wikimedia\AtEase\AtEase;
 use Wikimedia\Rdbms\IDatabase;
 use Wikimedia\Rdbms\IMaintainableDatabase;
 use Wikimedia\Rdbms\LBFactory;
@@ -474,7 +475,7 @@ class LBFactoryTest extends MediaWikiTestCase {
                unset( $db );
 
                /** @var IMaintainableDatabase $db */
-               $db = $lb->getConnection( DB_MASTER, [], '' );
+               $db = $lb->getConnection( DB_MASTER, [], $lb::DOMAIN_ANY );
 
                $this->assertEquals(
                        '',
@@ -554,7 +555,7 @@ class LBFactoryTest extends MediaWikiTestCase {
                );
                $lb = $factory->getMainLB();
                /** @var IMaintainableDatabase $db */
-               $db = $lb->getConnection( DB_MASTER, [], '' );
+               $db = $lb->getConnection( DB_MASTER, [], $lb::DOMAIN_ANY );
 
                $this->assertEquals( '', $db->getDomainID(), "Null domain used" );
 
@@ -622,16 +623,16 @@ class LBFactoryTest extends MediaWikiTestCase {
                );
                $lb = $factory->getMainLB();
                /** @var IDatabase $db */
-               $db = $lb->getConnection( DB_MASTER, [], '' );
+               $db = $lb->getConnection( DB_MASTER, [], $lb::DOMAIN_ANY );
 
-               \Wikimedia\suppressWarnings();
+               AtEase::suppressWarnings();
                try {
-                       $this->assertFalse( $db->selectDB( 'garbage-db' ) );
+                       $this->assertFalse( $db->selectDomain( 'garbagedb' ) );
                        $this->fail( "No error thrown." );
                } catch ( \Wikimedia\Rdbms\DBQueryError $e ) {
-                       $this->assertRegExp( '/[\'"]garbage-db[\'"]/', $e->getMessage() );
+                       $this->assertRegExp( '/[\'"]garbagedb[\'"]/', $e->getMessage() );
                }
-               \Wikimedia\restoreWarnings();
+               AtEase::restoreWarnings();
        }
 
        /**
@@ -650,12 +651,12 @@ class LBFactoryTest extends MediaWikiTestCase {
                );
                $lb = $factory->getMainLB();
 
-               if ( !$lb->getConnection( DB_MASTER )->databasesAreIndependent() ) {
-                       $this->markTestSkipped( "Not applicable per databasesAreIndependent()" );
+               if ( !$factory->getMainLB()->getServerAttributes( 0 )[Database::ATTR_DB_IS_FILE] ) {
+                       $this->markTestSkipped( "Not applicable per ATTR_DB_IS_FILE" );
                }
 
                /** @var IDatabase $db */
-               $lb->getConnection( DB_MASTER, [], '' );
+               $this->assertNotNull( $lb->getConnection( DB_MASTER, [], $lb::DOMAIN_ANY ) );
        }
 
        /**
@@ -679,7 +680,7 @@ class LBFactoryTest extends MediaWikiTestCase {
                }
 
                $db = $lb->getConnection( DB_MASTER );
-               $db->selectDB( 'garbage-db' );
+               $db->selectDomain( 'garbage-db' );
        }
 
        /**
index f496fa3..d239ac1 100644 (file)
@@ -290,6 +290,10 @@ class BagOStuffTest extends MediaWikiTestCase {
 
                $val = $this->cache->incrWithInit( $key, 0, 1, 3 );
                $this->assertEquals( 4, $val, "Correct init value" );
+               $this->cache->delete( $key );
+
+               $val = $this->cache->incrWithInit( $key, 0, 5 );
+               $this->assertEquals( 5, $val, "Correct init value" );
        }
 
        /**
index 4c92545..ee2d174 100644 (file)
@@ -362,6 +362,8 @@ class DatabaseMysqlBaseTest extends PHPUnit\Framework\TestCase {
                $db->method( 'getMasterServerInfo' )
                        ->willReturn( [ 'serverId' => 172, 'asOf' => time() ] );
 
+               $db->setLBInfo( 'replica', true );
+
                // Fake the current time.
                list( $nowSecFrac, $nowSec ) = explode( ' ', microtime() );
                $now = (float)$nowSec + (float)$nowSecFrac;