From 6c4c84b161947cb11ad0451a39e26b25be4c93d5 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 8 Mar 2016 20:16:11 +0100 Subject: Distrubute statuses as a fan-out-on-write system, with optional precomputing --- app/services/fan_out_on_write_service.rb | 46 ++++++++++++++++++++++++++++++++ app/services/precompute_feed_service.rb | 35 ++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 app/services/fan_out_on_write_service.rb create mode 100644 app/services/precompute_feed_service.rb (limited to 'app/services') diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb new file mode 100644 index 000000000..87a7c55ac --- /dev/null +++ b/app/services/fan_out_on_write_service.rb @@ -0,0 +1,46 @@ +class FanOutOnWriteService < BaseService + MAX_FEED_SIZE = 800 + + # Push a status into home and mentions feeds + # @param [Status] status + def call(status) + replied_to_user = status.reply? ? status.thread.account : nil + + # Deliver to local self + push(:home, status.account.id, status) if status.account.local? + + # Deliver to local followers + status.account.followers.each do |follower| + next if (status.reply? && !follower.following?(replied_to_user)) || !follower.local? + push(:home, follower.id, status) + end + + # Deliver to local mentioned + status.mentions.each do |mentioned_account| + next unless mentioned_account.local? + push(:mentions, mentioned_account.id, status) + end + end + + private + + def push(type, receiver_id, status) + redis.zadd(key(type, receiver_id), status.created_at.to_i, status.id) + trim(type, receiver_id) + end + + def trim(type, receiver_id) + return unless redis.zcard(key(type, receiver_id)) > MAX_FEED_SIZE + + last = redis.zrevrange(key(type, receiver_id), MAX_FEED_SIZE - 1, MAX_FEED_SIZE - 1) + redis.zremrangebyscore(key(type, receiver_id), '-inf', "(#{last.last}") + end + + def key(type, id) + "feed:#{type}:#{id}" + end + + def redis + $redis + end +end diff --git a/app/services/precompute_feed_service.rb b/app/services/precompute_feed_service.rb new file mode 100644 index 000000000..89b034404 --- /dev/null +++ b/app/services/precompute_feed_service.rb @@ -0,0 +1,35 @@ +class PrecomputeFeedService < BaseService + MAX_FEED_SIZE = 800 + + # Fill up a user's home/mentions feed from DB and return it + # @param [Symbol] type :home or :mentions + # @param [Account] account + # @return [Array] + def call(type, account) + statuses = send(type.to_s, account).order('created_at desc').limit(MAX_FEED_SIZE) + statuses.each { |status| push(type, account.id, status) } + statuses + end + + private + + def push(type, receiver_id, status) + redis.zadd(key(type, receiver_id), status.created_at.to_i, status.id) + end + + def home(account) + Status.where(account: [account] + account.following) + end + + def mentions(account) + Status.where(id: Mention.where(account: account).pluck(:status_id)) + end + + def key(type, id) + "feed:#{type}:#{id}" + end + + def redis + $redis + end +end -- cgit