Fix caching of future broadcast messages

This changes the caching mechanism so we cache both current _and_ future
broadcast messages, then manually filter out those we don't want to
display. This ensures we don't need any additional queries while still
being able to display the right messages at the right time.

Fixes https://gitlab.com/gitlab-org/gitlab-ce/issues/36661
This commit is contained in:
Yorick Peterse 2017-08-18 14:52:11 +02:00
parent 646aae3e4f
commit e0b589f161
No known key found for this signature in database
GPG Key ID: EDD30D2BEB691AC9
3 changed files with 55 additions and 5 deletions

View File

@ -19,11 +19,21 @@ class BroadcastMessage < ActiveRecord::Base
after_commit :flush_redis_cache
def self.current
Rails.cache.fetch(CACHE_KEY) do
where('ends_at > :now AND starts_at <= :now', now: Time.zone.now)
.reorder(id: :asc)
.to_a
end
messages = Rails.cache.fetch(CACHE_KEY) { current_and_future_messages.to_a }
return messages if messages.empty?
now_or_future = messages.select(&:now_or_future?)
# If there are cached entries but none are to be displayed we'll purge the
# cache so we don't keep running this code all the time.
Rails.cache.delete(CACHE_KEY) if now_or_future.empty?
now_or_future.select(&:now?)
end
def self.current_and_future_messages
where('ends_at > :now', now: Time.zone.now).reorder(id: :asc)
end
def active?
@ -38,6 +48,18 @@ class BroadcastMessage < ActiveRecord::Base
ends_at < Time.zone.now
end
def now?
(starts_at..ends_at).cover?(Time.zone.now)
end
def future?
starts_at > Time.zone.now
end
def now_or_future?
now? || future?
end
def flush_redis_cache
Rails.cache.delete(CACHE_KEY)
end

View File

@ -0,0 +1,5 @@
---
title: Fix caching of future broadcast messages
merge_request:
author:
type: fixed

View File

@ -53,6 +53,29 @@ describe BroadcastMessage do
2.times { described_class.current }
end
it 'includes messages that need to be displayed in the future' do
create(:broadcast_message)
future = create(
:broadcast_message,
starts_at: Time.now + 10.minutes,
ends_at: Time.now + 20.minutes
)
expect(described_class.current.length).to eq(1)
Timecop.travel(future.starts_at) do
expect(described_class.current.length).to eq(2)
end
end
it 'does not clear the cache if only a future message should be displayed' do
create(:broadcast_message, :future)
expect(Rails.cache).not_to receive(:delete)
expect(described_class.current.length).to eq(0)
end
end
describe '#active?' do