Bsitu has uploaded a new change for review.

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

Change subject: Add job to keep user echo notifications in reasonable volume
......................................................................

Add job to keep user echo notifications in reasonable volume

This is not tested and needs some test cases

Change-Id: I4d4fa4c987a1732e5e29536a7669e28c34d4ab18
---
M Echo.php
M controller/NotificationController.php
M includes/mapper/NotificationMapper.php
M includes/mapper/TargetPageMapper.php
A jobs/NotificationDeleteJob.php
5 files changed, 174 insertions(+), 4 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Echo 
refs/changes/13/159413/1

diff --git a/Echo.php b/Echo.php
index 5db62d9..6cb646c 100644
--- a/Echo.php
+++ b/Echo.php
@@ -239,10 +239,10 @@
 // The max number showed in bundled message, eg, <user> and 99+ others <action>
 $wgEchoMaxNotificationCount = 99;
 
-// The max number allowed to be updated on a web request, when we mark all 
notification
-// as read, it's a bad idea to update on a web request if the number is 
incredibly
-// huge, to prevent this, we just fetch 2000 thousand records and mark them as 
read.
-// This would cover most of the use cases.
+// The max number of notifications allowed for a user to do a live update,
+// this is also the number of max notifications allowed for a user to have
+// @FIXME - the name is not intuitive, probably change it when the deleteJob 
patch
+// is deployed to both deployment branches
 $wgEchoMaxUpdateCount = 2000;
 
 // The time interval between each bundle email in seconds
diff --git a/controller/NotificationController.php 
b/controller/NotificationController.php
index 3233d5c..8fbdd25 100644
--- a/controller/NotificationController.php
+++ b/controller/NotificationController.php
@@ -74,7 +74,9 @@
 
                $type = $event->getType();
                $notifyTypes = self::getEventNotifyTypes( $type );
+               $userIds = array();
                foreach ( self::getUsersToNotifyForEvent( $event ) as $user ) {
+                       $userIds[$user->getId()] = $user->getId();
                        $userNotifyTypes = $notifyTypes;
                        wfRunHooks( 'EchoGetNotificationTypes', array( $user, 
$event, &$userNotifyTypes ) );
 
@@ -83,6 +85,30 @@
                                self::doNotification( $event, $user, $type );
                        }
                }
+               if ( $userIds ) {
+                       self::enqueueDeleteJob( $userIds, $event );
+               }
+       }
+
+       /**
+        * Schedule a job to check and delete older notifications
+        *
+        * @param int $userIds
+        * @param EchoEvent $event
+        */
+       public static function enqueueDeleteJob( array $userIds, EchoEvent 
$event ) {
+               // Do nothing if there is no user
+               if ( !$userIds ) {
+                       return;
+               }
+
+               $job = new EchoNotificationDeleteJob(
+                       $event->getTitle() ?: Title::newMainPage(),
+                       array(
+                               'userIds' => $userIds
+                       )
+               );
+               JobQueueGroup::singleton()->push( $job );
        }
 
        /**
diff --git a/includes/mapper/NotificationMapper.php 
b/includes/mapper/NotificationMapper.php
index 52aca10..4d20bf2 100644
--- a/includes/mapper/NotificationMapper.php
+++ b/includes/mapper/NotificationMapper.php
@@ -206,4 +206,57 @@
                }
        }
 
+       /**
+        * Fetch a notification by user in the specified offset.  The caller 
should
+        * know that pass a big number for offset is not going to work
+        * @param User $user
+        * @param int $offset
+        * @return EchoNotification|bool
+        */
+       public function FetchByUserOffset( User $user, $offset ) {
+               $dbr = $this->dbFactory->getEchoDb( DB_SLAVE );
+               $row = $dbr->selectRow(
+                       array( 'echo_notification', 'echo_event' ),
+                       array( '*' ),
+                       array(
+                               'notification_user' => $user->getId(),
+                               'notification_bundle_base' => 1
+                       ),
+                       __METHOD__,
+                       array(
+                               'ORDER BY' => 'notification_timestamp DESC, 
notification_event DESC',
+                               'OFFSET' => $offset,
+                               'LIMIT' => 1 
+                       )
+                       array(
+                               'echo_event' => array( 'LEFT JOIN', 
'notification_event=event_id' ),
+                       )
+               );
+
+               if ( $row ) {
+                       return EchoNotification::newFromRow( $row );
+               } else {
+                       return false;   
+               }
+       }
+
+       /**
+        * Batch delete notifications by user and eventId offset
+        * @param User $user
+        * @param int $eventId
+        * @return boolean
+        */
+       public function deleteByUserEventOffset( User $user, $eventId ) {
+               $dbw = $this->dbFactory->getEchoDb( DB_MASTER );
+               $res = $dbw->delete(
+                       'echo_notification',
+                       array(
+                               'notification_user' => $user->getId(),
+                               'notification_event < ' . (int)$eventId
+                       ),
+                       __METHOD__      
+               );
+               return $res;
+       }
+
 }
