Attachment 'subscribercache.patch'

Download

   1 Description: Use a cache for storing and retrieving subscriber
   2  information to boost performance
   3 Origin: http://moinmo.in/MoinMoinBugs/GetSubscribersSlow
   4 Author: Vitaliy Shchupak, updated by Steve McIntyre to add locking
   5 Last-Update: 2012-04-29
   6 
   7 --- a/MoinMoin/Page.py	2012-04-29 19:17:26.113325108 +0100
   8 +++ b/MoinMoin/Page.py	2012-04-29 19:00:55.781245226 +0100
   9 @@ -811,7 +811,7 @@
  10  
  11          return link
  12  
  13 -    def getSubscribers(self, request, **kw):
  14 +    def getSubscribersUncached(self, request, **kw):  # original function renamed
  15          """ Get all subscribers of this page.
  16  
  17          @param request: the request object
  18 @@ -834,6 +834,8 @@
  19          else:
  20              from MoinMoin.security import Default as UserPerms
  21  
  22 +        request.clock.start("getSubscribersUncached")
  23 +
  24          # get email addresses of the all wiki user which have a profile stored;
  25          # add the address only if the user has subscribed to the page and
  26          # the user is not the current editor
  27 @@ -872,6 +874,113 @@
  28              else:
  29                  subscriber_list[lang].append(subscriber.email)
  30  
  31 +        request.clock.stop("getSubscribersUncached")
  32 +
  33 +        return subscriber_list
  34 +
  35 +    # cached version of getSubscribers
  36 +    def getSubscribers(self, request, **kw):
  37 +        """ Get all subscribers of this page.
  38 +
  39 +        @param request: the request object
  40 +        @keyword include_self: if 1, include current user (default: 0)
  41 +        @keyword return_users: if 1, return user instances (default: 0)
  42 +        @rtype: dict
  43 +        @return: lists of subscribed email addresses in a dict by language key
  44 +        """
  45 +        include_self = kw.get('include_self', self.include_self)
  46 +        return_users = kw.get('return_users', 0)
  47 +
  48 +        request.clock.start('getSubscribersCached')
  49 +        # extract categories of this page
  50 +        pageList = self.getCategories(request)
  51 +
  52 +        # add current page name for list matching
  53 +        pageList.append(self.page_name)
  54 +
  55 +        arena = 'user'
  56 +        key = 'page_sub'
  57 +
  58 +        # get or create cache file
  59 +        page_sub = {}
  60 +        cache = caching.CacheEntry(request, arena, key, scope='wiki', use_pickle=True)
  61 +        if cache.exists():
  62 +            page_sub = cache.content()
  63 +        else:
  64 +            #build a cache if it doesn't exist
  65 +            cache = caching.CacheEntry(request, arena, key, scope='wiki', use_pickle=True, do_locking=False)
  66 +            # lock to stop anybody else interfering with the data while we're working
  67 +            cache.lock('w')
  68 +            userlist = user.getUserList(request)
  69 +            for uid in userlist:
  70 +                subscriber = user.User(request, uid)
  71 +                # we don't care about storing entries for users without any page subscriptions
  72 +                if subscriber.subscribed_pages:
  73 +                    page_sub[subscriber.id] = {
  74 +                        'name': subscriber.name,
  75 +                        'email': subscriber.email,
  76 +                        'subscribed_pages': subscriber.subscribed_pages
  77 +                    }
  78 +            cache.update(page_sub)
  79 +            cache.unlock()
  80 +            cache.lock('r') # to go back to the same mode as if it had existed all along
  81 +
  82 +        if self.cfg.SecurityPolicy:
  83 +            UserPerms = self.cfg.SecurityPolicy
  84 +        else:
  85 +            from MoinMoin.security import Default as UserPerms
  86 +
  87 +        # get email addresses of the all wiki user which have a profile stored;
  88 +        # add the address only if the user has subscribed to the page and
  89 +        # the user is not the current editor
  90 +        userlist = page_sub
  91 +        subscriber_list = {}
  92 +
  93 +        pages = pageList[:]
  94 +        if request.cfg.interwikiname:
  95 +            pages += ["%s:%s" % (request.cfg.interwikiname, pagename) for pagename in pageList]
  96 +        # Create text for regular expression search
  97 +        text = '\n'.join(pages)
  98 +
  99 +        for uid in userlist.keys():
 100 +            if uid == request.user.id and not include_self:
 101 +                continue # no self notification
 102 +
 103 +            isSubscribed = False
 104 +
 105 +            # This is a bit wrong if return_users=1 (which implies that the caller will process
 106 +            # user attributes and may, for example choose to send an SMS)
 107 +            # So it _should_ be "not (subscriber.email and return_users)" but that breaks at the moment.
 108 +            if not userlist[uid]['email']:
 109 +                continue # skip empty email addresses
 110 +
 111 +            # now check patters for actual match
 112 +            for pattern in userlist[uid]['subscribed_pages']:
 113 +                if pattern in pages:
 114 +                    isSubscribed = True
 115 +                try:
 116 +                    pattern = re.compile(r'^%s$' % pattern, re.M)
 117 +                except re.error:
 118 +                    continue
 119 +                if pattern.search(text):
 120 +                    isSubscribed = True
 121 +
 122 +            # only if subscribed, then read user info from file
 123 +            if isSubscribed:
 124 +                subscriber = user.User(request, uid)
 125 +
 126 +                if not UserPerms(subscriber).read(self.page_name):
 127 +                    continue
 128 +
 129 +                lang = subscriber.language or request.cfg.language_default
 130 +                if not lang in subscriber_list:
 131 +                    subscriber_list[lang] = []
 132 +                if return_users:
 133 +                    subscriber_list[lang].append(subscriber)
 134 +                else:
 135 +                    subscriber_list[lang].append(subscriber.email)
 136 +
 137 +        request.clock.stop('getSubscribersCached')
 138          return subscriber_list
 139  
 140      def parse_processing_instructions(self):
 141 --- a/MoinMoin/user.py	2012-04-29 19:17:28.053323498 +0100
 142 +++ b/MoinMoin/user.py	2012-04-29 19:08:40.921246380 +0100
 143 @@ -618,6 +618,9 @@
 144              event = events.UserCreatedEvent(self._request, self)
 145              events.send_event(event)
 146  
 147 +        # update page subscriber's cache after saving user preferences
 148 +        self.updatePageSubCache()
 149 +
 150      # -----------------------------------------------------------------
 151      # Time and date formatting
 152  
 153 @@ -805,6 +808,36 @@
 154              self.save()
 155          return not self.isSubscribedTo([pagename])
 156  
 157 +    # update page subscribers cache
 158 +    def updatePageSubCache(self):
 159 +        """ When a user changes his preferences, we update the
 160 +        page subscriber's cache
 161 +        """
 162 +
 163 +        arena = 'user'
 164 +        key = 'page_sub'
 165 +
 166 +        page_sub = {}
 167 +        cache = caching.CacheEntry(self._request, arena, key, scope='wiki', use_pickle=True, do_locking=False)
 168 +        if not cache.exists():
 169 +            return  # if no cache file, just don't do anything
 170 +
 171 +        cache.lock('w')
 172 +        page_sub = cache.content()
 173 +
 174 +        # we don't care about storing entries for users without any page subscriptions
 175 +        if self.subscribed_pages:
 176 +            page_sub[self.id] = {
 177 +                'name': self.name,
 178 +                'email': self.email,
 179 +                'subscribed_pages': self.subscribed_pages
 180 +            }
 181 +        elif page_sub.get(self.id):
 182 +            del page_sub[self.id]
 183 +
 184 +        cache.update(page_sub)
 185 +        cache.unlock()
 186 +
 187      # -----------------------------------------------------------------
 188      # Quicklinks
 189  

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2012-08-08 20:37:50, 5.6 KB) [[attachment:subscriber_cache.py]]
  • [get | view] (2012-04-29 18:24:33, 7.1 KB) [[attachment:subscribercache.patch]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.