]> cat aescling's git repositories - mastodon.git/commitdiff
Move network calls out of transaction in ActivityPub handler (#8951)
authorEugen Rochko <eugen@zeonfederated.com>
Wed, 10 Oct 2018 22:50:18 +0000 (00:50 +0200)
committerGitHub <noreply@github.com>
Wed, 10 Oct 2018 22:50:18 +0000 (00:50 +0200)
Mention and emoji code may perform network calls, but does not need
to do that inside the database transaction. This may improve availability
of database connections when using pgBouncer in transaction mode.

app/lib/activitypub/activity/create.rb

index 9782897884673d1e999ada832299b75809f00233..b889461cb0d014ed6befb2df6eb25cbe1e27f554 100644 (file)
@@ -22,12 +22,16 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
   private
 
   def process_status
-    status_params = process_status_params
+    @tags     = []
+    @mentions = []
+    @params   = {}
 
-    ApplicationRecord.transaction do
-      @status = Status.create!(status_params)
+    process_status_params
+    process_tags
 
-      process_tags(@status)
+    ApplicationRecord.transaction do
+      @status = Status.create!(@params)
+      attach_tags(@status)
     end
 
     resolve_thread(@status)
@@ -42,62 +46,77 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
   end
 
   def process_status_params
-    {
-      uri: @object['id'],
-      url: object_url || @object['id'],
-      account: @account,
-      text: text_from_content || '',
-      language: detected_language,
-      spoiler_text: text_from_summary || '',
-      created_at: @object['published'],
-      override_timestamps: @options[:override_timestamps],
-      reply: @object['inReplyTo'].present?,
-      sensitive: @object['sensitive'] || false,
-      visibility: visibility_from_audience,
-      thread: replied_to_status,
-      conversation: conversation_from_uri(@object['conversation']),
-      media_attachment_ids: process_attachments.take(4).map(&:id),
-    }
-  end
-
-  def process_tags(status)
+    @params = begin
+      {
+        uri: @object['id'],
+        url: object_url || @object['id'],
+        account: @account,
+        text: text_from_content || '',
+        language: detected_language,
+        spoiler_text: text_from_summary || '',
+        created_at: @object['published'],
+        override_timestamps: @options[:override_timestamps],
+        reply: @object['inReplyTo'].present?,
+        sensitive: @object['sensitive'] || false,
+        visibility: visibility_from_audience,
+        thread: replied_to_status,
+        conversation: conversation_from_uri(@object['conversation']),
+        media_attachment_ids: process_attachments.take(4).map(&:id),
+      }
+    end
+  end
+
+  def attach_tags(status)
+    @tags.each do |tag|
+      status.tags << tag
+      TrendingTags.record_use!(hashtag, status.account, status.created_at) if status.public_visibility?
+    end
+
+    @mentions.each do |mention|
+      mention.status = status
+      mention.save
+    end
+  end
+
+  def process_tags
     return if @object['tag'].nil?
 
     as_array(@object['tag']).each do |tag|
       if equals_or_includes?(tag['type'], 'Hashtag')
-        process_hashtag tag, status
+        process_hashtag tag
       elsif equals_or_includes?(tag['type'], 'Mention')
-        process_mention tag, status
+        process_mention tag
       elsif equals_or_includes?(tag['type'], 'Emoji')
-        process_emoji tag, status
+        process_emoji tag
       end
     end
   end
 
-  def process_hashtag(tag, status)
+  def process_hashtag(tag)
     return if tag['name'].blank?
 
     hashtag = tag['name'].gsub(/\A#/, '').mb_chars.downcase
     hashtag = Tag.where(name: hashtag).first_or_create(name: hashtag)
 
-    return if status.tags.include?(hashtag)
+    return if @tags.include?(hashtag)
 
-    status.tags << hashtag
-    TrendingTags.record_use!(hashtag, status.account, status.created_at) if status.public_visibility?
+    @tags << hashtag
   rescue ActiveRecord::RecordInvalid
     nil
   end
 
-  def process_mention(tag, status)
+  def process_mention(tag)
     return if tag['href'].blank?
 
     account = account_from_uri(tag['href'])
     account = ::FetchRemoteAccountService.new.call(tag['href'], id: false) if account.nil?
+
     return if account.nil?
-    account.mentions.create(status: status)
+
+    @mentions << Mention.new(account: account)
   end
 
-  def process_emoji(tag, _status)
+  def process_emoji(tag)
     return if skip_download?
     return if tag['name'].blank? || tag['icon'].blank? || tag['icon']['url'].blank?