debian-mirror-gitlab/spec/lib/gitlab/memory/watchdog_spec.rb

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

220 lines
6.7 KiB
Ruby
Raw Normal View History

2022-08-13 15:12:31 +05:30
# frozen_string_literal: true
require 'spec_helper'
2023-03-04 22:38:38 +05:30
RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, feature_category: :application_performance do
2022-08-13 15:12:31 +05:30
context 'watchdog' do
2022-11-25 23:54:43 +05:30
let(:configuration) { instance_double(described_class::Configuration) }
2023-04-23 21:23:45 +05:30
let(:handler) { instance_double(described_class::Handlers::NullHandler) }
2023-03-04 22:38:38 +05:30
let(:reporter) { instance_double(described_class::EventReporter) }
2022-11-25 23:54:43 +05:30
let(:sleep_time_seconds) { 60 }
let(:threshold_violated) { false }
2022-08-27 11:52:29 +05:30
let(:watchdog_iterations) { 1 }
2022-11-25 23:54:43 +05:30
let(:name) { :monitor_name }
let(:payload) { { message: 'dummy_text' } }
let(:max_strikes) { 2 }
let(:monitor_class) do
Struct.new(:threshold_violated, :payload) do
def call
{ threshold_violated: threshold_violated, payload: payload }
end
def self.name
'MonitorName'
end
end
end
2022-08-27 11:52:29 +05:30
2022-08-13 15:12:31 +05:30
subject(:watchdog) do
2022-11-25 23:54:43 +05:30
described_class.new.tap do |instance|
# We need to defuse `sleep` and stop the internal loop after 1 iteration
2022-08-27 11:52:29 +05:30
iterations = 0
2022-10-11 01:57:18 +05:30
allow(instance).to receive(:sleep) do
instance.stop if (iterations += 1) > watchdog_iterations
end
2022-08-27 11:52:29 +05:30
end
end
2022-11-25 23:54:43 +05:30
describe '#initialize' do
it 'initialize new configuration' do
expect(described_class::Configuration).to receive(:new)
2022-08-27 11:52:29 +05:30
watchdog
2022-08-13 15:12:31 +05:30
end
end
2022-11-25 23:54:43 +05:30
describe '#call' do
before do
watchdog.configure do |config|
config.handler = handler
2023-03-04 22:38:38 +05:30
config.event_reporter = reporter
2022-11-25 23:54:43 +05:30
config.sleep_time_seconds = sleep_time_seconds
2022-08-13 15:12:31 +05:30
end
2022-11-25 23:54:43 +05:30
allow(handler).to receive(:call).and_return(true)
2023-03-04 22:38:38 +05:30
allow(reporter).to receive(:started)
allow(reporter).to receive(:stopped)
allow(reporter).to receive(:threshold_violated)
allow(reporter).to receive(:strikes_exceeded)
2022-08-13 15:12:31 +05:30
end
2022-10-11 01:57:18 +05:30
2023-03-04 22:38:38 +05:30
it 'reports started event once' do
expect(reporter).to receive(:started).once
2023-04-23 21:23:45 +05:30
.with({
2022-11-25 23:54:43 +05:30
memwd_handler_class: handler.class.name,
2023-03-04 22:38:38 +05:30
memwd_sleep_time_s: sleep_time_seconds
2023-04-23 21:23:45 +05:30
})
2022-10-11 01:57:18 +05:30
watchdog.call
end
2022-11-25 23:54:43 +05:30
it 'waits for check interval seconds' do
expect(watchdog).to receive(:sleep).with(sleep_time_seconds)
2022-10-11 01:57:18 +05:30
watchdog.call
end
2022-08-13 15:12:31 +05:30
2023-03-04 22:38:38 +05:30
context 'when no monitors are configured' do
it 'reports stopped event once with correct reason' do
expect(reporter).to receive(:stopped).once
2023-04-23 21:23:45 +05:30
.with({
2023-03-04 22:38:38 +05:30
memwd_handler_class: handler.class.name,
memwd_sleep_time_s: sleep_time_seconds,
memwd_reason: 'monitors are not configured'
2023-04-23 21:23:45 +05:30
})
2022-08-13 15:12:31 +05:30
2022-08-27 11:52:29 +05:30
watchdog.call
2022-08-13 15:12:31 +05:30
end
2022-10-11 01:57:18 +05:30
end
2022-08-13 15:12:31 +05:30
2023-03-04 22:38:38 +05:30
context 'when monitors are configured' do
before do
watchdog.configure do |config|
config.monitors.push monitor_class, threshold_violated, payload, max_strikes: max_strikes
end
end
2022-10-11 01:57:18 +05:30
2023-03-04 22:38:38 +05:30
it 'reports stopped event once' do
expect(reporter).to receive(:stopped).once
2023-04-23 21:23:45 +05:30
.with({
2023-03-04 22:38:38 +05:30
memwd_handler_class: handler.class.name,
2023-03-17 16:20:25 +05:30
memwd_sleep_time_s: sleep_time_seconds,
memwd_reason: 'background task stopped'
2023-04-23 21:23:45 +05:30
})
2022-10-11 01:57:18 +05:30
2022-11-25 23:54:43 +05:30
watchdog.call
end
2022-10-11 01:57:18 +05:30
2023-03-04 22:38:38 +05:30
context 'when process does not exceed threshold' do
it 'does not report violations event' do
expect(reporter).not_to receive(:threshold_violated)
expect(reporter).not_to receive(:strikes_exceeded)
2022-08-13 15:12:31 +05:30
2022-08-27 11:52:29 +05:30
watchdog.call
end
2022-10-11 01:57:18 +05:30
2022-11-25 23:54:43 +05:30
it 'does not execute handler' do
expect(handler).not_to receive(:call)
2022-10-11 01:57:18 +05:30
2022-11-25 23:54:43 +05:30
watchdog.call
end
2022-10-11 01:57:18 +05:30
end
2023-03-04 22:38:38 +05:30
context 'when process exceeds threshold' do
let(:threshold_violated) { true }
2022-10-11 01:57:18 +05:30
2023-03-04 22:38:38 +05:30
it 'reports threshold violated event' do
expect(reporter).to receive(:threshold_violated).with(name)
2022-08-13 15:12:31 +05:30
2022-11-25 23:54:43 +05:30
watchdog.call
end
2022-08-13 15:12:31 +05:30
2023-03-04 22:38:38 +05:30
context 'when process does not exceed the allowed number of strikes' do
it 'does not report strikes exceeded event' do
expect(reporter).not_to receive(:strikes_exceeded)
2022-10-11 01:57:18 +05:30
2023-03-04 22:38:38 +05:30
watchdog.call
end
2022-10-11 01:57:18 +05:30
2023-03-04 22:38:38 +05:30
it 'does not execute handler' do
expect(handler).not_to receive(:call)
2022-08-13 15:12:31 +05:30
2023-03-04 22:38:38 +05:30
watchdog.call
end
2022-08-27 11:52:29 +05:30
end
2022-08-13 15:12:31 +05:30
2023-03-04 22:38:38 +05:30
context 'when monitor exceeds the allowed number of strikes' do
let(:max_strikes) { 0 }
it 'reports strikes exceeded event' do
expect(reporter).to receive(:strikes_exceeded)
.with(
2023-04-23 21:23:45 +05:30
name, {
memwd_handler_class: handler.class.name,
memwd_sleep_time_s: sleep_time_seconds,
memwd_cur_strikes: 1,
memwd_max_strikes: max_strikes,
message: "dummy_text"
})
2023-03-04 22:38:38 +05:30
watchdog.call
2022-11-25 23:54:43 +05:30
end
2022-08-13 15:12:31 +05:30
2023-03-04 22:38:38 +05:30
it 'executes handler and stops the watchdog' do
expect(handler).to receive(:call).and_return(true)
expect(reporter).to receive(:stopped).once
2023-04-23 21:23:45 +05:30
.with({
2023-03-04 22:38:38 +05:30
memwd_handler_class: handler.class.name,
memwd_sleep_time_s: sleep_time_seconds,
memwd_reason: 'successfully handled'
2023-04-23 21:23:45 +05:30
})
2022-10-11 01:57:18 +05:30
2022-11-25 23:54:43 +05:30
watchdog.call
end
2022-10-11 01:57:18 +05:30
2023-03-04 22:38:38 +05:30
it 'schedules a heap dump' do
expect(Gitlab::Memory::Reports::HeapDump).to receive(:enqueue!)
watchdog.call
end
context 'when enforce_memory_watchdog ops toggle is off' do
before do
stub_feature_flags(enforce_memory_watchdog: false)
end
it 'always uses the NullHandler' do
expect(handler).not_to receive(:call)
2023-04-23 21:23:45 +05:30
expect(described_class::Handlers::NullHandler.instance).to receive(:call).and_return(true)
2023-03-04 22:38:38 +05:30
watchdog.call
2022-11-25 23:54:43 +05:30
end
end
2023-03-04 22:38:38 +05:30
context 'when multiple monitors exceeds allowed number of strikes' do
before do
watchdog.configure do |config|
config.monitors.push monitor_class, threshold_violated, payload, max_strikes: max_strikes
config.monitors.push monitor_class, threshold_violated, payload, max_strikes: max_strikes
end
end
it 'only calls the handler once' do
expect(handler).to receive(:call).once.and_return(true)
2022-11-25 23:54:43 +05:30
2023-03-04 22:38:38 +05:30
watchdog.call
end
2022-11-25 23:54:43 +05:30
end
end
2022-10-11 01:57:18 +05:30
end
2022-08-13 15:12:31 +05:30
end
end
2022-11-25 23:54:43 +05:30
describe '#configure' do
it 'yields block' do
expect { |b| watchdog.configure(&b) }.to yield_control
2022-10-11 01:57:18 +05:30
end
2022-08-13 15:12:31 +05:30
end
end
end