mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 14:03:30 +00:00 
			
		
		
		
	Pre-fetch data from the DB and hand to markdown thread
We want to avoid opening a DB connection in the markdown thread as its DB connection might live for a long time (imported from commit 7700b2ca793ee5e9add7f071b92f22a4bf576b3d)
This commit is contained in:
		@@ -10,10 +10,10 @@ import ujson
 | 
			
		||||
 | 
			
		||||
@cache_with_key(realm_alert_words_cache_key, timeout=3600*24)
 | 
			
		||||
def alert_words_in_realm(realm):
 | 
			
		||||
    users = zerver.models.UserProfile.objects.filter(realm=realm, is_active=True)
 | 
			
		||||
    all_user_words = dict((user, user_alert_words(user)) for user in users)
 | 
			
		||||
    users_with_words = dict((u, w) for (u, w) in all_user_words.iteritems() if len(w))
 | 
			
		||||
    return users_with_words
 | 
			
		||||
    users = zerver.models.UserProfile.objects.filter(realm=realm, is_active=True).values('id', 'alert_words')
 | 
			
		||||
    all_user_words = dict((user['id'], ujson.loads(user['alert_words'])) for user in users)
 | 
			
		||||
    user_ids_with_words = dict((user_id, w) for (user_id, w) in all_user_words.iteritems() if len(w))
 | 
			
		||||
    return user_ids_with_words
 | 
			
		||||
 | 
			
		||||
def user_alert_words(user_profile):
 | 
			
		||||
    return ujson.loads(user_profile.alert_words)
 | 
			
		||||
 
 | 
			
		||||
@@ -356,8 +356,9 @@ class Emoji(markdown.inlinepatterns.Pattern):
 | 
			
		||||
        orig_syntax = match.group("syntax")
 | 
			
		||||
        name = orig_syntax[1:-1]
 | 
			
		||||
 | 
			
		||||
        if current_message:
 | 
			
		||||
            realm_emoji = current_message.get_realm().get_emoji()
 | 
			
		||||
        realm_emoji = {}
 | 
			
		||||
        if db_data is not None:
 | 
			
		||||
            realm_emoji = db_data['emoji']
 | 
			
		||||
 | 
			
		||||
        if current_message and name in realm_emoji:
 | 
			
		||||
            return make_emoji(name, realm_emoji[name], orig_syntax)
 | 
			
		||||
@@ -534,19 +535,32 @@ class RealmFilterPattern(markdown.inlinepatterns.Pattern):
 | 
			
		||||
                        m.group("name"))
 | 
			
		||||
 | 
			
		||||
class UserMentionPattern(markdown.inlinepatterns.Pattern):
 | 
			
		||||
    def find_user_for_mention(self, name):
 | 
			
		||||
        if db_data is None:
 | 
			
		||||
            return (False, None)
 | 
			
		||||
 | 
			
		||||
        if mention.user_mention_matches_wildcard(name):
 | 
			
		||||
            return (True, None)
 | 
			
		||||
 | 
			
		||||
        user = db_data['full_names'].get(name.lower(), None)
 | 
			
		||||
        if user is None:
 | 
			
		||||
            user = db_data['short_names'].get(name.lower(), None)
 | 
			
		||||
 | 
			
		||||
        return (False, user)
 | 
			
		||||
 | 
			
		||||
    def handleMatch(self, m):
 | 
			
		||||
        name = m.group(2) or m.group(3)
 | 
			
		||||
 | 
			
		||||
        if current_message:
 | 
			
		||||
            wildcard, user = mention.find_user_for_mention(name, current_message.sender.realm)
 | 
			
		||||
            wildcard, user = self.find_user_for_mention(name)
 | 
			
		||||
 | 
			
		||||
            if wildcard:
 | 
			
		||||
                current_message.mentions_wildcard = True
 | 
			
		||||
                email = "*"
 | 
			
		||||
            elif user:
 | 
			
		||||
                current_message.mentions_user_ids.add(user.id)
 | 
			
		||||
                name = user.full_name
 | 
			
		||||
                email = user.email
 | 
			
		||||
                current_message.mentions_user_ids.add(user['id'])
 | 
			
		||||
                name = user['full_name']
 | 
			
		||||
                email = user['email']
 | 
			
		||||
            else:
 | 
			
		||||
                # Don't highlight @mentions that don't refer to a valid user
 | 
			
		||||
                return None
 | 
			
		||||
@@ -559,17 +573,18 @@ class UserMentionPattern(markdown.inlinepatterns.Pattern):
 | 
			
		||||
 | 
			
		||||
class AlertWordsNotificationProcessor(markdown.preprocessors.Preprocessor):
 | 
			
		||||
    def run(self, lines):
 | 
			
		||||
        if current_message:
 | 
			
		||||
        if current_message and db_data is not None:
 | 
			
		||||
            # We check for a user's custom notifications here, as we want
 | 
			
		||||
            # to check for plaintext words that depend on the recipient.
 | 
			
		||||
            realm_words = alert_words.alert_words_in_realm(current_message.sender.realm)
 | 
			
		||||
            realm_words = db_data['realm_alert_words']
 | 
			
		||||
 | 
			
		||||
            content = '\n'.join(lines)
 | 
			
		||||
            for user, words in realm_words.iteritems():
 | 
			
		||||
            for user_id, words in realm_words.iteritems():
 | 
			
		||||
                for word in words:
 | 
			
		||||
                    escaped = re.escape(word)
 | 
			
		||||
                    match_re = re.compile(r'\b%s\b' % (escaped,))
 | 
			
		||||
                    if re.search(match_re, content):
 | 
			
		||||
                        current_message.user_ids_with_alert_words.add(user.id)
 | 
			
		||||
                        current_message.user_ids_with_alert_words.add(user_id)
 | 
			
		||||
 | 
			
		||||
        return lines
 | 
			
		||||
 | 
			
		||||
