Skip to content
Snippets Groups Projects

Add cron worker to automatically rollout zoekt exact code search to paid namespaces

Merged John Mason requested to merge jm-namespace-assignment-service into master
1 file
+ 67
30
Compare changes
  • Side-by-side
  • Inline
require 'spec_helper'
RSpec.describe Search::Zoekt::NamespacePlanningService, feature_category: :global_search do
let(:namespace) { create(:namespace) }
let(:enabled_namespace) { create(:zoekt_enabled_namespace, namespace: namespace) }
let(:nodes) { create_list(:node, 3, unclaimed_storage_bytes: 10.gigabytes) }
let(:projects) do
let(:namespace1) { create(:namespace) }
let(:enabled_namespace1) { create(:zoekt_enabled_namespace, namespace: namespace1) }
let(:namespace2) { create(:namespace) }
let(:enabled_namespace2) { create(:zoekt_enabled_namespace, namespace: namespace2) }
let(:nodes) { create_list(:zoekt_node, 3, total_bytes: 100.gigabytes, used_bytes: 90.gigabytes) }
let(:projects_namespace1) do
[
create(:project, namespace: namespace1, statistics: create(:project_statistics, repository_size: 1.gigabyte)),
create(:project, namespace: namespace1, statistics: create(:project_statistics, repository_size: 2.gigabytes))
]
end
let(:projects_namespace2) do
[
create(:project, namespace: namespace, statistics: create(:project_statistics, repository_size: 1.gigabyte)),
create(:project, namespace: namespace, statistics: create(:project_statistics, repository_size: 2.gigabytes)),
create(:project, namespace: namespace, statistics: create(:project_statistics, repository_size: 3.gigabytes))
create(:project, namespace: namespace2, statistics: create(:project_statistics, repository_size: 3.gigabytes))
]
end
before do
# Ensure nodes are ordered by unclaimed space
allow(Search::Zoekt::Node).to receive(:order_by_unclaimed_space).and_return(nodes)
relation_stub = instance_double(ActiveRecord::Relation)
allow(relation_stub).to receive_message_chain(:where, :not).and_return(nodes)
allow(Search::Zoekt::Node).to receive(:order_by_unclaimed_space).and_return(relation_stub)
# TODO: use let_it_be
projects_namespace1
projects_namespace2
end
describe '.plan' do
subject(:plan) { described_class.plan(enabled_namespace: enabled_namespace, num_replicas: 2, buffer_factor: 1.5) }
subject(:plan) do
described_class.plan(
enabled_namespaces: [enabled_namespace1, enabled_namespace2],
num_replicas: num_replicas,
buffer_factor: buffer_factor
)
end
let(:num_replicas) { 2 }
let(:buffer_factor) { 1.5 }
it 'returns total required storage bytes across all namespaces' do
total_storage = (projects_namespace1 + projects_namespace2).sum do |p|
p.statistics.repository_size * buffer_factor
end
it 'returns the correct namespace and enabled_namespace IDs' do
expect(plan[:namespace_id]).to eq(namespace.id)
expect(plan[:enabled_namespace_id]).to eq(enabled_namespace.id)
expect(plan[:total_required_storage_bytes]).to eq(total_storage * num_replicas)
end
it 'calculates the total required storage bytes with buffer factor' do
total_storage = projects.sum { |p| p.statistics.repository_size * 1.5 }
expect(plan[:total_required_storage_bytes]).to eq(total_storage)
it 'returns plans for each enabled namespace' do
expect(plan[:namespaces].size).to eq(2)
expect(plan[:namespaces].map do |n|
n[:enabled_namespace_id]
end).to contain_exactly(enabled_namespace1.id, enabled_namespace2.id)
end
it 'creates replicas with indices assigned to nodes' do
expect(plan[:replicas].size).to eq(2)
first_replica_indices = plan[:replicas][0][:indices]
expect(first_replica_indices.size).to be <= Search::Zoekt::NamespacePlanningService::MAX_INDICES_PER_REPLICA
expect(first_replica_indices.first[:node_id]).to eq(nodes.first.id)
it 'calculates the namespace-specific required storage bytes' do
namespace1_storage = projects_namespace1.sum { |p| p.statistics.repository_size * buffer_factor }
namespace2_storage = projects_namespace2.sum { |p| p.statistics.repository_size * buffer_factor }
expect(plan[:namespaces][0][:namespace_required_storage_bytes]).to eq(namespace1_storage * num_replicas)
expect(plan[:namespaces][1][:namespace_required_storage_bytes]).to eq(namespace2_storage * num_replicas)
end
it 'assigns projects to indices based on capacity' do
first_replica_projects = plan[:replicas][0][:indices].flat_map { |index| index[:projects] }
expect(first_replica_projects).not_to be_empty
it 'assigns projects to indices for each namespace' do
namespace1_projects = plan[:namespaces][0][:replicas].flat_map { |r| r[:indices].flat_map { |i| i[:projects] } }
namespace2_projects = plan[:namespaces][1][:replicas].flat_map { |r| r[:indices].flat_map { |i| i[:projects] } }
expect(namespace1_projects).not_to be_empty
expect(namespace2_projects).not_to be_empty
end
context 'when no nodes are available' do
before do
allow(Node).to receive(:order_by_unclaimed_space).and_return([])
end
let(:nodes) { [] }
it 'logs an error for unavailable nodes' do
expect(plan[:errors]).to include(a_hash_including(type: :node_unavailable))
it 'logs an error for unavailable nodes for each namespace' do
plan[:namespaces].each do |namespace_plan|
expect(namespace_plan[:errors]).to include(a_hash_including(type: :node_unavailable))
end
end
end
@@ -57,8 +91,11 @@
allow(described_class).to receive(:MAX_INDICES_PER_REPLICA).and_return(1)
end
it 'logs an error for exceeding max indices' do
expect(plan[:errors]).to include(a_hash_including(type: :index_limit_exceeded))
fit 'logs an error for exceeding max indices for each namespace' do
binding.pry
plan[:namespaces].each do |namespace_plan|
expect(namespace_plan[:errors]).to include(a_hash_including(type: :index_limit_exceeded))
end
end
end
end
Loading