/** @var array */
private $mWatchedItems = array();
+ /** @var integer User::READ_* constant bitfield used to load data */
+ protected $queryFlagsUsed = self::READ_NORMAL;
+
public static $idCacheByName = array();
/**
// Set it now to avoid infinite recursion in accessors
$this->mLoadedItems = true;
+ $this->queryFlagsUsed = $flags;
switch ( $this->mFrom ) {
case 'defaults':
}
$this->mLoadedItems = true;
+ $this->queryFlagsUsed = $flags;
return true;
}
* @return bool false if the ID does not exist or data is invalid, true otherwise
* @since 1.25
*/
- public function loadFromCache() {
+ protected function loadFromCache() {
global $wgMemc;
if ( $this->mId == 0 ) {
/**
* Save user data to the shared cache
+ *
+ * This method should not be called outside the User class
*/
public function saveToCache() {
+ global $wgMemc;
+
$this->load();
$this->loadGroups();
$this->loadOptions();
+
if ( $this->isAnon() ) {
// Anonymous users are uncached
return;
}
+
+ // The cache needs good consistency due to its high TTL, so the user
+ // should have been loaded from the master to avoid lag amplification.
+ if ( !( $this->queryFlagsUsed & self::READ_LATEST ) ) {
+ wfWarn( "Cannot save slave-loaded User object data to cache." );
+ return;
+ }
+
$data = array();
foreach ( self::$mCacheVars as $name ) {
$data[$name] = $this->$name;
}
$data['mVersion'] = self::VERSION;
$key = wfMemcKey( 'user', 'id', $this->mId );
- global $wgMemc;
+
$wgMemc->set( $key, $data );
}
$this->mGroups = array();
Hooks::run( 'UserLoadDefaults', array( $this, $name ) );
-
}
/**
: array()
);
+ $this->queryFlagsUsed = $flags;
Hooks::run( 'UserLoadFromDatabase', array( $this, &$s ) );
if ( $s !== false ) {
*/
private function loadGroups() {
if ( is_null( $this->mGroups ) ) {
- $dbr = wfGetDB( DB_MASTER );
+ $dbr = ( $this->queryFlagsUsed & self::READ_LATEST )
+ ? wfGetDB( DB_MASTER )
+ : wfGetDB( DB_SLAVE );
$res = $dbr->select( 'user_groups',
array( 'ug_group' ),
array( 'ug_user' => $this->mId ),
private function loadPasswords() {
if ( $this->getId() !== 0 && ( $this->mPassword === null || $this->mNewpassword === null ) ) {
$this->loadFromRow( wfGetDB( DB_MASTER )->selectRow(
- 'user',
- array( 'user_password', 'user_newpassword', 'user_newpass_time', 'user_password_expires' ),
- array( 'user_id' => $this->getId() ),
- __METHOD__
- ) );
+ 'user',
+ array( 'user_password', 'user_newpassword', 'user_newpass_time', 'user_password_expires' ),
+ array( 'user_id' => $this->getId() ),
+ __METHOD__
+ ) );
}
}
* @return bool
*/
public function addGroup( $group ) {
+ $this->load();
+
if ( !Hooks::run( 'UserAddGroup', array( $this, &$group ) ) ) {
return false;
}
$this->load();
$this->loadPasswords();
if ( wfReadOnly() ) {
- return;
+ return; // @TODO: caller should deal with this instead!
}
if ( 0 == $this->mId ) {
return;
}
+ // This method is for updating existing users, so the user should
+ // have been loaded from the master to begin with to avoid problems.
+ if ( !( $this->queryFlagsUsed & self::READ_LATEST ) ) {
+ wfWarn( "Attempting to save slave-loaded User object data." );
+ }
+
$this->mTouched = self::newTouchedTimestamp();
if ( !$wgAuth->allowSetLocalPassword() ) {
$this->mPassword = self::getPasswordFactory()->newFromCiphertext( null );
// using CentralAuth. It's should be OK to commit and break the snapshot.
$dbw->commit( __METHOD__, 'flush' );
$options = array();
- $flags = 0;
+ $flags = self::READ_LATEST;
}
$this->mId = $dbw->selectField( 'user', 'user_id',
array( 'user_name' => $this->mName ), __METHOD__, $options );
if ( !is_array( $data ) ) {
wfDebug( "User: loading options for user " . $this->getId() . " from database.\n" );
// Load from database
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = ( $this->queryFlagsUsed & self::READ_LATEST )
+ ? wfGetDB( DB_MASTER )
+ : wfGetDB( DB_SLAVE );
$res = $dbr->select(
'user_properties',