Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add maintenance window logic #115

Merged
merged 3 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,9 @@ en:
return a boolean indicating whether the page is cached and will
be served by rack middleware.
default: proc { |_rack_env| false }
bigquery_maintenance_window:
description: |
Schedule a maintenance window during which no events are streamed to BigQuery
in the format of '22-01-2024 19:30..22-01-2024 20:30'.
ericaporter marked this conversation as resolved.
Show resolved Hide resolved
default: ENV['BIGQUERY_MAINTENANCE_WINDOW']

29 changes: 29 additions & 0 deletions lib/dfe/analytics.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def self.config
pseudonymise_web_request_user_id
entity_table_checks_enabled
rack_page_cached
bigquery_maintenance_window
]

@config ||= Struct.new(*configurables).new
Expand All @@ -86,6 +87,7 @@ def self.configure
config.pseudonymise_web_request_user_id ||= false
config.entity_table_checks_enabled ||= false
config.rack_page_cached ||= proc { |_rack_env| false }
config.bigquery_maintenance_window ||= ENV.fetch('BIGQUERY_MAINTENANCE_WINDOW', nil)
end

def self.initialize!
Expand Down Expand Up @@ -244,5 +246,32 @@ def self.rack_page_cached?(rack_env)
def self.entity_table_checks_enabled?
config.entity_table_checks_enabled
end

def self.parse_maintenance_window
return [nil, nil] unless config.bigquery_maintenance_window

start_str, end_str = config.bigquery_maintenance_window.split('..', 2).map(&:strip)
begin
start_time = DateTime.strptime(start_str, '%d-%m-%Y %H:%M')
end_time = DateTime.strptime(end_str, '%d-%m-%Y %H:%M')

if start_time > end_time
Rails.logger.info('Start time is after end time in maintenance window configuration')
return [nil, nil]
end

ericaporter marked this conversation as resolved.
Show resolved Hide resolved
[start_time, end_time]
rescue ArgumentError => e
Rails.logger.info("DfE::Analytics: Unexpected error in maintenance window configuration: #{e.message}")
[nil, nil]
end
end

def self.within_maintenance_window?
start_time, end_time = parse_maintenance_window
return false unless start_time && end_time

DateTime.now.between?(start_time, end_time)
end
ericaporter marked this conversation as resolved.
Show resolved Hide resolved
end
end
6 changes: 6 additions & 0 deletions lib/dfe/analytics/send_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ def self.do(events)
end

def perform(events)
if DfE::Analytics.within_maintenance_window?
# Delaying / Queueing events - the redis bit
Rails.logger.info('Within BigQuery maintenance window. Events will be queued for later processing.')
return
end

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to remove this block here and move into the do method above.

Suggested change
if DfE::Analytics.within_maintenance_window?
# Delaying / Queueing events - the redis bit
Rails.logger.info('Within BigQuery maintenance window. Events will be queued for later processing.')
return
end

In the do method replace lines 13-17 with the following:

   if DfE::Analytics.within_maintenance_window?
     set(wait_until: DfE::Analytics.next_scheduled_time_after_maintenance_window).perform_later(events)
   elsif DfE::Analytics.async?
     perform_later(events)
   else
     perform_now(events)
   end

if DfE::Analytics.log_only?
# Use the Rails logger here as the job's logger is set to :warn by default
Rails.logger.info("DfE::Analytics: #{events.inspect}")
Expand Down
36 changes: 36 additions & 0 deletions spec/dfe/analytics/send_events_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -162,5 +162,41 @@
end
end
end

context 'within the BigQuery maintenance window' do
before do
allow(DfE::Analytics).to receive(:within_maintenance_window?).and_return(true)
end

it 'logs that events will be queued for later processing and does not send events' do
expect(Rails.logger).to receive(:info).with('Within BigQuery maintenance window. Events will be queued for later processing.')

request = stub_analytics_event_submission
DfE::Analytics::Testing.webmock! do
described_class.new.perform([event.as_json])
expect(request).not_to have_been_made
end
end
end

context 'outside the BigQuery maintenance window' do
before do
allow(DfE::Analytics).to receive(:within_maintenance_window?).and_return(false)
end
ericaporter marked this conversation as resolved.
Show resolved Hide resolved

it 'sends events to BigQuery' do
request = stub_analytics_event_submission

DfE::Analytics::Testing.webmock! do
described_class.new.perform([event.as_json])

expect(request.with do |req|
body = JSON.parse(req.body)
payload = body['rows'].first['json']
expect(payload.except('occurred_at', 'request_uuid')).to match(a_hash_including(event.deep_stringify_keys))
end).to have_been_made
end
end
end
end
end
63 changes: 63 additions & 0 deletions spec/dfe/analytics_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -184,4 +184,67 @@
end
end
end

describe '.parse_maintenance_window' do
context 'with a valid maintenance window' do
before do
allow(described_class.config).to receive(:bigquery_maintenance_window)
.and_return('01-01-2020 00:00..01-01-2020 23:59')
end

it 'returns the correct start and end times' do
start_time, end_time = described_class.parse_maintenance_window
expect(start_time).to eq(DateTime.new(2020, 1, 1, 0, 0))
expect(end_time).to eq(DateTime.new(2020, 1, 1, 23, 59))
end
end

context 'when start time is after end time' do
before do
allow(described_class.config).to receive(:bigquery_maintenance_window)
.and_return('01-01-2020 23:59..01-01-2020 00:00')
end

it 'logs an error and returns [nil, nil]' do
expect(Rails.logger).to receive(:info).with(/Start time is after end time/)
expect(described_class.parse_maintenance_window).to eq([nil, nil])
end
end

context 'with an invalid format' do
before do
allow(described_class.config).to receive(:bigquery_maintenance_window)
.and_return('invalid_format')
end

it 'logs an error and returns [nil, nil]' do
expect(Rails.logger).to receive(:info).with(/Unexpected error/)
expect(described_class.parse_maintenance_window).to eq([nil, nil])
end
end
end

describe '.within_maintenance_window?' do
context 'when the current time is within the maintenance window' do
before do
allow(described_class).to receive(:parse_maintenance_window)
.and_return([DateTime.now - 1.hour, DateTime.now + 1.hour])
end

it 'returns true' do
expect(described_class.within_maintenance_window?).to be true
end
end

context 'when the current time is outside the maintenance window' do
before do
allow(described_class).to receive(:parse_maintenance_window)
.and_return([DateTime.now - 2.days, DateTime.now - 1.day])
end

it 'returns false' do
expect(described_class.within_maintenance_window?).to be false
end
end
end
end
Loading