require 'spec_helper' RSpec.describe CanvasSync::JobBatches::Pool do include ActiveJob::TestHelper subject { described_class.new(order: pool_order, concurrency: pool_concurrency) } let(:pool) { subject } let(:pool_order) { :fifo } let(:pool_concurrency) { 2 } describe '#initialize' do subject { described_class } it 'creates pid when called without it' do expect(subject.new.pid).not_to be_nil end it 'reuses pid when called with it' do batch = subject.new('dayPO5KxuRXXxw') expect(batch.pid).to eq('dayPO5KxuRXXxw') end end def load_pool(count = 3) jobs = count.times.map do |i| { job: "BatchTestJobBase", args: [1] } end subject.add_jobs(jobs, skip_refill: true) end describe "#job_checked_in" do it "gets called" do expect(CanvasSync::JobBatches::Pool).to receive(:job_checked_in).twice load_pool perform_enqueued_jobs do pool.send :refill_allotment Sidekiq::Worker.drain_all end end it "refills the pool" do expect(CanvasSync::JobBatches::Pool).to receive(:job_checked_in).and_call_original.exactly(3).times load_pool perform_enqueued_jobs do pool.send :refill_allotment Sidekiq::Worker.drain_all end end end describe "#cleanup_if_empty" do it "cleans if pool is empty and allowed to close" do expect(pool).to receive(:cleanup_redis) pool.cleanup_if_empty end it "doesn't clean if pool has pending" do load_pool expect(pool).to_not receive(:cleanup_redis) pool.cleanup_if_empty end it "doesn't clean if pool has active" do subject.redis.hset("#{subject.send(:redis_key)}-active", "blocked", "{}") expect(pool).to_not receive(:cleanup_redis) pool.cleanup_if_empty end it "doesn't clean if pool has activating" do subject.redis.hincrby(subject.send(:redis_key), "_active_count", 1) expect(pool).to_not receive(:cleanup_redis) pool.cleanup_if_empty end it "doesn't clean if pool is empty but is kept open" do pool.keep_open! expect(pool).to_not receive(:cleanup_redis) pool.cleanup_if_empty end it "doesn't clean if pool is empty but clean_when_empty is false" do subject.redis.hset(subject.send(:redis_key), "clean_when_empty", "false") expect(pool).to_not receive(:cleanup_redis) pool.cleanup_if_empty end end shared_examples "basic pool tests" do describe "#push_job_to_pool" do it "adds a job to the pool" do subject.send(:push_job_to_pool, { job: 'job1' }) expect(subject.pending_count).to eq(1) end end describe "#refill_allotment" do it "refills the pool with jobs" do load_pool expect(CanvasSync::JobBatches::ChainBuilder).to receive(:enqueue_job).twice subject.send(:refill_allotment) end it "limits to the concurrency count" do load_pool expect(CanvasSync::JobBatches::ChainBuilder).to receive(:enqueue_job).twice expect(subject.send(:refill_allotment)).to eql 2 expect(subject.pending_count).to eql 1 end it "considers already active jobs against concurrency" do load_pool subject.redis.hset("#{subject.send(:redis_key)}-active", "blocked", "{}") expect(CanvasSync::JobBatches::ChainBuilder).to receive(:enqueue_job).once expect(subject.send(:refill_allotment)).to eql 2 expect(subject.pending_count).to eql 2 end it "considers activating jobs against concurrency" do load_pool subject.redis.hincrby(subject.send(:redis_key), "_active_count", 1) expect(CanvasSync::JobBatches::ChainBuilder).to receive(:enqueue_job).once expect(subject.send(:refill_allotment)).to eql 2 expect(subject.pending_count).to eql 2 end it "doesn't fail if the pool is gone" do load_pool subject.cleanup_redis expect(CanvasSync::JobBatches::ChainBuilder).not_to receive(:enqueue_job) expect(subject.send(:refill_allotment)).to eql -1 end it "doesn't fail if the pool is empty" do expect(subject.send(:refill_allotment)).to eql 0 end end end context "FIFO Pool" do let(:pool_order) { :fifo } it_behaves_like "basic pool tests" end context "LIFO Pool" do let(:pool_order) { :lifo } it_behaves_like "basic pool tests" end context "Random Pool" do let(:pool_order) { :random } it_behaves_like "basic pool tests" end context "Priority Pool" do let(:pool_order) { :priority } it_behaves_like "basic pool tests" end end