diff --git a/includes/mapper/TargetPageMapper.php 
b/includes/mapper/TargetPageMapper.php
index 9083e44..e0fbaad 100644
--- a/includes/mapper/TargetPageMapper.php
+++ b/includes/mapper/TargetPageMapper.php
@@ -141,6 +141,27 @@
        }
 
        /**
+        * Delete multiple EchoTargetPage records by user & event_id offset
+        *
+        * @param User $user
+        * @param int $eventId
+        * @return boolean
+        */
+       public function deleteByUserEventOffset( User $user, $eventId ) {
+               $dbw = $this->dbFactory->getEchoDb( DB_MASTER );
+
+               $res = $dbw->delete(
+                       'echo_target_page',
+                       array(
+                               'etp_user' => $user->getId(),
+                               'etp_event < ' => (int)$eventId
+                       ),
+                       __METHOD__
+               );
+               return $res;
+       }
+
+       /**
         * Delete multiple EchoTargetPage records by user
         *
         * @param User $user
diff --git a/jobs/NotificationDeleteJob.php b/jobs/NotificationDeleteJob.php
new file mode 100644
index 0000000..fb1e10e
--- /dev/null
+++ b/jobs/NotificationDeleteJob.php
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * This job is created when sending notifications to the target users.  The 
purpose
+ * of this job is to delete older notifications when the number of 
notifications a
+ * user has is more than $wgEchoMaxUpdateCount, it does not make sense to have 
tons
+ * of notifications in the history while users wouldn't bother to click 'load 
more'
+ * like 100 times to see them. What we gain from this is we could run expensive
+ * queries otherwise that would requires adding index and data denormalization.
+ */
+class EchoNotificationDeleteJob extends Job {
+
+       /**
+        * UserIds to be processed
+        * @var int[]
+        */
+       protected $userIds = array();
+
+       /**
+        * @var MWEchoDbFactory
+        */
+       protected $dbFactory;
+
+       /**
+        * @param Title
+        * @param array
+        */
+       function __construct( $title, $params ) {
+               parent::__construct( __CLASS__, $title, $params );
+               $this->userIds = $params['userIds'];
+               $dbFactory = MWEchoDbFactory::newFromDefault();
+       }
+
+       /**
+        * Run the job of finding & deleting older notifications
+        */
+       function run() {
+               global $wgEchoMaxUpdateCount;
+
+               $updateCount  = 0;
+               $dbw = $this->dbFactory->getEchoDb( DB_MASTER );
+               $notifMapper  = new EchoNotificationMapper();
+               $targetMapper = new EchoTargetPageMapper();
+
+               foreach ( $this->users as $userId ) {
+                       $user = User::newFromId( $userId );
+                       $notif = $notifMapper->FetchByUserOffset( $user, 
$wgEchoMaxUpdateCount );
+                       if ( $notif ) {
+                               $dbw->startAtomic( __METHOD__ );
+                               $res = $notifMapper->deleteByUserEventOffset( 
$user, $notif->getEvent()->getId() );
+                               if ( $res ) {
+                                       $res = 
$targetMapper->deleteByUserEventOffset( $user, $notif->getEvent()->getId() );
+                               }
+                               $dbw->endAtomic( __METHOD__ );
+                               if ( $res ) {
+                                       $updateCount++;
+                                       $notifUser = 
MWEchoNotifUser::newFromUser( $user );
+                                       $notifUser->resetNotificationCount( 
DB_MASTER );
+                               }
+                               // Wait for slave if we are doing a lot of 
updates
+                               if ( $updateCount > 10 ) {
+                                       $this->dbFactory->waitForSlaves();
+                                       $updateCount = 0;
+                               }
+                       }
+               }
+               return true;
+       }
+
+}

-- 
To view, visit https://gerrit.wikimedia.org/r/159413
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I4d4fa4c987a1732e5e29536a7669e28c34d4ab18
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Echo
Gerrit-Branch: master
Gerrit-Owner: Bsitu <bs...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to