forked from glitch-soc/mastodon
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Remove recursion, separate out into separate workers/services, add li…
…mit to global maximum statuses fetched (untested, this might not work yet)
- Loading branch information
1 parent
0c9abe1
commit 33b5964
Showing
7 changed files
with
134 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# frozen_string_literal: true | ||
|
||
module Status::FetchRepliesConcern | ||
extend ActiveSupport::Concern | ||
|
||
# debounce fetching all replies to minimize DoS | ||
FETCH_REPLIES_DEBOUNCE = 30.minutes | ||
|
||
CREATED_RECENTLY_DEBOUNCE = 10.minutes | ||
|
||
included do | ||
scope :created_recently, -> { where(created_at: CREATED_RECENTLY_DEBOUNCE.ago..) } | ||
scope :not_created_recently, -> { where(created_at: ..CREATED_RECENTLY_DEBOUNCE.ago) } | ||
scope :fetched_recently, -> { where(fetched_replies_at: FETCH_REPLIES_DEBOUNCE.ago..) } | ||
scope :not_fetched_recently, -> { where(fetched_replies_at: ..FETCH_REPLIES_DEBOUNCE.ago).or(where(fetched_replies_at: nil)) } | ||
|
||
scope :shouldnt_fetch_replies, -> { local.merge(created_recently).merge(fetched_recently) } | ||
scope :should_fetch_replies, -> { local.invert_where.merge(not_created_recently).merge(not_fetched_recently) } | ||
end | ||
|
||
def should_fetch_replies? | ||
# we aren't brand new, and we haven't fetched replies since the debounce window | ||
!local? && created_at <= 10.minutes.ago && ( | ||
fetched_replies_at.nil? || fetched_replies_at <= FETCH_REPLIES_DEBOUNCE.ago | ||
) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# frozen_string_literal: true | ||
|
||
class ActivityPub::FetchAllRepliesService < ActivityPub::FetchRepliesService | ||
include JsonLdHelper | ||
|
||
# Limit of replies to fetch per status | ||
MAX_REPLIES = 500 | ||
|
||
def call(collection_or_uri, allow_synchronous_requests: true, request_id: nil) | ||
@allow_synchronous_requests = allow_synchronous_requests | ||
@filter_by_host = false | ||
|
||
@items = collection_items(collection_or_uri) | ||
@items = filtered_replies | ||
return if @items.nil? | ||
|
||
FetchReplyWorker.push_bulk(@items) { |reply_uri| [reply_uri, { 'request_id' => request_id }] } | ||
|
||
@items | ||
end | ||
|
||
private | ||
|
||
def filtered_replies | ||
return if @items.nil? | ||
|
||
# find all statuses that we *shouldn't* update the replies for, and use that as a filter | ||
uris = @items.map { |item| value_or_id(item) } | ||
dont_update = Status.where(uri: uris).shouldnt_fetch_replies.pluck(:uri) | ||
|
||
# touch all statuses that already exist and that we're about to update | ||
Status.where(uri: uris).should_fetch_replies.touch_all(:fetched_replies_at) | ||
|
||
# Reject all statuses that we already have in the db | ||
uris.reject { |uri| dont_update.include?(uri) }.take(MAX_REPLIES) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# frozen_string_literal: true | ||
|
||
# Fetch all replies to a status, querying recursively through | ||
# ActivityPub replies collections, fetching any statuses that | ||
# we either don't already have or we haven't checked for new replies | ||
# in the Status::FETCH_REPLIES_DEBOUNCE interval | ||
class ActivityPub::FetchAllRepliesWorker | ||
include Sidekiq::Worker | ||
include ExponentialBackoff | ||
|
||
sidekiq_options queue: 'pull', retry: 3 | ||
|
||
# Global max replies to fetch per request | ||
MAX_REPLIES = 1000 | ||
|
||
def perform(parent_status_id, options = {}) | ||
@parent_status = Status.find(parent_status_id) | ||
@current_account_id = options.fetch(:current_account_id, nil) | ||
@current_account = @current_account_id.nil? ? nil : Account.find(id: @current_account_id) | ||
|
||
all_replies = get_replies(@parent_status.uri) | ||
got_replies = all_replies.length | ||
until all_replies.empty? || got_replies >= MAX_REPLIES | ||
new_replies = get_replies(all_replies.pop) | ||
got_replies += new_replies.length | ||
all_replies << new_replies | ||
end | ||
end | ||
|
||
private | ||
|
||
def get_replies(status_uri) | ||
replies_uri = get_replies_uri(status_uri) | ||
return if replies_uri.nil? | ||
|
||
ActivityPub::FetchAllRepliesService.new.call(replies_uri, **options.deep_symbolize_keys) | ||
end | ||
|
||
def get_replies_uri(parent_status_uri) | ||
json_status = fetch_resource(parent_status_uri, true, @current_account) | ||
replies_uri = json_status['replies'] | ||
Rails.logger.debug { "Could not find replies uri for status URI: #{parent_status_uri}" } if replies_uri.nil? | ||
replies_uri | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters