Commit e4071fe2 authored by Valery Sizov's avatar Valery Sizov

[Geo] Fix the "Storage config" parameter in Geo nodes admin page

parent 3090c6b1
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20180704204006) do ActiveRecord::Schema.define(version: 20180719161844) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -1154,6 +1154,7 @@ ActiveRecord::Schema.define(version: 20180704204006) do ...@@ -1154,6 +1154,7 @@ ActiveRecord::Schema.define(version: 20180704204006) do
t.integer "wikis_checksummed_count" t.integer "wikis_checksummed_count"
t.integer "wikis_checksum_failed_count" t.integer "wikis_checksum_failed_count"
t.integer "wikis_checksum_mismatch_count" t.integer "wikis_checksum_mismatch_count"
t.binary "storage_configuration_digest"
end end
add_index "geo_node_statuses", ["geo_node_id"], name: "index_geo_node_statuses_on_geo_node_id", unique: true, using: :btree add_index "geo_node_statuses", ["geo_node_id"], name: "index_geo_node_statuses_on_geo_node_id", unique: true, using: :btree
......
class GeoNodeStatus < ActiveRecord::Base class GeoNodeStatus < ActiveRecord::Base
include ShaAttribute
belongs_to :geo_node belongs_to :geo_node
delegate :selective_sync_type, to: :geo_node delegate :selective_sync_type, to: :geo_node
...@@ -18,6 +20,8 @@ class GeoNodeStatus < ActiveRecord::Base ...@@ -18,6 +20,8 @@ class GeoNodeStatus < ActiveRecord::Base
:hashed_storage_migrated_max_id, :hashed_storage_attachments_max_id, :hashed_storage_migrated_max_id, :hashed_storage_attachments_max_id,
:repositories_checked_count, :repositories_checked_failed_count :repositories_checked_count, :repositories_checked_failed_count
sha_attribute :storage_configuration_digest
# Be sure to keep this consistent with Prometheus naming conventions # Be sure to keep this consistent with Prometheus naming conventions
PROMETHEUS_METRICS = { PROMETHEUS_METRICS = {
db_replication_lag_seconds: 'Database replication lag (seconds)', db_replication_lag_seconds: 'Database replication lag (seconds)',
...@@ -155,6 +159,7 @@ class GeoNodeStatus < ActiveRecord::Base ...@@ -155,6 +159,7 @@ class GeoNodeStatus < ActiveRecord::Base
self.attachments_count = attachments_finder.count_syncable self.attachments_count = attachments_finder.count_syncable
self.last_successful_status_check_at = Time.now self.last_successful_status_check_at = Time.now
self.storage_shards = StorageShard.all self.storage_shards = StorageShard.all
self.storage_configuration_digest = StorageShard.build_digest
self.version = Gitlab::VERSION self.version = Gitlab::VERSION
self.revision = Gitlab.revision self.revision = Gitlab.revision
...@@ -330,13 +335,11 @@ class GeoNodeStatus < ActiveRecord::Base ...@@ -330,13 +335,11 @@ class GeoNodeStatus < ActiveRecord::Base
calc_percentage(replication_slots_count, replication_slots_used_count) calc_percentage(replication_slots_count, replication_slots_used_count)
end end
# This method only is useful when the storage shard information is loaded
# from a remote node via JSON.
def storage_shards_match? def storage_shards_match?
return unless Gitlab::Geo.primary? return true if geo_node.primary?
return unless current_shards && primary_shards return false unless storage_configuration_digest && primary_storage_digest
shards_match?(current_shards, primary_shards) storage_configuration_digest == primary_storage_digest
end end
def [](key) def [](key)
...@@ -345,28 +348,8 @@ class GeoNodeStatus < ActiveRecord::Base ...@@ -345,28 +348,8 @@ class GeoNodeStatus < ActiveRecord::Base
private private
def current_shards def primary_storage_digest
serialize_storage_shards(storage_shards) @primary_storage_digest ||= Gitlab::Geo.primary_node.find_or_build_status.storage_configuration_digest
end
def primary_shards
serialize_storage_shards(StorageShard.all)
end
def serialize_storage_shards(shards)
StorageShardSerializer.new.represent(shards).as_json
end
def shards_match?(first, second)
names_match?(first, second)
end
def names_match?(first, second)
extract_names(first) == extract_names(second)
end
def extract_names(shards)
shards.map { |shard| shard['name'] }.sort
end end
def attachments_finder def attachments_finder
......
require 'digest'
# This is an in-memory structure only. The repository storage configuration is # This is an in-memory structure only. The repository storage configuration is
# in gitlab.yml and not in the database. This model makes it easier to work # in gitlab.yml and not in the database. This model makes it easier to work
# with the configuration. # with the configuration.
...@@ -25,4 +26,9 @@ class StorageShard ...@@ -25,4 +26,9 @@ class StorageShard
def self.allowed_params def self.allowed_params
%i(name).freeze %i(name).freeze
end end
def self.build_digest
names = Settings.repositories.storages.keys.sort
Digest::SHA1.hexdigest(names.join)
end
end end
---
title: "[Geo] Fix the Storage config parameter in Geo nodes admin page"
merge_request:
author:
type: fixed
class AddStorageConfigurationDigest < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
add_column :geo_node_statuses, :storage_configuration_digest, :binary
end
end
...@@ -367,9 +367,7 @@ module EE ...@@ -367,9 +367,7 @@ module EE
status.storage_shards.present? status.storage_shards.present?
end end
expose :storage_shards_match?, as: :storage_shards_match, if: ->(status, options) do expose :storage_shards_match?, as: :storage_shards_match
::Gitlab::Geo.primary? && status.storage_shards.present?
end
expose :_links do expose :_links do
expose :self do |geo_node_status| expose :self do |geo_node_status|
......
...@@ -133,6 +133,6 @@ describe EE::API::Entities::GeoNodeStatus, :postgresql do ...@@ -133,6 +133,6 @@ describe EE::API::Entities::GeoNodeStatus, :postgresql do
end end
it { is_expected.to have_key(:storage_shards) } it { is_expected.to have_key(:storage_shards) }
it { is_expected.not_to have_key(:storage_shards_match) } it { is_expected.to have_key(:storage_shards_match) }
end end
end end
...@@ -874,30 +874,32 @@ describe GeoNodeStatus, :geo do ...@@ -874,30 +874,32 @@ describe GeoNodeStatus, :geo do
end end
describe '#storage_shards_match?' do describe '#storage_shards_match?' do
before do it 'returns false if no shard data is available for secondary' do
stub_primary_node stub_primary_node
stub_current_geo_node(secondary)
status = create(:geo_node_status, geo_node: secondary, storage_configuration_digest: 'bc11119c101846c20367fff34ce9fffa9b05aab8')
expect(status.storage_shards_match?).to be false
end end
set(:status) { create(:geo_node_status) } it 'returns true even if no shard data is available for secondary' do
let(:data) { GeoNodeStatusSerializer.new.represent(status).as_json } stub_secondary_node
let(:result) { described_class.from_json(data) } stub_current_geo_node(primary)
it 'returns nil if no shard data is available' do status = create(:geo_node_status, geo_node: primary, storage_configuration_digest: 'bc11119c101846c20367fff34ce9fffa9b05aab8')
data.delete('storage_shards')
expect(result.storage_shards_match?).to be nil expect(status.storage_shards_match?).to be true
end end
it 'returns false if the storage shards do not match' do it 'returns false if the storage shards do not match' do
data['storage_shards'].first['name'] = 'broken-shard' stub_primary_node
stub_current_geo_node(secondary)
expect(result.storage_shards_match?).to be false create(:geo_node_status, geo_node: primary, storage_configuration_digest: 'aea7849c10b886c202676ff34ce9fdf0940567b8')
end
it 'returns true if the storage shards match in different order' do status = create(:geo_node_status, geo_node: secondary, storage_configuration_digest: 'bc11119c101846c20367fff34ce9fffa9b05aab8')
status.storage_shards.shuffle!
expect(result.storage_shards_match?).to be true expect(status.storage_shards_match?).to be false
end end
end end
......
require 'spec_helper' require 'spec_helper'
describe StorageShard do describe StorageShard do
describe '.current_shards' do describe '.all' do
it 'returns an array of StorageShard objects' do it 'returns an array of StorageShard objects' do
shards = described_class.all shards = described_class.all
...@@ -9,4 +9,10 @@ describe StorageShard do ...@@ -9,4 +9,10 @@ describe StorageShard do
expect(shards.map(&:name)).to match_array(Settings.repositories.storages.keys) expect(shards.map(&:name)).to match_array(Settings.repositories.storages.keys)
end end
end end
describe '.build_digest' do
it 'returns SHA1 digest for the current configuration' do
expect(described_class.build_digest).to eq('aea7849c10b886c202676ff34ce9fdf0940567b8')
end
end
end end
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