@@ -711,8 +726,14 @@ def _sanitize_for_log(md):
 | 
			
		||||
# provides no way to pass extra params through to a pattern. Thus, a global.
 | 
			
		||||
current_message = None
 | 
			
		||||
 | 
			
		||||
# We avoid doing DB queries in our markdown thread to avoid the overhead of
 | 
			
		||||
# opening a new DB connection. These connections tend to live longer than the
 | 
			
		||||
# threads themselves, as well.
 | 
			
		||||
db_data = None
 | 
			
		||||
 | 
			
		||||
def do_convert(md, realm_domain=None, message=None):
 | 
			
		||||
    """Convert Markdown to HTML, with Zulip-specific settings and hacks."""
 | 
			
		||||
    from zerver.models import UserProfile
 | 
			
		||||
 | 
			
		||||
    if realm_domain in md_engines:
 | 
			
		||||
        _md_engine = md_engines[realm_domain]
 | 
			
		||||
@@ -723,6 +744,18 @@ def do_convert(md, realm_domain=None, message=None):
 | 
			
		||||
 | 
			
		||||
    global current_message
 | 
			
		||||
    current_message = message
 | 
			
		||||
 | 
			
		||||
    # Pre-fetch data from the DB that is used in the bugdown thread
 | 
			
		||||
    global db_data
 | 
			
		||||
    if message:
 | 
			
		||||
        realm_users = UserProfile.objects.filter(realm=message.get_realm(), is_active=True) \
 | 
			
		||||
                                         .values('id', 'full_name', 'short_name', 'email')
 | 
			
		||||
 | 
			
		||||
        db_data = {'realm_alert_words': alert_words.alert_words_in_realm(message.get_realm()),
 | 
			
		||||
                   'full_names':        dict((user['full_name'].lower(), user) for user in realm_users),
 | 
			
		||||
                   'short_names':       dict((user['short_name'].lower(), user) for user in realm_users),
 | 
			
		||||
                   'emoji':             message.get_realm().get_emoji()}
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        # Spend at most 5 seconds rendering.
 | 
			
		||||
        # Sometimes Python-Markdown is really slow; see
 | 
			
		||||
@@ -745,6 +778,7 @@ def do_convert(md, realm_domain=None, message=None):
 | 
			
		||||
        return None
 | 
			
		||||
    finally:
 | 
			
		||||
        current_message = None
 | 
			
		||||
        db_data = None
 | 
			
		||||
 | 
			
		||||
bugdown_time_start = 0
 | 
			
		||||
bugdown_total_time = 0
 | 
			
		||||
 
 | 
			
		||||
@@ -9,16 +9,5 @@ find_mentions = r'(?<![^\s\'\"\(,:<])@(?:\*\*([^\*]+)\*\*|(\w+))'
 | 
			
		||||
 | 
			
		||||
wildcards = ['all', 'everyone']
 | 
			
		||||
 | 
			
		||||
def find_user_for_mention(mention, realm):
 | 
			
		||||
    if mention in wildcards:
 | 
			
		||||
        return (True, None)
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        user = zerver.models.UserProfile.objects.filter(
 | 
			
		||||
                Q(full_name__iexact=mention) | Q(short_name__iexact=mention),
 | 
			
		||||
                is_active=True,
 | 
			
		||||
                realm=realm).order_by("id")[0]
 | 
			
		||||
    except IndexError:
 | 
			
		||||
        user = None
 | 
			
		||||
 | 
			
		||||
    return (False, user)
 | 
			
		||||
def user_mention_matches_wildcard(mention):
 | 
			
		||||
    return mention in wildcards
 | 
			
		||||
 
 | 
			
		||||
@@ -614,7 +614,7 @@ class StreamMessagesTest(AuthedTestCase):
 | 
			
		||||
        with queries_captured() as queries:
 | 
			
		||||
            send_message()
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(len(queries) <= 4)
 | 
			
		||||
        self.assertTrue(len(queries) <= 5)
 | 
			
		||||
 | 
			
		||||
    def test_message_mentions(self):
 | 
			
		||||
        user_profile = get_user_profile_by_email("iago@zulip.com")
 | 
			
		||||
@@ -3966,9 +3966,9 @@ class AlertWordTests(AuthedTestCase):
 | 
			
		||||
 | 
			
		||||
        realm_words = alert_words_in_realm(user2.realm)
 | 
			
		||||
        self.assertEqual(len(realm_words), 2)
 | 
			
		||||
        self.assertEqual(realm_words.keys(), [user1, user2])
 | 
			
		||||
        self.assertEqual(realm_words[user1], ['alert', 'word'])
 | 
			
		||||
        self.assertEqual(realm_words[user2], ['another'])
 | 
			
		||||
        self.assertEqual(realm_words.keys(), [user1.id, user2.id])
 | 
			
		||||
        self.assertEqual(realm_words[user1.id], ['alert', 'word'])
 | 
			
		||||
        self.assertEqual(realm_words[user2.id], ['another'])
 | 
			
		||||
 | 
			
		||||
    def test_json_list_default(self):
 | 
			
		||||
        self.login("hamlet@zulip.com")
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user