From 35c4a37918bd68e4a8f90608b1756e97da5edb6b Mon Sep 17 00:00:00 2001 From: Kunal Mehta Date: Mon, 24 Aug 2015 16:43:38 -0700 Subject: [PATCH] Use db-replicated objectcache for storing last seen time So we're not abusing user preferences for the last seen time. EchoSeenTime is a small wrapper around ObjectCache that handles the fallback to user preferences during the transition. All JavaScript code now needs to use mw.config.get('wgEchoSeenTime'). Bug: T95839 Change-Id: Ia45ba5e30eb4564250539d04d5886d2598ebd49a --- Hooks.php | 3 +- autoload.php | 1 + includes/SeenTime.php | 59 +++++++++++++++++++ includes/api/ApiEchoMarkSeen.php | 9 +-- includes/special/SpecialNotifications.php | 6 +- modules/special/ext.echo.special.js | 2 +- .../mw.echo.dm.NotificationsModel.js | 6 +- 7 files changed, 71 insertions(+), 15 deletions(-) create mode 100644 includes/SeenTime.php diff --git a/Hooks.php b/Hooks.php index 796151a88..c8588995c 100644 --- a/Hooks.php +++ b/Hooks.php @@ -638,7 +638,8 @@ class EchoHooks { $msgNotificationTimestamp = $notifUser->getLastUnreadMessageTime(); $alertNotificationTimestamp = $notifUser->getLastUnreadAlertTime(); - $seenTime = $user->getOption( 'echo-seen-time' ); + $seenTime = EchoSeenTime::newFromUser( $user )->getTime(); + $sk->getOutput()->addJsConfigVars( 'wgEchoSeenTime', $seenTime ); $msgText = EchoNotificationController::formatNotificationCount( $msgCount ); $alertText = EchoNotificationController::formatNotificationCount( $alertCount ); diff --git a/autoload.php b/autoload.php index 46728d8b8..41d95f40a 100644 --- a/autoload.php +++ b/autoload.php @@ -71,6 +71,7 @@ $wgAutoloadClasses += array( 'EchoPageLinkFormatter' => __DIR__ . '/includes/formatters/PageLinkFormatter.php', 'EchoRevisionLocalCache' => __DIR__ . '/includes/cache/RevisionLocalCache.php', 'EchoRowUpdateGenerator' => __DIR__ . '/includes/BatchRowUpdate.php', + 'EchoSeenTime' => __DIR__ . '/includes/SeenTime.php', 'EchoSuppressionRowUpdateGenerator' => __DIR__ . '/includes/schemaUpdate.php', 'EchoTalkPageFunctionalTest' => __DIR__ . '/tests/phpunit/TalkPageFunctionalTest.php', 'EchoTargetPage' => __DIR__ . '/includes/model/TargetPage.php', diff --git a/includes/SeenTime.php b/includes/SeenTime.php new file mode 100644 index 000000000..b157a1ca0 --- /dev/null +++ b/includes/SeenTime.php @@ -0,0 +1,59 @@ +user = $user; + $this->key = wfMemcKey( 'echo', 'seen', 'time', $user->getId() ); + $this->cache = ObjectCache::getInstance( 'db-replicated' ); + } + + /** + * @param User $user + * @return EchoSeenTime + */ + public static function newFromUser( User $user ) { + return new self( $user ); + } + + /** + * @param int $flags BagOStuff::READ_LATEST to use the master + * @return string|bool false if no stored time + */ + public function getTime( $flags = 0 ) { + $cas = 0; // Unused, but we have to pass something by reference + $data = $this->cache->get( $this->key, $cas, $flags ); + if ( $data === false ) { + // Check if the user still has it set in their preferences + $data = $this->user->getOption( 'echo-seen-time', false ); + } + + return $data; + } + + public function setTime( $time ) { + return $this->cache->set( $this->key, $time ); + } +} diff --git a/includes/api/ApiEchoMarkSeen.php b/includes/api/ApiEchoMarkSeen.php index 6b3797318..a8851fba0 100755 --- a/includes/api/ApiEchoMarkSeen.php +++ b/includes/api/ApiEchoMarkSeen.php @@ -11,14 +11,9 @@ class ApiEchoMarkSeen extends ApiBase { $this->dieUsage( 'Login is required', 'login-required' ); } - // Load from the master to reduce CAS errors from high update frequency - $u = User::newFromId( $user->getId() ); - $u->load( User::READ_LATEST ); - $timestamp = wfTimestamp( TS_MW ); - // @TODO: do not abuse user preferences for "last seen" - $u->setOption( 'echo-seen-time', $timestamp ); - $u->saveSettings(); + $seenTime = EchoSeenTime::newFromUser( $user ); + $seenTime->setTime( $timestamp ); $this->getResult()->addValue( 'query', $this->getModuleName(), array( 'result' => 'success', diff --git a/includes/special/SpecialNotifications.php b/includes/special/SpecialNotifications.php index 6a4c019c4..50acbd5a6 100644 --- a/includes/special/SpecialNotifications.php +++ b/includes/special/SpecialNotifications.php @@ -71,7 +71,8 @@ class SpecialNotifications extends SpecialPage { $dateHeader = ''; $notices = ''; $unread = array(); - $seenTime = $user->getOption( 'echo-seen-time' ); + $echoSeenTime = EchoSeenTime::newFromUser( $user ); + $seenTime = $echoSeenTime->getTime(); foreach ( $notif as $row ) { $class = 'mw-echo-notification'; @@ -141,8 +142,7 @@ class SpecialNotifications extends SpecialPage { // Record time notifications have been seen $timestamp = wfTimestamp( TS_MW ); - $user->setOption( 'echo-seen-time', $timestamp ); - $user->saveSettings(); + $echoSeenTime->setTime( $timestamp ); } /** diff --git a/modules/special/ext.echo.special.js b/modules/special/ext.echo.special.js index 999f633f8..d349cb788 100644 --- a/modules/special/ext.echo.special.js +++ b/modules/special/ext.echo.special.js @@ -61,7 +61,7 @@ loadMore: function () { var notifications, data, container, $li, api = new mw.Api( { ajax: { cache: false } } ), - seenTime = mw.user.options.get( 'echo-seen-time' ), + seenTime = mw.config.get( 'wgEchoSeenTime' ), that = this, unread = [], apiData = { diff --git a/modules/viewmodel/mw.echo.dm.NotificationsModel.js b/modules/viewmodel/mw.echo.dm.NotificationsModel.js index b9d9c02c7..f7c8419df 100644 --- a/modules/viewmodel/mw.echo.dm.NotificationsModel.js +++ b/modules/viewmodel/mw.echo.dm.NotificationsModel.js @@ -27,7 +27,7 @@ this.api = new mw.Api( { ajax: { cache: false } } ); this.fetchNotificationsPromise = null; - this.seenTime = mw.user.options.get( 'echo-seen-time' ); + this.seenTime = mw.config.get( 'wgEchoSeenTime' ); // Store references to unseen and unread notifications this.unseenNotifications = new mw.echo.dm.NotificationList(); @@ -189,9 +189,9 @@ items = model.unseenNotifications.getItems(), time = data.query.echomarkseen.timestamp; - // update echo-seen-time value in JS (where it wouldn't + // update wgEchoSeenTime value in JS (where it wouldn't // otherwise propagate until page reload) - mw.user.options.set( 'echo-seen-time', time ); + mw.config.set( 'wgEchoSeenTime', time ); model.setSeenTime( time ); // Update the notifications seen status