220 lines
7 KiB
Ruby
220 lines
7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'spec_helper'
|
|
|
|
describe Gitlab::UsageDataConcerns::Topology do
|
|
include UsageDataHelpers
|
|
|
|
describe '#topology_usage_data' do
|
|
subject { Class.new.extend(described_class).topology_usage_data }
|
|
|
|
before do
|
|
# this pins down time shifts when benchmarking durations
|
|
allow(Process).to receive(:clock_gettime).and_return(0)
|
|
end
|
|
|
|
context 'when embedded Prometheus server is enabled' do
|
|
before do
|
|
expect(Gitlab::Prometheus::Internal).to receive(:prometheus_enabled?).and_return(true)
|
|
expect(Gitlab::Prometheus::Internal).to receive(:uri).and_return('http://prom:9090')
|
|
end
|
|
|
|
it 'contains a topology element' do
|
|
allow_prometheus_queries
|
|
|
|
expect(subject).to have_key(:topology)
|
|
end
|
|
|
|
context 'tracking node metrics' do
|
|
it 'contains node level metrics for each instance' do
|
|
expect_prometheus_api_to(
|
|
receive_node_memory_query,
|
|
receive_node_cpu_count_query,
|
|
receive_node_service_memory_query,
|
|
receive_node_service_process_count_query
|
|
)
|
|
|
|
expect(subject[:topology]).to eq({
|
|
duration_s: 0,
|
|
nodes: [
|
|
{
|
|
node_memory_total_bytes: 512,
|
|
node_cpus: 8,
|
|
node_services: [
|
|
{
|
|
name: 'web',
|
|
process_count: 10,
|
|
process_memory_rss: 300,
|
|
process_memory_uss: 301,
|
|
process_memory_pss: 302
|
|
},
|
|
{
|
|
name: 'sidekiq',
|
|
process_count: 5,
|
|
process_memory_rss: 303
|
|
}
|
|
]
|
|
},
|
|
{
|
|
node_memory_total_bytes: 1024,
|
|
node_cpus: 16,
|
|
node_services: [
|
|
{
|
|
name: 'sidekiq',
|
|
process_count: 15,
|
|
process_memory_rss: 400,
|
|
process_memory_pss: 401
|
|
},
|
|
{
|
|
name: 'redis',
|
|
process_count: 1,
|
|
process_memory_rss: 402
|
|
}
|
|
]
|
|
}
|
|
]
|
|
})
|
|
end
|
|
end
|
|
|
|
context 'and some node memory metrics are missing' do
|
|
it 'removes the respective entries' do
|
|
expect_prometheus_api_to(
|
|
receive_node_memory_query(result: []),
|
|
receive_node_cpu_count_query,
|
|
receive_node_service_memory_query,
|
|
receive_node_service_process_count_query
|
|
)
|
|
|
|
keys = subject[:topology][:nodes].flat_map(&:keys)
|
|
expect(keys).not_to include(:node_memory_total_bytes)
|
|
expect(keys).to include(:node_cpus, :node_services)
|
|
end
|
|
end
|
|
|
|
context 'and no results are found' do
|
|
it 'does not report anything' do
|
|
expect_prometheus_api_to receive(:aggregate).at_least(:once).and_return({})
|
|
|
|
expect(subject[:topology]).to eq({
|
|
duration_s: 0,
|
|
nodes: []
|
|
})
|
|
end
|
|
end
|
|
|
|
context 'and a connection error is raised' do
|
|
it 'does not report anything' do
|
|
expect_prometheus_api_to receive(:aggregate).and_raise('Connection failed')
|
|
|
|
expect(subject[:topology]).to eq({ duration_s: 0 })
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when embedded Prometheus server is disabled' do
|
|
it 'does not report anything' do
|
|
expect(Gitlab::Prometheus::Internal).to receive(:prometheus_enabled?).and_return(false)
|
|
|
|
expect(subject[:topology]).to eq({ duration_s: 0 })
|
|
end
|
|
end
|
|
end
|
|
|
|
def receive_node_memory_query(result: nil)
|
|
receive(:query)
|
|
.with(/node_memory_MemTotal_bytes/, an_instance_of(Hash))
|
|
.and_return(result || [
|
|
{
|
|
'metric' => { 'instance' => 'instance1:8080' },
|
|
'value' => [1000, '512']
|
|
},
|
|
{
|
|
'metric' => { 'instance' => 'instance2:8090' },
|
|
'value' => [1000, '1024']
|
|
}
|
|
])
|
|
end
|
|
|
|
def receive_node_cpu_count_query(result: nil)
|
|
receive(:query)
|
|
.with(/node_cpu_seconds_total/, an_instance_of(Hash))
|
|
.and_return(result || [
|
|
{
|
|
'metric' => { 'instance' => 'instance2:8090' },
|
|
'value' => [1000, '16']
|
|
},
|
|
{
|
|
'metric' => { 'instance' => 'instance1:8080' },
|
|
'value' => [1000, '8']
|
|
}
|
|
])
|
|
end
|
|
|
|
def receive_node_service_memory_query(result: nil)
|
|
receive(:query)
|
|
.with(/process_.+_memory_bytes/, an_instance_of(Hash))
|
|
.and_return(result || [
|
|
# instance 1: runs Puma + a small Sidekiq
|
|
{
|
|
'metric' => { 'instance' => 'instance1:8080', 'job' => 'gitlab-rails', '__name__' => 'ruby_process_resident_memory_bytes' },
|
|
'value' => [1000, '300']
|
|
},
|
|
{
|
|
'metric' => { 'instance' => 'instance1:8080', 'job' => 'gitlab-rails', '__name__' => 'ruby_process_unique_memory_bytes' },
|
|
'value' => [1000, '301']
|
|
},
|
|
{
|
|
'metric' => { 'instance' => 'instance1:8080', 'job' => 'gitlab-rails', '__name__' => 'ruby_process_proportional_memory_bytes' },
|
|
'value' => [1000, '302']
|
|
},
|
|
{
|
|
'metric' => { 'instance' => 'instance1:8090', 'job' => 'gitlab-sidekiq', '__name__' => 'ruby_process_resident_memory_bytes' },
|
|
'value' => [1000, '303']
|
|
},
|
|
# instance 2: runs a dedicated Sidekiq + Redis (which uses a different metric name)
|
|
{
|
|
'metric' => { 'instance' => 'instance2:8090', 'job' => 'gitlab-sidekiq', '__name__' => 'ruby_process_resident_memory_bytes' },
|
|
'value' => [1000, '400']
|
|
},
|
|
{
|
|
'metric' => { 'instance' => 'instance2:8090', 'job' => 'gitlab-sidekiq', '__name__' => 'ruby_process_proportional_memory_bytes' },
|
|
'value' => [1000, '401']
|
|
},
|
|
{
|
|
'metric' => { 'instance' => 'instance2:9121', 'job' => 'redis', '__name__' => 'process_resident_memory_bytes' },
|
|
'value' => [1000, '402']
|
|
}
|
|
])
|
|
end
|
|
|
|
def receive_node_service_process_count_query(result: nil)
|
|
receive(:query)
|
|
.with(/process_start_time_seconds/, an_instance_of(Hash))
|
|
.and_return(result || [
|
|
# instance 1
|
|
{
|
|
'metric' => { 'instance' => 'instance1:8080', 'job' => 'gitlab-rails' },
|
|
'value' => [1000, '10']
|
|
},
|
|
{
|
|
'metric' => { 'instance' => 'instance1:8090', 'job' => 'gitlab-sidekiq' },
|
|
'value' => [1000, '5']
|
|
},
|
|
# instance 2
|
|
{
|
|
'metric' => { 'instance' => 'instance2:8090', 'job' => 'gitlab-sidekiq' },
|
|
'value' => [1000, '15']
|
|
},
|
|
{
|
|
'metric' => { 'instance' => 'instance2:9121', 'job' => 'redis' },
|
|
'value' => [1000, '1']
|
|
},
|
|
# unknown service => should be stripped out
|
|
{
|
|
'metric' => { 'instance' => 'instance2:9000', 'job' => 'not-a-gitlab-service' },
|
|
'value' => [1000, '42']
|
|
}
|
|
])
|
|
end
|
|
end
|