Commit f0f73598 authored by Nick Thomas's avatar Nick Thomas
Browse files

Start tracking shards in the database

parent 9f1a45dc6d30
# frozen_string_literal: true
class Shard < ActiveRecord::Base
# Store shard names from the configuration file in the database. This is not a
# list of active shards - we just want to assign an immutable, unique ID to
# every shard name for easy indexing / referencing.
def self.populate!
return unless table_exists?
# The GitLab config does not change for the lifecycle of the process
in_config = Gitlab.config.repositories.storages.keys.map(&:to_s)
transaction do
in_db = all.pluck(:name)
missing = in_config - in_db
missing.map { |name| by_name(name) }
end
end
def self.by_name(name)
find_or_create_by(name: name)
rescue ActiveRecord::RecordNotUnique
retry
end
end
return if Gitlab::Database.read_only?
Shard.populate!
# frozen_string_literal: true
class AddShardsTable < ActiveRecord::Migration
DOWNTIME = false
def change
create_table :shards do |t|
t.string :name, null: false, index: { unique: true }
end
end
end
......@@ -1931,6 +1931,12 @@
add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree
add_index "services", ["template"], name: "index_services_on_template", using: :btree
create_table "shards", force: :cascade do |t|
t.string "name", null: false
end
add_index "shards", ["name"], name: "index_shards_on_name", unique: true, using: :btree
create_table "site_statistics", force: :cascade do |t|
t.integer "repositories_count", default: 0, null: false
end
......
......@@ -8,6 +8,7 @@
it { is_expected.to belong_to(:group) }
it { is_expected.to belong_to(:namespace) }
it { is_expected.to belong_to(:creator).class_name('User') }
it { is_expected.to belong_to(:pool_repository) }
it { is_expected.to have_many(:users) }
it { is_expected.to have_many(:services) }
it { is_expected.to have_many(:events) }
......
# frozen_string_literals: true
require 'spec_helper'
describe Shard do
describe '.populate!' do
it 'creates shards based on the config file' do
expect(described_class.all).to be_empty
stub_storage_settings(foo: {}, bar: {}, baz: {})
described_class.populate!
expect(described_class.all.map(&:name)).to match_array(%w[default foo bar baz])
end
end
describe '.by_name' do
let(:default_shard) { described_class.find_by(name: 'default') }
before do
described_class.populate!
end
it 'returns an existing shard' do
expect(described_class.by_name('default')).to eq(default_shard)
end
it 'creates a new shard' do
result = described_class.by_name('foo')
expect(result).not_to eq(default_shard)
expect(result.name).to eq('foo')
end
it 'retries if creation races' do
expect(described_class)
.to receive(:find_or_create_by)
.with(name: 'default')
.and_raise(ActiveRecord::RecordNotUnique, 'fail')
.once
expect(described_class)
.to receive(:find_or_create_by)
.with(name: 'default')
.and_call_original
expect(described_class.by_name('default')).to eq(default_shard)
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