Specs for precompute feed service (#3142)
* Add spec for precompute feed service * Refactor PrecomputeFeedService * spec wip
This commit is contained in:
		
							parent
							
								
									4a3db71692
								
							
						
					
					
						commit
						db4119f971
					
				
					 3 changed files with 57 additions and 10 deletions
				
			
		| 
						 | 
					@ -1,20 +1,48 @@
 | 
				
			||||||
# frozen_string_literal: true
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PrecomputeFeedService < BaseService
 | 
					class PrecomputeFeedService < BaseService
 | 
				
			||||||
  # Fill up a user's home/mentions feed from DB and return a subset
 | 
					  LIMIT = FeedManager::MAX_ITEMS / 4
 | 
				
			||||||
  # @param [Symbol] type :home or :mentions
 | 
					
 | 
				
			||||||
  # @param [Account] account
 | 
					  def call(account)
 | 
				
			||||||
  def call(_, account)
 | 
					    @account = account
 | 
				
			||||||
 | 
					    populate_feed
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  attr_reader :account
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def populate_feed
 | 
				
			||||||
    redis.pipelined do
 | 
					    redis.pipelined do
 | 
				
			||||||
      # NOTE: Added `id desc, account_id desc` to `ORDER BY` section to optimize query.
 | 
					      statuses.each do |status|
 | 
				
			||||||
      Status.as_home_timeline(account).order(account_id: :desc).limit(FeedManager::MAX_ITEMS / 4).each do |status|
 | 
					        process_status(status)
 | 
				
			||||||
        next if status.direct_visibility? || FeedManager.instance.filter?(:home, status, account.id)
 | 
					 | 
				
			||||||
        redis.zadd(FeedManager.instance.key(:home, account.id), status.id, status.reblog? ? status.reblog_of_id : status.id)
 | 
					 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private
 | 
					  def process_status(status)
 | 
				
			||||||
 | 
					    add_status_to_feed(status) unless skip_status?(status)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def skip_status?(status)
 | 
				
			||||||
 | 
					    status.direct_visibility? || status_filtered?(status)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def add_status_to_feed(status)
 | 
				
			||||||
 | 
					    redis.zadd(account_home_key, status.id, status.reblog? ? status.reblog_of_id : status.id)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def status_filtered?(status)
 | 
				
			||||||
 | 
					    FeedManager.instance.filter?(:home, status, account.id)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def account_home_key
 | 
				
			||||||
 | 
					    FeedManager.instance.key(:home, account.id)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def statuses
 | 
				
			||||||
 | 
					    Status.as_home_timeline(account).order(account_id: :desc).limit(LIMIT)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def redis
 | 
					  def redis
 | 
				
			||||||
    Redis.current
 | 
					    Redis.current
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,8 @@ class RegenerationWorker
 | 
				
			||||||
  sidekiq_options queue: 'pull', backtrace: true, unique: :until_executed
 | 
					  sidekiq_options queue: 'pull', backtrace: true, unique: :until_executed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def perform(account_id, _ = :home)
 | 
					  def perform(account_id, _ = :home)
 | 
				
			||||||
    PrecomputeFeedService.new.call(:home, Account.find(account_id))
 | 
					    account = Account.find(account_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    PrecomputeFeedService.new.call(account)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,22 @@
 | 
				
			||||||
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require 'rails_helper'
 | 
					require 'rails_helper'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RSpec.describe PrecomputeFeedService do
 | 
					RSpec.describe PrecomputeFeedService do
 | 
				
			||||||
  subject { PrecomputeFeedService.new }
 | 
					  subject { PrecomputeFeedService.new }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe 'call' do
 | 
				
			||||||
 | 
					    let(:account) { Fabricate(:account) }
 | 
				
			||||||
 | 
					    it 'fills a user timeline with statuses' do
 | 
				
			||||||
 | 
					      account = Fabricate(:account)
 | 
				
			||||||
 | 
					      followed_account = Fabricate(:account)
 | 
				
			||||||
 | 
					      Fabricate(:follow, account: account, target_account: followed_account)
 | 
				
			||||||
 | 
					      status = Fabricate(:status, account: followed_account)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expected_redis_args = FeedManager.instance.key(:home, account.id), status.id, status.id
 | 
				
			||||||
 | 
					      expect_any_instance_of(Redis).to receive(:zadd).with(*expected_redis_args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      subject.call(account)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue