Commit 2bae697a authored by Vijay Hawoldar's avatar Vijay Hawoldar

Refactor NamespaceStatistics into CE

So that we can calculate dependency proxy size at the namespace level,
this refactors the NamespaceStatistics model into CE to make it
available outside of EE

Changelog: changed
parent 56c44a68
......@@ -43,6 +43,7 @@ class Namespace < ApplicationRecord
has_many :projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :project_statistics
has_one :namespace_settings, inverse_of: :namespace, class_name: 'NamespaceSetting', autosave: true
has_one :namespace_statistics
has_one :namespace_route, foreign_key: :namespace_id, autosave: false, inverse_of: :namespace, class_name: 'Route'
has_many :namespace_members, foreign_key: :member_namespace_id, inverse_of: :member_namespace, class_name: 'Member'
......
......@@ -27,10 +27,17 @@ class Namespace::RootStorageStatistics < ApplicationRecord
update!(merged_attributes)
end
def self.namespace_statistics_attributes
%w(storage_size dependency_proxy_size)
end
private
def merged_attributes
attributes_from_project_statistics.merge!(attributes_from_personal_snippets) { |key, v1, v2| v1 + v2 }
attributes_from_project_statistics.merge!(
attributes_from_personal_snippets,
attributes_from_namespace_statistics
) { |key, v1, v2| v1 + v2 }
end
def attributes_from_project_statistics
......@@ -68,6 +75,27 @@ class Namespace::RootStorageStatistics < ApplicationRecord
.where(author: namespace.owner_id)
.select("COALESCE(SUM(s.repository_size), 0) AS #{SNIPPETS_SIZE_STAT_NAME}")
end
def from_namespace_statistics
namespace
.self_and_descendants
.joins("INNER JOIN namespace_statistics ns ON ns.namespace_id = namespaces.id")
.select(
'COALESCE(SUM(ns.storage_size), 0) AS storage_size',
'COALESCE(SUM(ns.dependency_proxy_size), 0) AS dependency_proxy_size'
)
end
def attributes_from_namespace_statistics
# At the moment, only groups can have some storage data because of dependency proxy assets.
# Therefore, if the namespace is not a group one, there is no need to perform
# the query. If this changes in the future and we add some sort of resource to
# users that it's store in NamespaceStatistics, we will need to remove this
# guard clause.
return {} unless namespace.group_namespace?
from_namespace_statistics.take.slice(*self.class.namespace_statistics_attributes)
end
end
Namespace::RootStorageStatistics.prepend_mod_with('Namespace::RootStorageStatistics')
# frozen_string_literal: true
class NamespaceStatistics < ApplicationRecord
class NamespaceStatistics < ApplicationRecord # rubocop:disable Gitlab/NamespacedClass
include AfterCommitQueue
belongs_to :namespace
......@@ -15,13 +15,11 @@ class NamespaceStatistics < ApplicationRecord
delegate :group_namespace?, to: :namespace
COLUMNS_TO_REFRESH = [:wiki_size].freeze
def refresh!(only: [])
return if Gitlab::Database.read_only?
return unless group_namespace?
COLUMNS_TO_REFRESH.each do |column|
self.class.columns_to_refresh.each do |column|
if only.empty? || only.include?(column)
public_send("update_#{column}") # rubocop:disable GitlabSecurity/PublicSend
end
......@@ -31,21 +29,21 @@ class NamespaceStatistics < ApplicationRecord
end
def update_storage_size
self.storage_size = wiki_size
self.storage_size = dependency_proxy_size
end
def update_wiki_size
return unless group_wiki_available?
def update_dependency_proxy_size
return unless group_namespace?
self.wiki_size = namespace.wiki.repository.size.megabytes
self.dependency_proxy_size = namespace.dependency_proxy_manifests.sum(:size) + namespace.dependency_proxy_blobs.sum(:size)
end
private
def group_wiki_available?
group_namespace? && namespace.feature_available?(:group_wikis)
def self.columns_to_refresh
[:dependency_proxy_size]
end
private
def update_root_storage_statistics
return unless group_namespace?
......@@ -54,3 +52,5 @@ class NamespaceStatistics < ApplicationRecord
end
end
end
NamespaceStatistics.prepend_mod_with('NamespaceStatistics')
......@@ -23,7 +23,6 @@ module EE
prepended do
include EachBatch
has_one :namespace_statistics
has_one :namespace_limit, inverse_of: :namespace
has_one :gitlab_subscription
has_one :elasticsearch_indexed_namespace
......
......@@ -6,34 +6,20 @@ module EE
extend ActiveSupport::Concern
extend ::Gitlab::Utils::Override
prepended do
NAMESPACE_STATISTICS_ATTRIBUTES = %w[storage_size wiki_size].freeze
end
class_methods do
extend ::Gitlab::Utils::Override
override :merged_attributes
def merged_attributes
super.merge!(attributes_from_namespace_statistics) { |key, v1, v2| v1 + v2 }
override :namespace_statistics_attributes
def namespace_statistics_attributes
super << 'wiki_size'
end
end
private
def attributes_from_namespace_statistics
# At the moment, only groups can have some storage data because of group wikis.
# Therefore, if the namespace is not a group one, there is no need to perform
# the query. If this changes in the future and we add some sort of resource to
# users that it's store in NamespaceStatistics, we will need to remove this
# guard clause.
return {} unless namespace.group_namespace?
from_namespace_statistics.take.slice(*NAMESPACE_STATISTICS_ATTRIBUTES)
end
override :from_namespace_statistics
def from_namespace_statistics
namespace
.self_and_descendants
.joins("INNER JOIN namespace_statistics ns ON ns.namespace_id = namespaces.id")
.select(
'COALESCE(SUM(ns.storage_size), 0) AS storage_size',
super.select(
'COALESCE(SUM(ns.wiki_size), 0) AS wiki_size'
)
end
......
# frozen_string_literal: true
module EE
module NamespaceStatistics
extend ActiveSupport::Concern
extend ::Gitlab::Utils::Override
def update_storage_size
super
self.storage_size += wiki_size
end
def update_wiki_size
return unless group_wiki_available?
self.wiki_size = namespace.wiki.repository.size.megabytes
end
class_methods do
extend ::Gitlab::Utils::Override
override :columns_to_refresh
def columns_to_refresh
super << :wiki_size
end
end
private
def group_wiki_available?
group_namespace? && namespace.licensed_feature_available?(:group_wikis)
end
end
end
......@@ -22,7 +22,7 @@ RSpec.describe EE::Namespace::RootStorageStatistics do
let(:namespace) { root_group }
it 'aggregates namespace statistics' do
it 'aggregates namespace wiki statistics' do
# This group is not a descendant of the root_group so it shouldn't be included in the final stats.
other_group = create(:group)
create(:namespace_statistics, namespace: other_group, storage_size: 500, wiki_size: 500)
......@@ -31,23 +31,9 @@ RSpec.describe EE::Namespace::RootStorageStatistics do
root_storage_statistics.reload
total_repository_size = project_stat1.repository_size + project_stat2.repository_size
total_lfs_objects_size = project_stat1.lfs_objects_size + project_stat2.lfs_objects_size
total_build_artifacts_size = project_stat1.build_artifacts_size + project_stat2.build_artifacts_size
total_packages_size = project_stat1.packages_size + project_stat2.packages_size
total_snippets_size = project_stat1.snippets_size + project_stat2.snippets_size
total_pipeline_artifacts_size = project_stat1.pipeline_artifacts_size + project_stat2.pipeline_artifacts_size
total_uploads_size = project_stat1.uploads_size + project_stat2.uploads_size
total_wiki_size = project_stat1.wiki_size + project_stat2.wiki_size + root_namespace_stat.wiki_size + group1_namespace_stat.wiki_size + group2_namespace_stat.wiki_size + subgroup1_namespace_stat.wiki_size
total_storage_size = project_stat1.storage_size + project_stat2.storage_size + root_namespace_stat.storage_size + group1_namespace_stat.storage_size + group2_namespace_stat.storage_size + subgroup1_namespace_stat.storage_size
expect(root_storage_statistics.repository_size).to eq(total_repository_size)
expect(root_storage_statistics.lfs_objects_size).to eq(total_lfs_objects_size)
expect(root_storage_statistics.build_artifacts_size).to eq(total_build_artifacts_size)
expect(root_storage_statistics.packages_size).to eq(total_packages_size)
expect(root_storage_statistics.snippets_size).to eq(total_snippets_size)
expect(root_storage_statistics.pipeline_artifacts_size).to eq(total_pipeline_artifacts_size)
expect(root_storage_statistics.uploads_size).to eq(total_uploads_size)
expect(root_storage_statistics.storage_size).to eq(total_storage_size)
expect(root_storage_statistics.wiki_size).to eq(total_wiki_size)
end
......
......@@ -14,7 +14,6 @@ RSpec.describe Namespace do
let!(:premium_plan) { create(:premium_plan) }
let!(:ultimate_plan) { create(:ultimate_plan) }
it { is_expected.to have_one(:namespace_statistics) }
it { is_expected.to have_one(:namespace_limit) }
it { is_expected.to have_one(:elasticsearch_indexed_namespace) }
it { is_expected.to have_one :upcoming_reconciliation }
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe NamespaceStatistics do
include WikiHelpers
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:group_wiki) do
create(:group_wiki, group: group).tap do |group_wiki|
group_wiki.create_page('home', 'test content')
end
end
describe '#refresh!' do
let(:namespace) { group }
let(:statistics) { create(:namespace_statistics, namespace: namespace) }
let(:columns) { [] }
subject(:refresh!) { statistics.refresh!(only: columns) }
shared_examples 'creates the namespace statistics' do
specify do
expect(statistics).to receive(:save!)
refresh!
end
end
context 'when no option is passed' do
it 'updates the wiki size' do
expect(statistics).to receive(:update_wiki_size)
refresh!
end
it_behaves_like 'creates the namespace statistics'
end
context 'when wiki_size option is passed' do
let(:columns) { [:wiki_size] }
it 'updates the wiki size' do
expect(statistics).to receive(:update_wiki_size)
refresh!
end
it_behaves_like 'creates the namespace statistics'
end
end
describe '#update_storage_size' do
let_it_be(:statistics, reload: true) { create(:namespace_statistics, namespace: group, dependency_proxy_size: 2, storage_size: 2) }
it 'adds wiki_size to the storage_size' do
statistics.wiki_size = 3
statistics.update_storage_size
expect(statistics.storage_size).to eq 5
end
end
describe '#update_wiki_size' do
let_it_be(:statistics, reload: true) { create(:namespace_statistics, namespace: group) }
subject(:update_wiki_size) { statistics.update_wiki_size }
context 'when group_wikis feature is not enabled' do
it 'does not update the wiki size' do
stub_group_wikis(false)
update_wiki_size
expect(statistics.wiki_size).to be_zero
end
end
context 'when group_wikis feature is enabled' do
before do
stub_group_wikis(true)
end
it 'updates the wiki size' do
update_wiki_size
expect(statistics.wiki_size).to eq group.wiki.repository.size.megabytes.to_i
end
context 'when namespace does not belong to a group' do
let(:statistics) { create(:namespace_statistics, namespace: user.namespace) }
it 'does not update the wiki size' do
expect(statistics).not_to receive(:wiki)
update_wiki_size
expect(statistics.wiki_size).to be_zero
end
end
end
end
end
......@@ -28,24 +28,24 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do
let(:project1) { create(:project, namespace: namespace) }
let(:project2) { create(:project, namespace: namespace) }
let!(:stat1) { create(:project_statistics, project: project1, with_data: true, size_multiplier: 100) }
let!(:stat2) { create(:project_statistics, project: project2, with_data: true, size_multiplier: 200) }
let!(:project_stat1) { create(:project_statistics, project: project1, with_data: true, size_multiplier: 100) }
let!(:project_stat2) { create(:project_statistics, project: project2, with_data: true, size_multiplier: 200) }
shared_examples 'data refresh' do
shared_examples 'project data refresh' do
it 'aggregates project statistics' do
root_storage_statistics.recalculate!
root_storage_statistics.reload
total_repository_size = stat1.repository_size + stat2.repository_size
total_wiki_size = stat1.wiki_size + stat2.wiki_size
total_lfs_objects_size = stat1.lfs_objects_size + stat2.lfs_objects_size
total_build_artifacts_size = stat1.build_artifacts_size + stat2.build_artifacts_size
total_packages_size = stat1.packages_size + stat2.packages_size
total_storage_size = stat1.storage_size + stat2.storage_size
total_snippets_size = stat1.snippets_size + stat2.snippets_size
total_pipeline_artifacts_size = stat1.pipeline_artifacts_size + stat2.pipeline_artifacts_size
total_uploads_size = stat1.uploads_size + stat2.uploads_size
total_repository_size = project_stat1.repository_size + project_stat2.repository_size
total_wiki_size = project_stat1.wiki_size + project_stat2.wiki_size
total_lfs_objects_size = project_stat1.lfs_objects_size + project_stat2.lfs_objects_size
total_build_artifacts_size = project_stat1.build_artifacts_size + project_stat2.build_artifacts_size
total_packages_size = project_stat1.packages_size + project_stat2.packages_size
total_storage_size = project_stat1.storage_size + project_stat2.storage_size
total_snippets_size = project_stat1.snippets_size + project_stat2.snippets_size
total_pipeline_artifacts_size = project_stat1.pipeline_artifacts_size + project_stat2.pipeline_artifacts_size
total_uploads_size = project_stat1.uploads_size + project_stat2.uploads_size
expect(root_storage_statistics.repository_size).to eq(total_repository_size)
expect(root_storage_statistics.wiki_size).to eq(total_wiki_size)
......@@ -83,7 +83,7 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do
end
end
it_behaves_like 'data refresh'
it_behaves_like 'project data refresh'
it_behaves_like 'does not include personal snippets'
context 'with subgroups' do
......@@ -93,19 +93,81 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do
let(:project1) { create(:project, namespace: subgroup1) }
let(:project2) { create(:project, namespace: subgroup2) }
it_behaves_like 'data refresh'
it_behaves_like 'project data refresh'
it_behaves_like 'does not include personal snippets'
end
context 'with a group namespace' do
let_it_be(:root_group) { create(:group) }
let_it_be(:group1) { create(:group, parent: root_group) }
let_it_be(:subgroup1) { create(:group, parent: group1) }
let_it_be(:group2) { create(:group, parent: root_group) }
let_it_be(:root_namespace_stat) { create(:namespace_statistics, namespace: root_group, storage_size: 100, dependency_proxy_size: 100) }
let_it_be(:group1_namespace_stat) { create(:namespace_statistics, namespace: group1, storage_size: 200, dependency_proxy_size: 200) }
let_it_be(:group2_namespace_stat) { create(:namespace_statistics, namespace: group2, storage_size: 300, dependency_proxy_size: 300) }
let_it_be(:subgroup1_namespace_stat) { create(:namespace_statistics, namespace: subgroup1, storage_size: 300, dependency_proxy_size: 100) }
let(:namespace) { root_group }
it 'aggregates namespace statistics' do
# This group is not a descendant of the root_group so it shouldn't be included in the final stats.
other_group = create(:group)
create(:namespace_statistics, namespace: other_group, storage_size: 500, dependency_proxy_size: 500)
root_storage_statistics.recalculate!
total_repository_size = project_stat1.repository_size + project_stat2.repository_size
total_lfs_objects_size = project_stat1.lfs_objects_size + project_stat2.lfs_objects_size
total_build_artifacts_size = project_stat1.build_artifacts_size + project_stat2.build_artifacts_size
total_packages_size = project_stat1.packages_size + project_stat2.packages_size
total_snippets_size = project_stat1.snippets_size + project_stat2.snippets_size
total_pipeline_artifacts_size = project_stat1.pipeline_artifacts_size + project_stat2.pipeline_artifacts_size
total_uploads_size = project_stat1.uploads_size + project_stat2.uploads_size
total_wiki_size = project_stat1.wiki_size + project_stat2.wiki_size
total_dependency_proxy_size = root_namespace_stat.dependency_proxy_size + group1_namespace_stat.dependency_proxy_size + group2_namespace_stat.dependency_proxy_size + subgroup1_namespace_stat.dependency_proxy_size
total_storage_size = project_stat1.storage_size + project_stat2.storage_size + root_namespace_stat.storage_size + group1_namespace_stat.storage_size + group2_namespace_stat.storage_size + subgroup1_namespace_stat.storage_size
expect(root_storage_statistics.repository_size).to eq(total_repository_size)
expect(root_storage_statistics.lfs_objects_size).to eq(total_lfs_objects_size)
expect(root_storage_statistics.build_artifacts_size).to eq(total_build_artifacts_size)
expect(root_storage_statistics.packages_size).to eq(total_packages_size)
expect(root_storage_statistics.snippets_size).to eq(total_snippets_size)
expect(root_storage_statistics.pipeline_artifacts_size).to eq(total_pipeline_artifacts_size)
expect(root_storage_statistics.uploads_size).to eq(total_uploads_size)
expect(root_storage_statistics.dependency_proxy_size).to eq(total_dependency_proxy_size)
expect(root_storage_statistics.wiki_size).to eq(total_wiki_size)
expect(root_storage_statistics.storage_size).to eq(total_storage_size)
end
it 'works when there are no namespace statistics' do
NamespaceStatistics.delete_all
root_storage_statistics.recalculate!
total_storage_size = project_stat1.storage_size + project_stat2.storage_size
expect(root_storage_statistics.storage_size).to eq(total_storage_size)
end
end
context 'with a personal namespace' do
let_it_be(:user) { create(:user) }
let(:namespace) { user.namespace }
it_behaves_like 'data refresh'
it_behaves_like 'project data refresh'
it 'does not aggregate namespace statistics' do
create(:namespace_statistics, namespace: user.namespace, storage_size: 200, dependency_proxy_size: 200)
root_storage_statistics.recalculate!
expect(root_storage_statistics.storage_size).to eq(project_stat1.storage_size + project_stat2.storage_size)
expect(root_storage_statistics.dependency_proxy_size).to eq(0)
end
context 'when user has personal snippets' do
let(:total_project_snippets_size) { stat1.snippets_size + stat2.snippets_size }
let(:total_project_snippets_size) { project_stat1.snippets_size + project_stat2.snippets_size }
it 'aggregates personal and project snippets size' do
# This is just a a snippet authored by other user
......
......@@ -23,6 +23,7 @@ RSpec.describe Namespace do
it { is_expected.to have_one :root_storage_statistics }
it { is_expected.to have_one :aggregation_schedule }
it { is_expected.to have_one :namespace_settings }
it { is_expected.to have_one(:namespace_statistics) }
it { is_expected.to have_many :custom_emoji }
it { is_expected.to have_one :package_setting_relation }
it { is_expected.to have_one :onboarding_progress }
......
......@@ -3,15 +3,8 @@
require 'spec_helper'
RSpec.describe NamespaceStatistics do
include WikiHelpers
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:group_wiki) do
create(:group_wiki, group: group).tap do |group_wiki|
group_wiki.create_page('home', 'test content')
end
end
it { is_expected.to belong_to(:namespace) }
......@@ -22,7 +15,7 @@ RSpec.describe NamespaceStatistics do
let(:statistics) { create(:namespace_statistics, namespace: namespace) }
let(:columns) { [] }
subject { statistics.refresh!(only: columns) }
subject(:refresh!) { statistics.refresh!(only: columns) }
context 'when database is read_only' do
it 'does not save the object' do
......@@ -30,7 +23,7 @@ RSpec.describe NamespaceStatistics do
expect(statistics).not_to receive(:save!)
subject
refresh!
end
end
......@@ -40,7 +33,7 @@ RSpec.describe NamespaceStatistics do
it 'does not save the object' do
expect(statistics).not_to receive(:save!)
subject
refresh!
end
end
......@@ -48,7 +41,7 @@ RSpec.describe NamespaceStatistics do
specify do
expect(statistics).to receive(:save!)
subject
refresh!
end
end
......@@ -56,31 +49,32 @@ RSpec.describe NamespaceStatistics do
let(:columns) { [:foo] }
it 'does not update any column' do
expect(statistics).not_to receive(:update_wiki_size)
create(:dependency_proxy_manifest, group: namespace, size: 50)
subject
expect(statistics).not_to receive(:update_dependency_proxy_size)
expect { refresh! }.not_to change { statistics.reload.storage_size }
end
it_behaves_like 'creates the namespace statistics'
end
context 'when no option is passed' do
it 'updates the wiki size' do
expect(statistics).to receive(:update_wiki_size)
it 'updates the dependency proxy size' do
expect(statistics).to receive(:update_dependency_proxy_size)
subject
refresh!
end
it_behaves_like 'creates the namespace statistics'
end
context 'when wiki_size option is passed' do
let(:columns) { [:wiki_size] }
context 'when dependency_proxy_size option is passed' do
let(:columns) { [:dependency_proxy_size] }
it 'updates the wiki size' do
expect(statistics).to receive(:update_wiki_size)
it 'updates the dependency proxy size' do
expect(statistics).to receive(:update_dependency_proxy_size)
subject
refresh!
end
it_behaves_like 'creates the namespace statistics'
......@@ -90,8 +84,8 @@ RSpec.describe NamespaceStatistics do
describe '#update_storage_size' do
let_it_be(:statistics, reload: true) { create(:namespace_statistics, namespace: group) }
it 'sets storage_size to the wiki_size' do
statistics.wiki_size = 3
it 'sets storage_size to the dependency_proxy_size' do
statistics.dependency_proxy_size = 3
statistics.update_storage_size
......@@ -99,48 +93,32 @@ RSpec.describe NamespaceStatistics do
end
end
describe '#update_wiki_size' do
describe '#update_dependency_proxy_size' do
let_it_be(:statistics, reload: true) { create(:namespace_statistics, namespace: group) }
let_it_be(:dependency_proxy_manifest) { create(:dependency_proxy_manifest, group: group, size: 50) }
let_it_be(:dependency_proxy_blob) { create(:dependency_proxy_blob, group: group, size: 50) }
subject { statistics.update_wiki_size }
context 'when group_wikis feature is not enabled' do
it 'does not update the wiki size' do
stub_group_wikis(false)
subject(:update_dependency_proxy_size) { statistics.update_dependency_proxy_size }
subject
it 'updates the dependency proxy size' do
update_dependency_proxy_size
expect(statistics.wiki_size).to be_zero
end
end
context 'when group_wikis feature is enabled enabled' do
before do
stub_group_wikis(true)
end
it 'updates the wiki size' do
subject
expect(statistics.wiki_size).to eq group.wiki.repository.size.megabytes.to_i
expect(statistics.dependency_proxy_size).to eq 100
end
context 'when namespace does not belong to a group' do
let(:statistics) { create(:namespace_statistics, namespace: user.namespace) }
it 'does not update the wiki size' do
expect(statistics).not_to receive(:wiki)
subject
it 'does not update the dependency proxy size' do
update_dependency_proxy_size
expect(statistics.wiki_size).to be_zero
end
expect(statistics.dependency_proxy_size).to be_zero
end
end
end
context 'before saving statistics' do
let(:statistics) { create(:namespace_statistics, namespace: group, wiki_size: 10) }
let(:statistics) { create(:namespace_statistics, namespace: group, dependency_proxy_size: 10) }
it 'updates storage size' do
expect(statistics).to receive(:update_storage_size).and_call_original
......@@ -167,9 +145,9 @@ RSpec.describe NamespaceStatistics do
context 'when storage_size is updated' do
before do
# we have to update this value instead of `storage_size` because the before_save
# hook we have. If we don't do it, storage_size will be set to the wiki_size value
# hook we have. If we don't do it, storage_size will be set to the dependency_proxy_size value
# which is 0.
statistics.wiki_size = 10
statistics.dependency_proxy_size = 10
end
it 'enqueues the job to update root storage statistics' do
......@@ -193,7 +171,7 @@ RSpec.describe NamespaceStatistics do
context 'when other columns are updated' do
it 'does not enqueue the job to update root storage statistics' do
columns_to_update = NamespaceStatistics.columns_hash.except('id', 'namespace_id', 'wiki_size', 'storage_size').keys
columns_to_update = NamespaceStatistics.columns_hash.reject { |k, _| %w(id namespace_id).include?(k) || k.include?('_size') }.keys
columns_to_update.each { |c| statistics[c] = 10 }
expect(statistics).not_to receive(:update_root_storage_statistics)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment