Merge "Database: Remove resultObject() from the public interface"
authorAaron Schulz <aschulz@wikimedia.org>
Sat, 26 Sep 2015 18:25:01 +0000 (18:25 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Sat, 26 Sep 2015 18:25:01 +0000 (18:25 +0000)
includes/User.php
includes/UserRightsProxy.php
includes/filerepo/LocalRepo.php
includes/filerepo/file/LocalFile.php
includes/libs/objectcache/WANObjectCache.php
includes/specials/SpecialMovepage.php

index d57dfaa..753061d 100644 (file)
@@ -2290,10 +2290,14 @@ class User implements IDBAccessObject {
         */
        public function clearSharedCache() {
                $id = $this->getId();
-               if ( $id ) {
-                       $key = wfMemcKey( 'user', 'id', $id );
-                       ObjectCache::getMainWANInstance()->delete( $key );
+               if ( !$id ) {
+                       return;
                }
+
+               $key = wfMemcKey( 'user', 'id', $id );
+               wfGetDB( DB_MASTER )->onTransactionPreCommitOrIdle( function() use ( $key ) {
+                       ObjectCache::getMainWANInstance()->delete( $key );
+               } );
        }
 
        /**
index a19f698..2c7032f 100644 (file)
@@ -278,8 +278,9 @@ class UserRightsProxy {
                        array( 'user_id' => $this->id ),
                        __METHOD__ );
 
-               $cache = ObjectCache::getMainWANInstance();
                $key = wfForeignMemcKey( $this->database, false, 'user', 'id', $this->id );
-               $cache->delete( $key );
+               $this->db->onTransactionPreCommitOrIdle( function() use ( $key ) {
+                       ObjectCache::getMainWANInstance()->delete( $key );
+               } );
        }
 }
index 6a2c064..b209bd6 100644 (file)
@@ -521,15 +521,11 @@ class LocalRepo extends FileRepo {
         * @return void
         */
        function invalidateImageRedirect( Title $title ) {
-               $cache = ObjectCache::getMainWANInstance();
-
-               $memcKey = $this->getSharedCacheKey( 'image_redirect', md5( $title->getDBkey() ) );
-               if ( $memcKey ) {
-                       // Set a temporary value for the cache key, to ensure
-                       // that this value stays purged long enough so that
-                       // it isn't refreshed with a stale value due to a
-                       // lagged slave.
-                       $cache->delete( $memcKey, 12 );
+               $key = $this->getSharedCacheKey( 'image_redirect', md5( $title->getDBkey() ) );
+               if ( $key ) {
+                       $this->getMasterDB()->onTransactionPreCommitOrIdle( function() use ( $key ) {
+                               ObjectCache::getMainWANInstance()->delete( $key );
+                       } );
                }
        }
 
index 4c9c2aa..3225d78 100644 (file)
@@ -321,7 +321,9 @@ class LocalFile extends File {
                        return;
                }
 
-               ObjectCache::getMainWANInstance()->delete( $key );
+               $this->repo->getMasterDB()->onTransactionPreCommitOrIdle( function() use ( $key ) {
+                       ObjectCache::getMainWANInstance()->delete( $key );
+               } );
        }
 
        /**
index fb96269..c78b299 100644 (file)
@@ -286,6 +286,31 @@ class WANObjectCache {
         *   - a) Replication lag is bounded to being less than HOLDOFF_TTL; or
         *   - b) If lag is higher, the DB will have gone into read-only mode already
         *
+        * When using potentially long-running ACID transactions, a good pattern is
+        * to use a pre-commit hook to issue the delete. This means that immediately
+        * after commit, callers will see the tombstone in cache in the local datacenter
+        * and in the others upon relay. It also avoids the following race condition:
+        *   - a) T1 begins, changes a row, and calls delete()
+        *   - b) The HOLDOFF_TTL passes, expiring the delete() tombstone
+        *   - c) T2 starts, reads the row and calls set() due to a cache miss
+        *   - d) T1 finally commits
+        *   - e) Stale value is stuck in cache
+        *
+        * Example usage:
+        * @code
+        *     $dbw->begin(); // start of request
+        *     ... <execute some stuff> ...
+        *     // Update the row in the DB
+        *     $dbw->update( ... );
+        *     $key = wfMemcKey( 'homes', $homeId );
+        *     // Purge the corresponding cache entry just before committing
+        *     $dbw->onTransactionPreCommitOrIdle( function() use ( $cache, $key ) {
+        *         $cache->delete( $key );
+        *     } );
+        *     ... <execute some stuff> ...
+        *     $dbw->commit(); // end of request
+        * @endcode
+        *
         * If called twice on the same key, then the last hold-off TTL takes
         * precedence. For idempotence, the $ttl should not vary for different
         * delete() calls on the same key. Also note that lowering $ttl reduces
index f7a0a20..ab28fa4 100644 (file)
@@ -464,7 +464,7 @@ class MovePageForm extends UnlistedSpecialPage {
                                'name' => $submitVar,
                                'value' => $movepagebtn,
                                'label' => $movepagebtn,
-                               'flags' => array( 'progressive', 'primary' ),
+                               'flags' => array( 'constructive', 'primary' ),
                                'type' => 'submit',
                        ) ),
                        array(