Commit be832e63 authored by Nick Thomas's avatar Nick Thomas

Merge branch 'da-repository-verification' into 'master'

Geo - Switch from time-based checking of outdated checksums to the nil-checksum-based approach

Closes #5176 and #5346

See merge request gitlab-org/gitlab-ee!4910
parents 78240f13 1f004e12
...@@ -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: 20180314100728) do ActiveRecord::Schema.define(version: 20180314174825) 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"
...@@ -1900,15 +1900,14 @@ ActiveRecord::Schema.define(version: 20180314100728) do ...@@ -1900,15 +1900,14 @@ ActiveRecord::Schema.define(version: 20180314100728) do
t.integer "project_id", null: false t.integer "project_id", null: false
t.binary "repository_verification_checksum" t.binary "repository_verification_checksum"
t.binary "wiki_verification_checksum" t.binary "wiki_verification_checksum"
t.boolean "last_repository_verification_failed", default: false, null: false
t.boolean "last_wiki_verification_failed", default: false, null: false
t.datetime_with_timezone "last_repository_verification_at"
t.datetime_with_timezone "last_wiki_verification_at"
t.string "last_repository_verification_failure" t.string "last_repository_verification_failure"
t.string "last_wiki_verification_failure" t.string "last_wiki_verification_failure"
end end
add_index "project_repository_states", ["last_repository_verification_failure"], name: "idx_repository_states_on_repository_failure_partial", where: "(last_repository_verification_failure IS NOT NULL)", using: :btree
add_index "project_repository_states", ["last_wiki_verification_failure"], name: "idx_repository_states_on_wiki_failure_partial", where: "(last_wiki_verification_failure IS NOT NULL)", using: :btree
add_index "project_repository_states", ["project_id"], name: "index_project_repository_states_on_project_id", unique: true, using: :btree add_index "project_repository_states", ["project_id"], name: "index_project_repository_states_on_project_id", unique: true, using: :btree
add_index "project_repository_states", ["repository_verification_checksum", "wiki_verification_checksum"], name: "idx_repository_states_on_checksums_partial", where: "((repository_verification_checksum IS NULL) OR (wiki_verification_checksum IS NULL))", using: :btree
create_table "project_statistics", force: :cascade do |t| create_table "project_statistics", force: :cascade do |t|
t.integer "project_id", null: false t.integer "project_id", null: false
......
...@@ -84,12 +84,12 @@ module Geo ...@@ -84,12 +84,12 @@ module Geo
end end
end end
# find all registries that need a repository or wiki verified # Find all registries that need a repository or wiki verification
def find_registries_to_verify def find_registries_to_verify(batch_size:)
if use_legacy_queries? if use_legacy_queries?
legacy_find_registries_to_verify legacy_find_registries_to_verify(batch_size: batch_size)
else else
fdw_find_registries_to_verify fdw_find_registries_to_verify(batch_size: batch_size)
end end
end end
...@@ -151,34 +151,6 @@ module Geo ...@@ -151,34 +151,6 @@ module Geo
end end
end end
def conditions_for_verification(type, use_fdw = true)
last_verification_failed = "last_#{type}_verification_failed".to_sym
verification_checksum = "#{type}_verification_checksum".to_sym
last_verification_at = "last_#{type}_verification_at".to_sym
state_arel = use_fdw ? fdw_repository_state_arel : legacy_repository_state_arel
# primary verification did not fail
primary_verification_not_failed = state_arel[last_verification_failed].eq(false)
# primary checksum is not NULL
primary_has_checksum = state_arel[verification_checksum].not_eq(nil)
# primary was verified later than the secondary verification
primary_recently_verified = state_arel[last_verification_at].gt(registry_arel[last_verification_at])
.or(registry_arel[last_verification_at].eq(nil))
# secondary verification failed and the last verification was over 24.hours.ago
# this allows us to retry any verification failures if they haven't already corrected themselves
secondary_failure_period = registry_arel[last_verification_at].lt(24.hours.ago)
.and(registry_arel[last_verification_failed].eq(true))
primary_verification_not_failed
.and(primary_has_checksum)
.and(primary_recently_verified)
.or(secondary_failure_period)
end
# #
# FDW accessors # FDW accessors
# #
...@@ -213,12 +185,28 @@ module Geo ...@@ -213,12 +185,28 @@ module Geo
.merge(Geo::ProjectRegistry.retry_due) .merge(Geo::ProjectRegistry.retry_due)
end end
# find all registries that need a repository or wiki verified # Find all registries that repository or wiki need verification
# @return [ActiveRecord::Relation<Geo::ProjectRegistry>] list of registries that need verification # @return [ActiveRecord::Relation<Geo::ProjectRegistry>] list of registries that need verification
def fdw_find_registries_to_verify def fdw_find_registries_to_verify(batch_size:)
Geo::ProjectRegistry Geo::ProjectRegistry
.joins("LEFT OUTER JOIN #{fdw_repository_state_table} ON #{fdw_repository_state_table}.project_id = project_registry.project_id") .joins(fdw_inner_join_repository_state)
.where(conditions_for_verification(:repository, true).or(conditions_for_verification(:wiki, true))) .where(
local_registry_table[:repository_verification_checksum].eq(nil).or(
local_registry_table[:wiki_verification_checksum].eq(nil)
)
)
.where(
fdw_repository_state_table[:repository_verification_checksum].not_eq(nil).or(
fdw_repository_state_table[:wiki_verification_checksum].not_eq(nil)
)
).limit(batch_size)
end
def fdw_inner_join_repository_state
local_registry_table
.join(fdw_repository_state_table, Arel::Nodes::InnerJoin)
.on(local_registry_table[:project_id].eq(fdw_repository_state_table[:project_id]))
.join_sources
end end
# #
...@@ -311,44 +299,42 @@ module Geo ...@@ -311,44 +299,42 @@ module Geo
end end
# @return [ActiveRecord::Relation<Geo::ProjectRegistry>] list of registries that need verification # @return [ActiveRecord::Relation<Geo::ProjectRegistry>] list of registries that need verification
def legacy_find_registries_to_verify def legacy_find_registries_to_verify(batch_size:)
registries = Geo::ProjectRegistry registries = Geo::ProjectRegistry.where(
.pluck(:project_id, :last_repository_verification_at, :last_wiki_verification_at, local_registry_table[:repository_verification_checksum].eq(nil).or(
:last_repository_verification_failed, :last_wiki_verification_failed) local_registry_table[:wiki_verification_checksum].eq(nil)
)
).pluck(:project_id)
return Geo::ProjectRegistry.none if registries.empty? return Geo::ProjectRegistry.none if registries.empty?
id_and_values = registries.map do |project_id, repo_at, wiki_at, repo_failed, wiki_failed| id_and_want_to_sync = registries.map { |project_id| "(#{project_id}, #{quote_value(true)})" }
"(#{project_id}, to_timestamp(#{repo_at.to_i}), to_timestamp(#{wiki_at.to_i}),
#{quote_value(repo_failed)}, #{quote_value(wiki_failed)})"
end
joined_relation = ProjectRepositoryState.joins(<<~SQL) joined_relation =
INNER JOIN ProjectRepositoryState.joins(<<~SQL)
(VALUES #{id_and_values.join(',')}) INNER JOIN
project_registry(project_id, last_repository_verification_at, last_wiki_verification_at, (VALUES #{id_and_want_to_sync.join(',')})
last_repository_verification_failed, last_wiki_verification_failed) project_registry(project_id, want_to_sync)
ON #{ProjectRepositoryState.table_name}.project_id = project_registry.project_id ON #{legacy_repository_state_table.name}.project_id = project_registry.project_id
SQL SQL
project_ids = joined_relation project_ids = joined_relation
.where(conditions_for_verification(:repository, false).or(conditions_for_verification(:wiki, false))) .where(
.pluck(:project_id) legacy_repository_state_table[:repository_verification_checksum].not_eq(nil).or(
legacy_repository_state_table[:wiki_verification_checksum].not_eq(nil)
)
).where(
project_registry: { want_to_sync: true }
).limit(batch_size).pluck(:project_id)
::Geo::ProjectRegistry.where(project_id: project_ids) Geo::ProjectRegistry.where(project_id: project_ids)
end end
private def local_registry_table
def registry_arel
Geo::ProjectRegistry.arel_table Geo::ProjectRegistry.arel_table
end end
def fdw_repository_state_arel def legacy_repository_state_table
Geo::Fdw::ProjectRepositoryState.arel_table
end
def legacy_repository_state_arel
::ProjectRepositoryState.arel_table ::ProjectRepositoryState.arel_table
end end
...@@ -357,7 +343,7 @@ module Geo ...@@ -357,7 +343,7 @@ module Geo
end end
def fdw_repository_state_table def fdw_repository_state_table
Geo::Fdw::ProjectRepositoryState.table_name Geo::Fdw::ProjectRepositoryState.arel_table
end end
end end
end end
...@@ -2,20 +2,19 @@ module Geo ...@@ -2,20 +2,19 @@ module Geo
class RepositoryVerificationFinder class RepositoryVerificationFinder
def find_outdated_projects(batch_size:) def find_outdated_projects(batch_size:)
Project.select(:id) Project.select(:id)
.with_route
.joins(:repository_state) .joins(:repository_state)
.where(repository_unverified .where(repository_outdated.or(wiki_outdated))
.or(repository_verification_outdated) .order(last_repository_updated_at_asc)
.or(wiki_unverified)
.or(wiki_verification_outdated))
.order(repository_state_table[:last_repository_verification_at].asc)
.limit(batch_size) .limit(batch_size)
end end
def find_unverified_projects(batch_size:) def find_unverified_projects(batch_size:)
Project.select(:id) Project.select(:id)
.with_route
.joins(left_join_repository_state) .joins(left_join_repository_state)
.where(repository_never_verified) .where(repository_never_verified)
.order(projects_table[:last_repository_updated_at].asc) .order(last_repository_updated_at_asc)
.limit(batch_size) .limit(batch_size)
end end
...@@ -52,26 +51,20 @@ module Geo ...@@ -52,26 +51,20 @@ module Geo
.join_sources .join_sources
end end
def repository_unverified def repository_outdated
repository_state_table[:repository_verification_checksum].eq(nil) repository_state_table[:repository_verification_checksum].eq(nil)
end end
def repository_verification_outdated def wiki_outdated
repository_state_table[:last_repository_verification_at]
.lt(projects_table[:last_repository_updated_at])
end
def wiki_unverified
repository_state_table[:wiki_verification_checksum].eq(nil) repository_state_table[:wiki_verification_checksum].eq(nil)
end end
def wiki_verification_outdated
repository_state_table[:last_wiki_verification_at]
.lt(projects_table[:last_repository_updated_at])
end
def repository_never_verified def repository_never_verified
repository_state_table[:id].eq(nil) repository_state_table[:id].eq(nil)
end end
def last_repository_updated_at_asc
Gitlab::Database.nulls_last_order('projects.last_repository_updated_at', 'ASC')
end
end end
end end
class Geo::ProjectRegistry < Geo::BaseRegistry class Geo::ProjectRegistry < Geo::BaseRegistry
include ::EachBatch include ::EachBatch
include ::IgnorableColumn
ignore_column :last_repository_verification_at
ignore_column :last_repository_verification_failed
ignore_column :last_wiki_verification_at
ignore_column :last_wiki_verification_failed
belongs_to :project belongs_to :project
...@@ -8,8 +14,10 @@ class Geo::ProjectRegistry < Geo::BaseRegistry ...@@ -8,8 +14,10 @@ class Geo::ProjectRegistry < Geo::BaseRegistry
scope :dirty, -> { where(arel_table[:resync_repository].eq(true).or(arel_table[:resync_wiki].eq(true))) } scope :dirty, -> { where(arel_table[:resync_repository].eq(true).or(arel_table[:resync_wiki].eq(true))) }
scope :failed_repos, -> { where(arel_table[:repository_retry_count].gt(0)) } scope :failed_repos, -> { where(arel_table[:repository_retry_count].gt(0)) }
scope :failed_wikis, -> { where(arel_table[:wiki_retry_count].gt(0)) } scope :failed_wikis, -> { where(arel_table[:wiki_retry_count].gt(0)) }
scope :verification_failed_repos, -> { where(arel_table[:last_repository_verification_failed].eq(true)) } scope :verified_repos, -> { where.not(repository_verification_checksum: nil) }
scope :verification_failed_wikis, -> { where(arel_table[:last_wiki_verification_failed].eq(true)) } scope :verified_wikis, -> { where.not(wiki_verification_checksum: nil) }
scope :verification_failed_repos, -> { where.not(last_repository_verification_failure: nil) }
scope :verification_failed_wikis, -> { where.not(last_wiki_verification_failure: nil) }
def self.failed def self.failed
repository_sync_failed = arel_table[:repository_retry_count].gt(0) repository_sync_failed = arel_table[:repository_retry_count].gt(0)
...@@ -19,8 +27,8 @@ class Geo::ProjectRegistry < Geo::BaseRegistry ...@@ -19,8 +27,8 @@ class Geo::ProjectRegistry < Geo::BaseRegistry
end end
def self.verification_failed def self.verification_failed
repository_verification_failed = arel_table[:last_repository_verification_failed].eq(true) repository_verification_failed = arel_table[:last_repository_verification_failure].not_eq(nil)
wiki_verification_failed = arel_table[:last_wiki_verification_failed].eq(true) wiki_verification_failed = arel_table[:last_wiki_verification_failure].not_eq(nil)
where(repository_verification_failed.or(wiki_verification_failed)) where(repository_verification_failed.or(wiki_verification_failed))
end end
...@@ -44,16 +52,6 @@ class Geo::ProjectRegistry < Geo::BaseRegistry ...@@ -44,16 +52,6 @@ class Geo::ProjectRegistry < Geo::BaseRegistry
.where(resync_wiki: false) .where(resync_wiki: false)
end end
def self.verified_repos
where.not(last_repository_verification_at: nil, repository_verification_checksum: nil)
.where(last_repository_verification_failed: false)
end
def self.verified_wikis
where.not(last_wiki_verification_at: nil, wiki_verification_checksum: nil)
.where(last_wiki_verification_failed: false)
end
def repository_sync_due?(scheduled_time) def repository_sync_due?(scheduled_time)
never_synced_repository? || repository_sync_needed?(scheduled_time) never_synced_repository? || repository_sync_needed?(scheduled_time)
end end
...@@ -62,11 +60,6 @@ class Geo::ProjectRegistry < Geo::BaseRegistry ...@@ -62,11 +60,6 @@ class Geo::ProjectRegistry < Geo::BaseRegistry
project.wiki_enabled? && (never_synced_wiki? || wiki_sync_needed?(scheduled_time)) project.wiki_enabled? && (never_synced_wiki? || wiki_sync_needed?(scheduled_time))
end end
delegate :repository_state, to: :project
delegate :repository_verification_checksum, :last_repository_verification_at,
:wiki_verification_checksum, :last_wiki_verification_at,
to: :repository_state, allow_nil: true, prefix: :project
def repository_path(type) def repository_path(type)
repo_path = project.disk_path repo_path = project.disk_path
......
class ProjectRepositoryState < ActiveRecord::Base class ProjectRepositoryState < ActiveRecord::Base
include IgnorableColumn
include ShaAttribute include ShaAttribute
ignore_column :last_repository_verification_at
ignore_column :last_repository_verification_failed
ignore_column :last_wiki_verification_at
ignore_column :last_wiki_verification_failed
sha_attribute :repository_verification_checksum sha_attribute :repository_verification_checksum
sha_attribute :wiki_verification_checksum sha_attribute :wiki_verification_checksum
...@@ -8,34 +14,18 @@ class ProjectRepositoryState < ActiveRecord::Base ...@@ -8,34 +14,18 @@ class ProjectRepositoryState < ActiveRecord::Base
validates :project, presence: true, uniqueness: true validates :project, presence: true, uniqueness: true
scope :verification_failed_repos, -> { where(arel_table[:last_repository_verification_failed].eq(true)) } scope :verification_failed_repos, -> { where.not(last_repository_verification_failure: nil) }
scope :verification_failed_wikis, -> { where(arel_table[:last_wiki_verification_failed].eq(true)) } scope :verification_failed_wikis, -> { where.not(last_wiki_verification_failure: nil) }
scope :verified_repos, -> { where.not(repository_verification_checksum: nil).where(last_repository_verification_failure: nil) }
scope :verified_wikis, -> { where.not(wiki_verification_checksum: nil).where(last_wiki_verification_failure: nil) }
def repository_checksum_outdated?(timestamp) def repository_checksum_outdated?
repository_verification_checksum.nil? || recalculate_repository_checksum?(timestamp) repository_verification_checksum.nil?
end end
def wiki_checksum_outdated?(timestamp) def wiki_checksum_outdated?
return false unless project.wiki_enabled? return false unless project.wiki_enabled?
wiki_verification_checksum.nil? || recalculate_wiki_checksum?(timestamp) wiki_verification_checksum.nil?
end
def self.verified_repos
where.not(repository_verification_checksum: nil)
.where(last_repository_verification_failed: false)
end
def self.verified_wikis
where.not(wiki_verification_checksum: nil)
.where(last_wiki_verification_failed: false)
end
def recalculate_repository_checksum?(timestamp)
last_repository_verification_at.nil? || timestamp > last_repository_verification_at
end
def recalculate_wiki_checksum?(timestamp)
last_wiki_verification_at.nil? || timestamp > last_wiki_verification_at
end end
end end
...@@ -16,8 +16,7 @@ module EE ...@@ -16,8 +16,7 @@ module EE
def process_wiki_repository_update def process_wiki_repository_update
if ::Gitlab::Geo.primary? if ::Gitlab::Geo.primary?
# Create wiki repository updated event on Geo event log ::Geo::RepositoryUpdatedService.new(project, source: ::Geo::RepositoryUpdatedEvent::WIKI).execute
::Geo::RepositoryUpdatedEventStore.new(project, source: ::Geo::RepositoryUpdatedEvent::WIKI).create
end end
end end
end end
......
...@@ -112,11 +112,6 @@ module Geo ...@@ -112,11 +112,6 @@ module Geo
attrs["last_#{type}_synced_at"] = started_at attrs["last_#{type}_synced_at"] = started_at
attrs["#{type}_retry_count"] = retry_count + 1 attrs["#{type}_retry_count"] = retry_count + 1
attrs["#{type}_retry_at"] = next_retry_time(attrs["#{type}_retry_count"]) attrs["#{type}_retry_at"] = next_retry_time(attrs["#{type}_retry_count"])
# indicate that repository verification needs to be done again
attrs["#{type}_verification_checksum"] = nil
attrs["last_#{type}_verification_at"] = nil
attrs["last_#{type}_verification_failure"] = nil
end end
if finished_at if finished_at
...@@ -125,6 +120,10 @@ module Geo ...@@ -125,6 +120,10 @@ module Geo
attrs["#{type}_retry_count"] = nil attrs["#{type}_retry_count"] = nil
attrs["#{type}_retry_at"] = nil attrs["#{type}_retry_at"] = nil
attrs["force_to_redownload_#{type}"] = false attrs["force_to_redownload_#{type}"] = false
# Indicate that repository verification needs to be done again
attrs["#{type}_verification_checksum"] = nil
attrs["last_#{type}_verification_failure"] = nil
end end
registry.update!(attrs) registry.update!(attrs)
......
module Geo
class RepositoryUpdatedService
include ::Gitlab::Geo::ProjectLogHelpers
def initialize(project, params = {})
@project = project
@params = params
@refs = params.fetch(:refs, [])
@changes = params.fetch(:changes, [])
@source = params.fetch(:source, Geo::RepositoryUpdatedEvent::REPOSITORY)
end
def execute
return false unless Gitlab::Geo.primary?
reset_repository_checksum!
create_repository_updated_event!
true
end
private
attr_reader :project, :refs, :changes, :source
delegate :repository_state, to: :project
def create_repository_updated_event!
Geo::RepositoryUpdatedEventStore.new(
project, refs: refs, changes: changes, source: source
).create
end
def reset_repository_checksum!
return if repository_state.nil?
repository_state.update!("#{repository_checksum_column}" => nil)
rescue => e
log_error('Cannot reset repository checksum', e)
raise Gitlab::Git::Checksum::Failure, "Cannot reset repository checksum: #{e}"
end
def repository_checksum_column
"#{Geo::RepositoryUpdatedEvent.sources.key(source)}_verification_checksum"
end
end
end
# rubocop:disable GitlabSecurity/PublicSend
module Geo module Geo
class RepositoryVerifySecondaryService class RepositoryVerifySecondaryService
include Gitlab::Geo::RepositoryVerificationLogHelpers include Gitlab::Geo::RepositoryVerificationLogHelpers
...@@ -19,72 +17,56 @@ module Geo ...@@ -19,72 +17,56 @@ module Geo
verify_checksum verify_checksum
end end
# This is primarily a guard method, to reduce the chance of false failures (which could happen private
# for repositories that change very rapidly)
def should_verify_checksum?
primary_checksum = registry.repository_state.public_send("#{type}_verification_checksum")
secondary_checksum = registry.public_send("#{type}_verification_checksum")
primary_last_verification_at = registry.repository_state.public_send("last_#{type}_verification_at")
secondary_last_verification_at = registry.public_send("last_#{type}_verification_at") || Time.at(0)
secondary_last_successful_sync_at = registry.public_send("last_#{type}_successful_sync_at")
# primary repository was verified (even if checksum is nil).
# note: we allow a nil primary checksum so that we will run through the checksum
# and set the verification date on the secondary. Otherwise, we'll keep revisiting
# this record over and over.
return false if primary_last_verification_at.nil?
# secondary repository checksum does not equal the primary repository checksum attr_reader :registry, :type
return false if secondary_checksum == primary_checksum && !primary_checksum.nil?
# primary was verified later than the secondary verification def should_verify_checksum?
return false if primary_last_verification_at < secondary_last_verification_at return false if resync?
# secondary repository was successfully synced after the last secondary verification primary_checksum.present? && primary_checksum != secondary_checksum
return false if secondary_last_successful_sync_at.nil? || secondary_last_successful_sync_at < secondary_last_verification_at end
true def resync?
registry.public_send("resync_#{type}") # rubocop:disable GitlabSecurity/PublicSend
end end
private def primary_checksum
project.repository_state.public_send("#{type}_verification_checksum") # rubocop:disable GitlabSecurity/PublicSend
end
attr_reader :registry, :type def secondary_checksum
registry.public_send("#{type}_verification_checksum") # rubocop:disable GitlabSecurity/PublicSend
end
def verify_checksum def verify_checksum
checksum = calculate_checksum(project.repository_storage, repository_path) checksum = calculate_checksum(project.repository_storage, repository_path)
if mismatch?(checksum) if mismatch?(checksum)
record_status(error_msg: "#{type.to_s.capitalize} checksum mismatch: #{repository_path}") update_registry!(failure: "#{type.to_s.capitalize} checksum mismatch: #{repository_path}")
else else
record_status(checksum: checksum) update_registry!(checksum: checksum)
end end
rescue ::Gitlab::Git::Repository::NoRepository, ::Gitlab::Git::Checksum::Failure, Timeout::Error => e rescue ::Gitlab::Git::Repository::NoRepository, ::Gitlab::Git::Checksum::Failure, Timeout::Error => e
record_status(error_msg: "Error verifying #{type.to_s.capitalize} checksum: #{repository_path}", exception: e) update_registry!(failure: "Error verifying #{type.to_s.capitalize} checksum: #{repository_path}", exception: e)
end end
def mismatch?(checksum) def mismatch?(checksum)
checksum != registry.public_send("project_#{type}_verification_checksum") primary_checksum != checksum
end end
def calculate_checksum(storage, relative_path) def calculate_checksum(storage, relative_path)
Gitlab::Git::Checksum.new(storage, relative_path).calculate Gitlab::Git::Checksum.new(storage, relative_path).calculate
end end
# note: the `last_#{type}_verification_at` is always set, indicating that was the def update_registry!(checksum: nil, failure: nil, exception: nil, details: {})
# time that we _did_ a verification, success or failure
def record_status(checksum: nil, error_msg: nil, exception: nil, details: {})
attrs = { attrs = {
"#{type}_verification_checksum" => checksum, "#{type}_verification_checksum" => checksum,
"last_#{type}_verification_at" => DateTime.now, "last_#{type}_verification_failure" => failure
"last_#{type}_verification_failure" => nil,
"last_#{type}_verification_failed" => false
} }
if error_msg if failure
attrs["last_#{type}_verification_failed"] = true log_error(failure, exception, type: type, repository_path: repository_path, full_path: path_to_repo)
attrs["last_#{type}_verification_failure"] = error_msg
log_error(error_msg, exception, type: type, repository_path: repository_path, full_path: path_to_repo)
end end
registry.update!(attrs) registry.update!(attrs)
......
...@@ -11,8 +11,9 @@ module EE ...@@ -11,8 +11,9 @@ module EE
def after_project_changes_hooks(post_received, user, refs, changes) def after_project_changes_hooks(post_received, user, refs, changes)
super super
# Generate repository updated event on Geo event log when Geo is enabled if ::Gitlab::Geo.primary?
::Geo::RepositoryUpdatedEventStore.new(post_received.project, refs: refs, changes: changes).create ::Geo::RepositoryUpdatedService.new(post_received.project, refs: refs, changes: changes).execute
end
end end
def process_wiki_changes(post_received) def process_wiki_changes(post_received)
...@@ -20,9 +21,8 @@ module EE ...@@ -20,9 +21,8 @@ module EE
update_wiki_es_indexes(post_received) update_wiki_es_indexes(post_received)
if ::Gitlab::Geo.enabled? if ::Gitlab::Geo.primary?
# Create wiki repository updated event on Geo event log ::Geo::RepositoryUpdatedService.new(post_received.project, source: ::Geo::RepositoryUpdatedEvent::WIKI).execute
::Geo::RepositoryUpdatedEventStore.new(post_received.project, source: ::Geo::RepositoryUpdatedEvent::WIKI).create
end end
end end
......
...@@ -32,7 +32,7 @@ module Geo ...@@ -32,7 +32,7 @@ module Geo
end end
def schedule_job(project_id) def schedule_job(project_id)
job_id = Geo::RepositoryVerification::Primary::SingleWorker.perform_async(project_id, Time.now) job_id = Geo::RepositoryVerification::Primary::SingleWorker.perform_async(project_id)
{ id: project_id, job_id: job_id } if job_id { id: project_id, job_id: job_id } if job_id
end end
......
...@@ -11,15 +11,15 @@ module Geo ...@@ -11,15 +11,15 @@ module Geo
attr_reader :project attr_reader :project
def perform(project_id, scheduled_time) def perform(project_id)
return unless Gitlab::Geo.primary? return unless Gitlab::Geo.primary?
@project = Project.find_by(id: project_id) @project = Project.find_by(id: project_id)
return if project.nil? || project.pending_delete? return if project.nil? || project.pending_delete?
try_obtain_lease do try_obtain_lease do
calculate_repository_checksum if repository_state.repository_checksum_outdated?(scheduled_time) calculate_repository_checksum if repository_state.repository_checksum_outdated?
calculate_wiki_checksum if repository_state.wiki_checksum_outdated?(scheduled_time) calculate_wiki_checksum if repository_state.wiki_checksum_outdated?
end end
end end
...@@ -38,14 +38,12 @@ module Geo ...@@ -38,14 +38,12 @@ module Geo
update_repository_state!(type, checksum: checksum.calculate) update_repository_state!(type, checksum: checksum.calculate)
rescue => e rescue => e
log_error('Error calculating the repository checksum', e, type: type) log_error('Error calculating the repository checksum', e, type: type)
update_repository_state!(type, failed: true, failure: e.message) update_repository_state!(type, failure: e.message)
end end
def update_repository_state!(type, checksum: nil, failed: false, failure: nil) def update_repository_state!(type, checksum: nil, failure: nil)
repository_state.update!( repository_state.update!(
"#{type}_verification_checksum" => checksum, "#{type}_verification_checksum" => checksum,
"last_#{type}_verification_at" => DateTime.now,
"last_#{type}_verification_failed" => failed,
"last_#{type}_verification_failure" => failure "last_#{type}_verification_failure" => failure
) )
end end
......
...@@ -19,7 +19,8 @@ module Geo ...@@ -19,7 +19,8 @@ module Geo
end end
def load_pending_resources def load_pending_resources
finder.find_registries_to_verify.limit(db_retrieve_batch_size).pluck(:id) finder.find_registries_to_verify(batch_size: db_retrieve_batch_size)
.pluck(:id)
end end
def schedule_job(registry_id) def schedule_job(registry_id)
......
---
title: Geo - Switch from time-based checking of outdated checksums to the nil-checksum-based
approach
merge_request:
author:
type: changed
class AddPartialIndexToProjectRegistyVerificationFailureColumns < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
REPOSITORY_INDEX_NAME = 'idx_project_registry_on_repository_failure_partial'
WIKI_INDEX_NAME = 'idx_project_registry_on_wiki_failure_partial'
disable_ddl_transaction!
def up
unless index_exists?(:project_registry, :project_id, name: REPOSITORY_INDEX_NAME)
add_concurrent_index(:project_registry, :project_id, name: REPOSITORY_INDEX_NAME, where: 'last_repository_verification_failure IS NOT NULL')
end
unless index_exists?(:project_registry, :project_id, name: WIKI_INDEX_NAME)
add_concurrent_index(:project_registry, :project_id, name: WIKI_INDEX_NAME, where: 'last_wiki_verification_failure IS NOT NULL')
end
end
def down
if index_exists?(:project_registry, :project_id, name: REPOSITORY_INDEX_NAME)
remove_concurrent_index_by_name(:project_registry, REPOSITORY_INDEX_NAME)
end
if index_exists?(:project_registry, :project_id, name: WIKI_INDEX_NAME)
remove_concurrent_index_by_name(:project_registry, WIKI_INDEX_NAME)
end
end
end
class AddPartialIndexToProjectRegistyChecksumColumns < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
REPOSITORY_INDEX_NAME = 'idx_project_registry_on_repository_checksum_partial'
WIKI_INDEX_NAME = 'idx_project_registry_on_wiki_checksum_partial'
disable_ddl_transaction!
def up
unless index_exists?(:project_registry, :repository_verification_checksum, name: REPOSITORY_INDEX_NAME)
add_concurrent_index(:project_registry, :repository_verification_checksum, name: REPOSITORY_INDEX_NAME, where: 'repository_verification_checksum IS NULL')
end
unless index_exists?(:project_registry, :wiki_verification_checksum, name: WIKI_INDEX_NAME)
add_concurrent_index(:project_registry, :wiki_verification_checksum, name: WIKI_INDEX_NAME, where: 'wiki_verification_checksum IS NULL')
end
end
def down
if index_exists?(:project_registry, :repository_verification_checksum, name: REPOSITORY_INDEX_NAME)
remove_concurrent_index_by_name(:project_registry, REPOSITORY_INDEX_NAME)
end
if index_exists?(:project_registry, :wiki_verification_checksum, name: WIKI_INDEX_NAME)
remove_concurrent_index_by_name(:project_registry, WIKI_INDEX_NAME)
end
end
end
class RemoveLastVerificationFailedColumnsFromGeoProjectRegistry < ActiveRecord::Migration
DOWNTIME = false
def up
remove_column :project_registry, :last_repository_verification_failed
remove_column :project_registry, :last_wiki_verification_failed
end
def down
add_column :project_registry, :last_repository_verification_failed, :boolean, default: false, null: false
add_column :project_registry, :last_wiki_verification_failed, :boolean, default: false, null: false
end
end
class RemoveLastVerificationAtColumnsFromGeoProjectRegistry < ActiveRecord::Migration
DOWNTIME = false
def up
remove_column :project_registry, :last_repository_verification_at
remove_column :project_registry, :last_wiki_verification_at
end
def down
add_column :project_registry, :last_repository_verification_at, :datetime_with_timezone
add_column :project_registry, :last_wiki_verification_at, :datetime_with_timezone
end
end
...@@ -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: 20180201154345) do ActiveRecord::Schema.define(version: 20180320013929) 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"
...@@ -53,21 +53,21 @@ ActiveRecord::Schema.define(version: 20180201154345) do ...@@ -53,21 +53,21 @@ ActiveRecord::Schema.define(version: 20180201154345) do
t.string "last_repository_sync_failure" t.string "last_repository_sync_failure"
t.string "last_wiki_sync_failure" t.string "last_wiki_sync_failure"
t.string "repository_verification_checksum" t.string "repository_verification_checksum"
t.datetime_with_timezone "last_repository_verification_at"
t.boolean "last_repository_verification_failed", default: false, null: false
t.string "last_repository_verification_failure" t.string "last_repository_verification_failure"
t.string "wiki_verification_checksum" t.string "wiki_verification_checksum"
t.datetime_with_timezone "last_wiki_verification_at"
t.boolean "last_wiki_verification_failed", default: false, null: false
t.string "last_wiki_verification_failure" t.string "last_wiki_verification_failure"
end end
add_index "project_registry", ["last_repository_successful_sync_at"], name: "index_project_registry_on_last_repository_successful_sync_at", using: :btree add_index "project_registry", ["last_repository_successful_sync_at"], name: "index_project_registry_on_last_repository_successful_sync_at", using: :btree
add_index "project_registry", ["last_repository_synced_at"], name: "index_project_registry_on_last_repository_synced_at", using: :btree add_index "project_registry", ["last_repository_synced_at"], name: "index_project_registry_on_last_repository_synced_at", using: :btree
add_index "project_registry", ["project_id"], name: "idx_project_registry_on_repository_failure_partial", where: "(last_repository_verification_failure IS NOT NULL)", using: :btree
add_index "project_registry", ["project_id"], name: "idx_project_registry_on_wiki_failure_partial", where: "(last_wiki_verification_failure IS NOT NULL)", using: :btree
add_index "project_registry", ["project_id"], name: "index_project_registry_on_project_id", unique: true, using: :btree add_index "project_registry", ["project_id"], name: "index_project_registry_on_project_id", unique: true, using: :btree
add_index "project_registry", ["repository_retry_at"], name: "index_project_registry_on_repository_retry_at", using: :btree add_index "project_registry", ["repository_retry_at"], name: "index_project_registry_on_repository_retry_at", using: :btree
add_index "project_registry", ["repository_verification_checksum"], name: "idx_project_registry_on_repository_checksum_partial", where: "(repository_verification_checksum IS NULL)", using: :btree
add_index "project_registry", ["resync_repository"], name: "index_project_registry_on_resync_repository", using: :btree add_index "project_registry", ["resync_repository"], name: "index_project_registry_on_resync_repository", using: :btree
add_index "project_registry", ["resync_wiki"], name: "index_project_registry_on_resync_wiki", using: :btree add_index "project_registry", ["resync_wiki"], name: "index_project_registry_on_resync_wiki", using: :btree
add_index "project_registry", ["wiki_retry_at"], name: "index_project_registry_on_wiki_retry_at", using: :btree add_index "project_registry", ["wiki_retry_at"], name: "index_project_registry_on_wiki_retry_at", using: :btree
add_index "project_registry", ["wiki_verification_checksum"], name: "idx_project_registry_on_wiki_checksum_partial", where: "(wiki_verification_checksum IS NULL)", using: :btree
end end
class AddPartialIndexToProjectRepositoryStatesChecksumColumns < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
INDEX_NAME = 'idx_repository_states_on_checksums_partial'
disable_ddl_transaction!
def up
unless index_exists?(:project_repository_states, [:repository_verification_checksum, :wiki_verification_checksum], name: INDEX_NAME)
add_concurrent_index(:project_repository_states,
[:repository_verification_checksum, :wiki_verification_checksum],
name: INDEX_NAME,
length: Gitlab::Database.mysql? ? 20 : nil,
where: 'repository_verification_checksum IS NULL OR wiki_verification_checksum IS NULL'
)
end
end
def down
if index_exists?(:project_repository_states, [:repository_verification_checksum, :wiki_verification_checksum], name: INDEX_NAME)
remove_concurrent_index_by_name(:project_repository_states, INDEX_NAME)
end
end
end
class AddPartialIndexToProjectRepositoryStatesVerificationColumns < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
REPOSITORY_INDEX_NAME = 'idx_repository_states_on_repository_failure_partial'
WIKI_INDEX_NAME = 'idx_repository_states_on_wiki_failure_partial'
disable_ddl_transaction!
def up
unless index_exists?(:project_repository_states, :last_repository_verification_failure, name: REPOSITORY_INDEX_NAME)
add_concurrent_index(:project_repository_states, :last_repository_verification_failure, name: REPOSITORY_INDEX_NAME, length: Gitlab::Database.mysql? ? 20 : nil, where: 'last_repository_verification_failure IS NOT NULL')
end
unless index_exists?(:project_repository_states, :last_wiki_verification_failure, name: WIKI_INDEX_NAME)
add_concurrent_index(:project_repository_states, :last_wiki_verification_failure, name: WIKI_INDEX_NAME, length: Gitlab::Database.mysql? ? 20 : nil, where: 'last_wiki_verification_failure IS NOT NULL')
end
end
def down
if index_exists?(:project_repository_states, :last_repository_verification_failure, name: REPOSITORY_INDEX_NAME)
remove_concurrent_index_by_name(:project_repository_states, REPOSITORY_INDEX_NAME)
end
if index_exists?(:project_repository_states, :last_wiki_verification_failure, name: WIKI_INDEX_NAME)
remove_concurrent_index_by_name(:project_repository_states, WIKI_INDEX_NAME)
end
end
end
class RemoveLastVericationAtColumnsFromProjectRepositoryStates < ActiveRecord::Migration
DOWNTIME = false
def up
remove_column :project_repository_states, :last_repository_verification_at
remove_column :project_repository_states, :last_wiki_verification_at
end
def down
add_column :project_repository_states, :last_repository_verification_at, :datetime_with_timezone
add_column :project_repository_states, :last_wiki_verification_at, :datetime_with_timezone
end
end
class RemoveLastVericationFailedColumnsFromProjectRepositoryStates < ActiveRecord::Migration
DOWNTIME = false
def up
remove_column :project_repository_states, :last_repository_verification_failed
remove_column :project_repository_states, :last_wiki_verification_failed
end
def down
add_column :project_repository_states, :last_repository_verification_failed, :boolean
add_column :project_repository_states, :last_wiki_verification_failed, :boolean
end
end
...@@ -149,11 +149,25 @@ module Gitlab ...@@ -149,11 +149,25 @@ module Gitlab
{ {
database_config: YAML.load_file(GEO_DATABASE_CONFIG), database_config: YAML.load_file(GEO_DATABASE_CONFIG),
db_dir: GEO_DB_DIR, db_dir: GEO_DB_DIR,
migrations_paths: [Rails.root.join(GEO_DB_DIR, 'migrate')], migrations_paths: geo_migrations_paths,
seed_loader: SeedLoader.new seed_loader: SeedLoader.new
} }
end end
def geo_migrations_paths
migrations_paths = [geo_migrate_path]
migrations_paths << geo_post_migration_path unless ENV['SKIP_POST_DEPLOYMENT_MIGRATIONS']
migrations_paths
end
def geo_migrate_path
Rails.root.join(GEO_DB_DIR, 'migrate')
end
def geo_post_migration_path
Rails.root.join(GEO_DB_DIR, 'post_migrate')
end
def with_geo_db def with_geo_db
abort_if_no_geo_config! abort_if_no_geo_config!
......
...@@ -35,6 +35,11 @@ module Gitlab ...@@ -35,6 +35,11 @@ module Gitlab
@db_migrate_path ||= File.join(Rails.root, 'ee', 'db', 'geo', 'migrate') @db_migrate_path ||= File.join(Rails.root, 'ee', 'db', 'geo', 'migrate')
end end
def self.db_post_migrate_path
# Lazy initialisation so Rails.root will be defined
@db_post_migrate_path ||= File.join(Rails.root, 'ee', 'db', 'geo', 'post_migrate')
end
def self.get_database_version def self.get_database_version
if defined?(ActiveRecord) if defined?(ActiveRecord)
connection = ::Geo::BaseRegistry.connection connection = ::Geo::BaseRegistry.connection
...@@ -51,7 +56,7 @@ module Gitlab ...@@ -51,7 +56,7 @@ module Gitlab
def self.get_migration_version def self.get_migration_version
latest_migration = nil latest_migration = nil
Dir[File.join(self.db_migrate_path, "[0-9]*_*.rb")].each do |f| Dir[File.join(self.db_migrate_path, "[0-9]*_*.rb"), File.join(self.db_post_migrate_path, "[0-9]*_*.rb")].each do |f|
timestamp = f.scan(/0*([0-9]+)_[_.a-zA-Z0-9]*.rb/).first.first rescue -1 timestamp = f.scan(/0*([0-9]+)_[_.a-zA-Z0-9]*.rb/).first.first rescue -1
if latest_migration.nil? || timestamp.to_i > latest_migration.to_i if latest_migration.nil? || timestamp.to_i > latest_migration.to_i
......
...@@ -103,7 +103,8 @@ module Gitlab ...@@ -103,7 +103,8 @@ module Gitlab
end end
def handle_repository_updated_event(event, created_at) def handle_repository_updated_event(event, created_at)
registry = find_or_initialize_registry(event.project_id, "resync_#{event.source}" => true) registry = find_or_initialize_registry(event.project_id,
"resync_#{event.source}" => true, "#{event.source}_verification_checksum" => nil)
logger.event_info( logger.event_info(
created_at, created_at,
......
...@@ -74,29 +74,33 @@ FactoryBot.define do ...@@ -74,29 +74,33 @@ FactoryBot.define do
end end
trait :repository_verified do trait :repository_verified do
repository_verification_checksum 'f079a831cab27bcda7d81cd9b48296d0c3dd92ee' repository_verification_checksum 'f079a831cab27bcda7d81cd9b48296d0c3dd92ee'
last_repository_verification_failed false last_repository_verification_failure nil
last_repository_verification_at { 5.days.ago }
end end
trait :wiki_verified do trait :repository_verification_failed do
wiki_verification_checksum 'e079a831cab27bcda7d81cd9b48296d0c3dd92ef' repository_verification_checksum nil
last_wiki_verification_failed false last_repository_verification_failure 'Repository checksum did not match'
last_wiki_verification_at { 5.days.ago }
end end
trait :repository_verification_failed do trait :repository_verification_outdated do
repository_verification_checksum nil repository_verification_checksum nil
last_repository_verification_at { 5.days.ago } last_repository_verification_failure nil
last_repository_verification_failed true end
last_repository_verification_failure 'Repository checksum did not match'
trait :wiki_verified do
wiki_verification_checksum 'e079a831cab27bcda7d81cd9b48296d0c3dd92ef'
last_wiki_verification_failure nil
end end
trait :wiki_verification_failed do trait :wiki_verification_failed do
wiki_verification_checksum nil wiki_verification_checksum nil
last_wiki_verification_at { 5.days.ago } last_wiki_verification_failure 'Wiki checksum did not match'
last_wiki_verification_failed true end
last_wiki_verification_failure 'Wiki checksum did not match'
trait :wiki_verification_outdated do
wiki_verification_checksum nil
last_wiki_verification_failure nil
end end
end end
end end
...@@ -2,28 +2,34 @@ FactoryBot.define do ...@@ -2,28 +2,34 @@ FactoryBot.define do
factory :repository_state, class: 'ProjectRepositoryState' do factory :repository_state, class: 'ProjectRepositoryState' do
project project
trait :repository_failed do
repository_verification_checksum nil
last_repository_verification_failure 'Could not calculate the checksum'
end
trait :repository_outdated do trait :repository_outdated do
repository_verification_checksum 'f079a831cab27bcda7d81cd9b48296d0c3dd92ee' repository_verification_checksum nil
last_repository_verification_at { 5.days.ago } last_repository_verification_failure false
last_repository_verification_failed false
end end
trait :repository_verified do trait :repository_verified do
repository_verification_checksum 'f079a831cab27bcda7d81cd9b48296d0c3dd92ee' repository_verification_checksum 'f079a831cab27bcda7d81cd9b48296d0c3dd92ee'
last_repository_verification_failed false last_repository_verification_failure false
last_repository_verification_at { Time.now } end
trait :wiki_failed do
wiki_verification_checksum nil
last_wiki_verification_failure 'Could not calculate the checksum'
end end
trait :wiki_outdated do trait :wiki_outdated do
repository_verification_checksum 'f079a831cab27bcda7d81cd9b48296d0c3dd92ee' wiki_verification_checksum nil
last_repository_verification_at { 5.days.ago } last_wiki_verification_failure nil
last_repository_verification_failed false
end end
trait :wiki_verified do trait :wiki_verified do
wiki_verification_checksum 'e079a831cab27bcda7d81cd9b48296d0c3dd92ef' wiki_verification_checksum 'e079a831cab27bcda7d81cd9b48296d0c3dd92ef'
last_wiki_verification_failed false last_wiki_verification_failure nil
last_wiki_verification_at { Time.now }
end end
end end
end end
...@@ -31,16 +31,6 @@ describe Geo::RepositoryVerificationFinder, :postgresql do ...@@ -31,16 +31,6 @@ describe Geo::RepositoryVerificationFinder, :postgresql do
expect(subject.find_outdated_projects(batch_size: 10)) expect(subject.find_outdated_projects(batch_size: 10))
.to match_array(project) .to match_array(project)
end end
it 'returns verified projects that repositories have changed' do
repository_outdated = create(:repository_state, :repository_outdated).project
repository_outdated.update_column(:last_repository_updated_at, 6.hours.ago)
wiki_outdated = create(:repository_state, :wiki_outdated).project
wiki_outdated.update_column(:last_repository_updated_at, 48.hours.ago)
expect(subject.find_outdated_projects(batch_size: 10))
.to match_array([repository_outdated, wiki_outdated])
end
end end
describe '#find_unverified_projects' do describe '#find_unverified_projects' do
......
...@@ -111,22 +111,44 @@ describe Gitlab::Geo::LogCursor::Daemon, :postgresql, :clean_gitlab_redis_shared ...@@ -111,22 +111,44 @@ describe Gitlab::Geo::LogCursor::Daemon, :postgresql, :clean_gitlab_redis_shared
expect { daemon.run_once! }.to change(Geo::ProjectRegistry, :count).by(1) expect { daemon.run_once! }.to change(Geo::ProjectRegistry, :count).by(1)
end end
it 'sets resync_repository to true if event source is repository' do context 'when event source is repository' do
repository_updated_event.update!(source: Geo::RepositoryUpdatedEvent::REPOSITORY) let!(:registry) { create(:geo_project_registry, :synced, :repository_verified, project: repository_updated_event.project) }
registry = create(:geo_project_registry, :synced, project: repository_updated_event.project)
daemon.run_once! before do
repository_updated_event.update!(source: Geo::RepositoryUpdatedEvent::REPOSITORY)
end
it 'sets resync_repository to true' do
daemon.run_once!
expect(registry.reload.resync_repository).to be true expect(registry.reload.resync_repository).to be true
end
it 'resets the repository verification checksum' do
daemon.run_once!
expect(registry.reload).to have_attributes(resync_repository: true, repository_verification_checksum: nil)
end
end end
it 'sets resync_wiki to true if event source is wiki' do context 'when event source is wiki' do
repository_updated_event.update!(source: Geo::RepositoryUpdatedEvent::WIKI) let!(:registry) { create(:geo_project_registry, :synced, :wiki_verified, project: repository_updated_event.project) }
registry = create(:geo_project_registry, :synced, project: repository_updated_event.project)
daemon.run_once! before do
repository_updated_event.update!(source: Geo::RepositoryUpdatedEvent::WIKI)
end
it 'sets resync_wiki to true' do
daemon.run_once!
expect(registry.reload.resync_wiki).to be true expect(registry.reload.resync_wiki).to be true
end
it 'resets the wiki verification checksum' do
daemon.run_once!
expect(registry.reload).to have_attributes(resync_wiki: true, wiki_verification_checksum: nil)
end
end end
it 'performs Geo::ProjectSyncWorker' do it 'performs Geo::ProjectSyncWorker' do
......
...@@ -18,59 +18,39 @@ describe ProjectRepositoryState do ...@@ -18,59 +18,39 @@ describe ProjectRepositoryState do
end end
describe '#repository_checksum_outdated?' do describe '#repository_checksum_outdated?' do
where(:repository_verification_checksum, :last_repository_verification_at, :expected) do it 'returns true when repository_verification_checksum is nil' do
now = Time.now repository_state.repository_verification_checksum = nil
past = now - 1.year
future = now + 1.year
nil | nil | true expect(repository_state.repository_checksum_outdated?).to eq true
'123' | nil | true
'123' | past | true
'123' | now | true
'123' | future | false
end end
with_them do it 'returns false when repository_verification_checksum is not nil' do
before do repository_state.repository_verification_checksum = '123'
repository_state.update!(repository_verification_checksum: repository_verification_checksum, last_repository_verification_at: last_repository_verification_at)
end
subject { repository_state.repository_checksum_outdated?(Time.now) }
it { is_expected.to eq(expected) } expect(repository_state.repository_checksum_outdated?).to eq false
end end
end end
describe '#wiki_checksum_outdated?' do describe '#wiki_checksum_outdated?' do
where(:wiki_verification_checksum, :last_wiki_verification_at, :expected) do context 'wiki enabled' do
now = Time.now it 'returns true when wiki_verification_checksum is nil' do
past = now - 1.year repository_state.wiki_verification_checksum = nil
future = now + 1.year
nil | nil | true expect(repository_state.wiki_checksum_outdated?).to eq true
'123' | nil | true
'123' | past | true
'123' | now | true
'123' | future | false
end
with_them do
before do
repository_state.update!(wiki_verification_checksum: wiki_verification_checksum, last_wiki_verification_at: last_wiki_verification_at)
end end
subject { repository_state.wiki_checksum_outdated?(Time.now) } it 'returns false when wiki_verification_checksum is not nil' do
repository_state.wiki_verification_checksum = '123'
context 'wiki enabled' do expect(repository_state.wiki_checksum_outdated?).to eq false
it { is_expected.to eq(expected) }
end end
end
context 'wiki disabled' do context 'wiki disabled' do
before do it 'returns false' do
project.update!(wiki_enabled: false) project.update!(wiki_enabled: false)
end
it { is_expected.to be_falsy } expect(repository_state.wiki_checksum_outdated?).to eq false
end end
end end
end end
......
...@@ -160,6 +160,18 @@ describe Geo::RepositorySyncService do ...@@ -160,6 +160,18 @@ describe Geo::RepositorySyncService do
expect(registry.last_repository_successful_sync_at).not_to be_nil expect(registry.last_repository_successful_sync_at).not_to be_nil
end end
it 'resets the repository_verification_checksum' do
subject.execute
expect(registry.repository_verification_checksum).to be_nil
end
it 'resets the last_repository_verification_failure' do
subject.execute
expect(registry.last_repository_verification_failure).to be_nil
end
it 'logs success with timings' do it 'logs success with timings' do
allow(Gitlab::Geo::Logger).to receive(:info).and_call_original allow(Gitlab::Geo::Logger).to receive(:info).and_call_original
expect(Gitlab::Geo::Logger).to receive(:info).with(hash_including(:message, :update_delay_s, :download_time_s)).and_call_original expect(Gitlab::Geo::Logger).to receive(:info).with(hash_including(:message, :update_delay_s, :download_time_s)).and_call_original
......
require 'spec_helper'
describe Geo::RepositoryUpdatedService do
include ::EE::GeoHelpers
set(:project) { create(:project) }
set(:primary) { create(:geo_node, :primary) }
set(:secondary) { create(:geo_node) }
set(:repository_state) { create(:repository_state, :repository_verified, :wiki_verified, project: project) }
before do
stub_current_geo_node(primary)
end
describe '#execute' do
subject { described_class.new(project, source: source) }
shared_examples 'repository being updated' do
context 'when not running on a primary node' do
before do
allow(Gitlab::Geo).to receive(:primary?) { false }
end
it 'does not create a repository updated event' do
expect { subject.execute }.not_to change(Geo::RepositoryUpdatedEvent, :count)
end
it 'does not reset the repository verification checksum' do
expect { subject.execute }.not_to change(repository_state.reload, "#{method_prefix}_verification_checksum")
end
end
context 'when running on a primary node' do
it 'creates a repository updated event' do
expect { subject.execute }.to change(Geo::RepositoryUpdatedEvent, :count).by(1)
end
it 'resets the repository verification checksum' do
expect { subject.execute }.to change { repository_state.reload.public_send("#{method_prefix}_verification_checksum") }.to(nil)
end
it 'does not raise an error when project have never been verified' do
expect { described_class.new(create(:project)) }.not_to raise_error
end
it 'raises a Gitlab::Git::Checksum error when an error occurs' do
allow(subject.repository_state).to receive(:update!)
.with("#{method_prefix}_verification_checksum" => nil)
.and_raise(ActiveRecord::RecordInvalid.new(repository_state))
expect { subject.execute }.to raise_error Gitlab::Git::Checksum::Failure, /Cannot reset repository checksum/
end
end
end
context 'when repository is being updated' do
include_examples 'repository being updated' do
let(:source) { Geo::RepositoryUpdatedEvent::REPOSITORY }
let(:method_prefix) { 'repository' }
end
end
context 'when wiki is being updated' do
include_examples 'repository being updated' do
let(:source) { Geo::RepositoryUpdatedEvent::WIKI }
let(:method_prefix) { 'wiki' }
end
end
end
end
...@@ -3,159 +3,80 @@ require 'spec_helper' ...@@ -3,159 +3,80 @@ require 'spec_helper'
describe Geo::RepositoryVerifySecondaryService, :geo do describe Geo::RepositoryVerifySecondaryService, :geo do
include ::EE::GeoHelpers include ::EE::GeoHelpers
let(:primary) { create(:geo_node, :primary) }
let(:secondary) { create(:geo_node) } let(:secondary) { create(:geo_node) }
before do before do
stub_current_geo_node(secondary) stub_current_geo_node(secondary)
end end
describe '#execute' do shared_examples 'verify checksums for repositories/wikis' do |type|
let(:repository_state) { create(:repository_state, project: create(:project, :repository))} let(:checksum) { instance_double('Gitlab::Git::Checksum') }
let(:registry) do let(:storage) { project.repository_storage }
registry = create(:geo_project_registry, project: repository_state.project) let(:relative_path) { registry.repository_path(type) }
registry.project.last_repository_updated_at = 7.hours.ago
registry.project.repository_state.last_repository_verification_at = 5.hours.ago
registry.last_repository_successful_sync_at = 5.hours.ago
registry.project.repository_state.repository_verification_checksum = 'my_checksum'
registry
end
let(:service) { described_class.new(registry, :repository) }
it 'only works on the secondary' do
stub_current_geo_node(primary)
expect(service).not_to receive(:log_info) subject(:service) { described_class.new(registry, type) }
service.execute
end
it 'sets checksum when the checksum matches' do it 'does not calculate the checksum when not running on a secondary' do
allow(service).to receive(:calculate_checksum).and_return('my_checksum') allow(Gitlab::Geo).to receive(:secondary?) { false }
expect(service).to receive(:record_status).once.with(checksum: 'my_checksum') expect(Gitlab::Git::Checksum).not_to receive(:new).with(storage, relative_path)
service.execute service.execute
end end
it 'sets failure message when the checksum does not match' do it 'does not verify the checksum if resync is needed' do
allow(service).to receive(:calculate_checksum).and_return('not_my_checksum') registry.assign_attributes("resync_#{type}" => true)
expect(service).to receive(:record_status).once.with(error_msg: start_with('Repository checksum mismatch')) expect(Gitlab::Git::Checksum).not_to receive(:new).with(storage, relative_path)
service.execute service.execute
end end
end
shared_examples 'should_verify_checksum? for repositories/wikis' do |type|
let(:repository_state) { create(:repository_state, project: create(:project, :repository))}
let(:registry) do
registry = create(:geo_project_registry, project: repository_state.project)
registry.project.last_repository_updated_at = 7.hours.ago
registry.project.repository_state.public_send("last_#{type}_verification_at=", 5.hours.ago)
registry.public_send("last_#{type}_successful_sync_at=", 5.hours.ago)
registry.project.repository_state.public_send("#{type}_verification_checksum=", 'my_checksum')
registry
end
let(:service) { described_class.new(registry, type) }
it 'verifies the repository' do
expect(service.should_verify_checksum?).to be_truthy
end
it 'does not verify if primary was never verified' do
registry.project.repository_state.public_send("last_#{type}_verification_at=", nil)
expect(service.should_verify_checksum?).to be_falsy
end
it 'does not verify if the checksums already match' do
registry.project.repository_state.public_send("#{type}_verification_checksum=", 'my_checksum')
registry.public_send("#{type}_verification_checksum=", 'my_checksum')
expect(service.should_verify_checksum?).to be_falsy it 'does not verify the checksum if primary was never verified' do
end repository_state.assign_attributes("#{type}_verification_checksum" => nil)
it 'does not verify if the primary was verified before the secondary' do expect(Gitlab::Git::Checksum).not_to receive(:new).with(storage, relative_path)
registry.project.repository_state.public_send("last_#{type}_verification_at=", 50.minutes.ago)
registry.public_send("last_#{type}_verification_at=", 30.minutes.ago)
expect(service.should_verify_checksum?).to be_falsy service.execute
end end
it 'does verify if the secondary was never verified' do it 'does not verify the checksum if the checksums already match' do
registry.public_send("last_#{type}_verification_at=", nil) repository_state.assign_attributes("#{type}_verification_checksum" => 'my_checksum')
registry.assign_attributes("#{type}_verification_checksum" => 'my_checksum')
expect(service.should_verify_checksum?).to be_truthy
end
it 'does not verify if never synced' do expect(Gitlab::Git::Checksum).not_to receive(:new).with(storage, relative_path)
registry.public_send("last_#{type}_successful_sync_at=", nil)
expect(service.should_verify_checksum?).to be_falsy service.execute
end end
it 'does not verify if the secondary synced before the last secondary verification' do it 'sets checksum when the checksum matches' do
registry.public_send("last_#{type}_verification_at=", 50.minutes.ago) expect(Gitlab::Git::Checksum).to receive(:new).with(storage, relative_path) { checksum }
registry.public_send("last_#{type}_successful_sync_at=", 30.minutes.ago) expect(checksum).to receive(:calculate).and_return('my_checksum')
expect(service.should_verify_checksum?).to be_falsy expect { service.execute }.to change(registry, "#{type}_verification_checksum")
.from(nil).to('my_checksum')
end end
it 'has been at least 6 hours since the primary repository was updated' do it 'keeps track of failure when the checksum mismatch' do
registry.project.last_repository_updated_at = 7.hours.ago expect(Gitlab::Git::Checksum).to receive(:new).with(storage, relative_path) { checksum }
expect(checksum).to receive(:calculate).and_return('other_checksum')
expect(service.should_verify_checksum?).to be_truthy expect { service.execute }.to change(registry, "last_#{type}_verification_failure")
.from(nil).to(/#{Regexp.quote(type.to_s.capitalize)} checksum mismatch/)
end end
end end
describe '#should_verify_checksum?' do describe '#execute' do
let(:project) { create(:project, :repository, :wiki_repo) }
let!(:repository_state) { create(:repository_state, project: project, repository_verification_checksum: 'my_checksum', wiki_verification_checksum: 'my_checksum') }
let(:registry) { create(:geo_project_registry, :synced, project: project) }
context 'repository' do context 'repository' do
include_examples 'should_verify_checksum? for repositories/wikis', :repository include_examples 'verify checksums for repositories/wikis', :repository
end end
context 'wiki' do context 'wiki' do
include_examples 'should_verify_checksum? for repositories/wikis', :wiki include_examples 'verify checksums for repositories/wikis', :wiki
end
end
shared_examples 'record_status for repositories/wikis' do |type|
it 'records a successful verification' do
service.send(:record_status, checksum: 'my_checksum')
registry.reload
expect(registry.public_send("#{type}_verification_checksum")).to eq 'my_checksum'
expect(registry.public_send("last_#{type}_verification_at")).not_to be_nil
expect(registry.public_send("last_#{type}_verification_failure")).to be_nil
expect(registry.public_send("last_#{type}_verification_failed")).to be_falsey
end
it 'records a failure' do
service.send(:record_status, error_msg: 'Repository checksum did not match')
registry.reload
expect(registry.public_send("#{type}_verification_checksum")).to be_nil
expect(registry.public_send("last_#{type}_verification_at")).not_to be_nil
expect(registry.public_send("last_#{type}_verification_failure")).to eq 'Repository checksum did not match'
expect(registry.public_send("last_#{type}_verification_failed")).to be_truthy
end
end
describe '#record_status' do
let(:registry) { create(:geo_project_registry) }
context 'for a repository' do
let(:service) { described_class.new(registry, :repository) }
include_examples 'record_status for repositories/wikis', :repository
end
context 'for a wiki' do
let(:service) { described_class.new(registry, :wiki) }
include_examples 'record_status for repositories/wikis', :wiki
end end
end end
end end
...@@ -139,6 +139,18 @@ RSpec.describe Geo::WikiSyncService do ...@@ -139,6 +139,18 @@ RSpec.describe Geo::WikiSyncService do
expect(registry.last_wiki_successful_sync_at).not_to be_nil expect(registry.last_wiki_successful_sync_at).not_to be_nil
end end
it 'resets the wiki_verification_checksum' do
subject.execute
expect(registry.wiki_verification_checksum).to be_nil
end
it 'resets the last_wiki_verification_failure' do
subject.execute
expect(registry.last_wiki_verification_failure).to be_nil
end
it 'logs success with timings' do it 'logs success with timings' do
allow(Gitlab::Geo::Logger).to receive(:info).and_call_original allow(Gitlab::Geo::Logger).to receive(:info).and_call_original
expect(Gitlab::Geo::Logger).to receive(:info).with(hash_including(:message, :update_delay_s, :download_time_s)).and_call_original expect(Gitlab::Geo::Logger).to receive(:info).with(hash_including(:message, :update_delay_s, :download_time_s)).and_call_original
......
...@@ -19,17 +19,22 @@ describe WikiPages::CreateService do ...@@ -19,17 +19,22 @@ describe WikiPages::CreateService do
end end
describe '#execute' do describe '#execute' do
context 'when running on a Geo primary node' do it 'calls Geo::RepositoryUpdatedService when running on a Geo primary node' do
before do allow(Gitlab::Geo).to receive(:primary?) { true }
allow(Gitlab::Geo).to receive(:primary?) { true }
end
it 'triggers Geo::RepositoryUpdatedEventStore when Geo is enabled' do repository_updated_service = instance_double('::Geo::RepositoryUpdatedService')
expect(::Geo::RepositoryUpdatedEventStore).to receive(:new).with(instance_of(Project), source: Geo::RepositoryUpdatedEvent::WIKI).and_call_original expect(::Geo::RepositoryUpdatedService).to receive(:new).with(project, source: Geo::RepositoryUpdatedEvent::WIKI) { repository_updated_service }
expect_any_instance_of(::Geo::RepositoryUpdatedEventStore).to receive(:create) expect(repository_updated_service).to receive(:execute)
service.execute service.execute
end end
it 'does not call Geo::RepositoryUpdatedService when not running on a Geo primary node' do
allow(Gitlab::Geo).to receive(:primary?) { false }
expect(::Geo::RepositoryUpdatedService).not_to receive(:new).with(project, source: Geo::RepositoryUpdatedEvent::WIKI)
service.execute
end end
end end
end end
...@@ -12,17 +12,22 @@ describe WikiPages::DestroyService do ...@@ -12,17 +12,22 @@ describe WikiPages::DestroyService do
end end
describe '#execute' do describe '#execute' do
context 'when running on a Geo primary node' do it 'calls Geo::RepositoryUpdatedService when running on a Geo primary node' do
before do allow(Gitlab::Geo).to receive(:primary?) { true }
allow(Gitlab::Geo).to receive(:primary?) { true }
end
it 'triggers Geo::RepositoryUpdatedEventStore when Geo is enabled' do repository_updated_service = instance_double('::Geo::RepositoryUpdatedService')
expect(::Geo::RepositoryUpdatedEventStore).to receive(:new).with(instance_of(Project), source: ::Geo::RepositoryUpdatedEvent::WIKI).and_call_original expect(::Geo::RepositoryUpdatedService).to receive(:new).with(project, source: Geo::RepositoryUpdatedEvent::WIKI) { repository_updated_service }
expect_any_instance_of(::Geo::RepositoryUpdatedEventStore).to receive(:create) expect(repository_updated_service).to receive(:execute)
service.execute(page) service.execute(page)
end end
it 'does not call Geo::RepositoryUpdatedService when not running on a Geo primary node' do
allow(Gitlab::Geo).to receive(:primary?) { false }
expect(::Geo::RepositoryUpdatedService).not_to receive(:new).with(project, source: Geo::RepositoryUpdatedEvent::WIKI)
service.execute(page)
end end
end end
end end
...@@ -20,17 +20,22 @@ describe WikiPages::UpdateService do ...@@ -20,17 +20,22 @@ describe WikiPages::UpdateService do
end end
describe '#execute' do describe '#execute' do
context 'when running on a Geo primary node' do it 'calls Geo::RepositoryUpdatedService when running on a Geo primary node' do
before do allow(Gitlab::Geo).to receive(:primary?) { true }
allow(Gitlab::Geo).to receive(:primary?) { true }
end
it 'triggers Geo::RepositoryUpdatedEventStore when Geo is enabled' do repository_updated_service = instance_double('::Geo::RepositoryUpdatedService')
expect(::Geo::RepositoryUpdatedEventStore).to receive(:new).with(instance_of(Project), source: Geo::RepositoryUpdatedEvent::WIKI).and_call_original expect(::Geo::RepositoryUpdatedService).to receive(:new).with(project, source: Geo::RepositoryUpdatedEvent::WIKI) { repository_updated_service }
expect_any_instance_of(::Geo::RepositoryUpdatedEventStore).to receive(:create) expect(repository_updated_service).to receive(:execute)
service.execute(page) service.execute(page)
end end
it 'does not call Geo::RepositoryUpdatedService when not running on a Geo primary node' do
allow(Gitlab::Geo).to receive(:primary?) { false }
expect(::Geo::RepositoryUpdatedService).not_to receive(:new).with(project, source: Geo::RepositoryUpdatedEvent::WIKI)
service.execute(page)
end end
end end
end end
...@@ -37,11 +37,11 @@ describe Geo::RepositoryVerification::Primary::ShardWorker, :postgresql, :clean_ ...@@ -37,11 +37,11 @@ describe Geo::RepositoryVerification::Primary::ShardWorker, :postgresql, :clean_
create(:repository_state, :wiki_outdated, project: wiki_outdated) create(:repository_state, :wiki_outdated, project: wiki_outdated)
expect(Geo::RepositoryVerification::Primary::SingleWorker) expect(Geo::RepositoryVerification::Primary::SingleWorker)
.not_to receive(:perform_async).with(verified_project.id, instance_of(Time)) .not_to receive(:perform_async).with(verified_project.id)
expect(Geo::RepositoryVerification::Primary::SingleWorker) expect(Geo::RepositoryVerification::Primary::SingleWorker)
.to receive(:perform_async).with(repository_outdated.id, instance_of(Time)) .to receive(:perform_async).with(repository_outdated.id)
expect(Geo::RepositoryVerification::Primary::SingleWorker) expect(Geo::RepositoryVerification::Primary::SingleWorker)
.to receive(:perform_async).with(wiki_outdated.id, instance_of(Time)) .to receive(:perform_async).with(wiki_outdated.id)
subject.perform(shard_name) subject.perform(shard_name)
end end
...@@ -52,7 +52,7 @@ describe Geo::RepositoryVerification::Primary::ShardWorker, :postgresql, :clean_ ...@@ -52,7 +52,7 @@ describe Geo::RepositoryVerification::Primary::ShardWorker, :postgresql, :clean_
create(:repository_state, :wiki_verified, project: missing_repository_verification) create(:repository_state, :wiki_verified, project: missing_repository_verification)
expect(Geo::RepositoryVerification::Primary::SingleWorker) expect(Geo::RepositoryVerification::Primary::SingleWorker)
.to receive(:perform_async).with(missing_repository_verification.id, instance_of(Time)) .to receive(:perform_async).with(missing_repository_verification.id)
subject.perform(shard_name) subject.perform(shard_name)
end end
...@@ -63,7 +63,7 @@ describe Geo::RepositoryVerification::Primary::ShardWorker, :postgresql, :clean_ ...@@ -63,7 +63,7 @@ describe Geo::RepositoryVerification::Primary::ShardWorker, :postgresql, :clean_
create(:repository_state, :repository_verified, project: missing_wiki_verification) create(:repository_state, :repository_verified, project: missing_wiki_verification)
expect(Geo::RepositoryVerification::Primary::SingleWorker) expect(Geo::RepositoryVerification::Primary::SingleWorker)
.to receive(:perform_async).with(missing_wiki_verification.id, instance_of(Time)) .to receive(:perform_async).with(missing_wiki_verification.id)
subject.perform(shard_name) subject.perform(shard_name)
end end
...@@ -107,11 +107,11 @@ describe Geo::RepositoryVerification::Primary::ShardWorker, :postgresql, :clean_ ...@@ -107,11 +107,11 @@ describe Geo::RepositoryVerification::Primary::ShardWorker, :postgresql, :clean_
create(:repository_state, :repository_outdated, project: missing_outdated) create(:repository_state, :repository_outdated, project: missing_outdated)
expect(Geo::RepositoryVerification::Primary::SingleWorker) expect(Geo::RepositoryVerification::Primary::SingleWorker)
.to receive(:perform_async).with(healthy_unverified.id, instance_of(Time)) .to receive(:perform_async).with(healthy_unverified.id)
expect(Geo::RepositoryVerification::Primary::SingleWorker) expect(Geo::RepositoryVerification::Primary::SingleWorker)
.not_to receive(:perform_async).with(missing_not_verified.id, instance_of(Time)) .not_to receive(:perform_async).with(missing_not_verified.id)
expect(Geo::RepositoryVerification::Primary::SingleWorker) expect(Geo::RepositoryVerification::Primary::SingleWorker)
.not_to receive(:perform_async).with(missing_outdated.id, instance_of(Time)) .not_to receive(:perform_async).with(missing_outdated.id)
Sidekiq::Testing.inline! { subject.perform(shard_name) } Sidekiq::Testing.inline! { subject.perform(shard_name) }
end end
......
...@@ -22,7 +22,7 @@ describe Geo::RepositoryVerification::Primary::SingleWorker, :postgresql, :clean ...@@ -22,7 +22,7 @@ describe Geo::RepositoryVerification::Primary::SingleWorker, :postgresql, :clean
expect_any_instance_of(Gitlab::Git::Checksum).not_to receive(:calculate) expect_any_instance_of(Gitlab::Git::Checksum).not_to receive(:calculate)
subject.perform(project_without_repositories.id, Time.now) subject.perform(project_without_repositories.id)
end end
it 'does not calculate the checksum when project is pending deletion' do it 'does not calculate the checksum when project is pending deletion' do
...@@ -30,97 +30,105 @@ describe Geo::RepositoryVerification::Primary::SingleWorker, :postgresql, :clean ...@@ -30,97 +30,105 @@ describe Geo::RepositoryVerification::Primary::SingleWorker, :postgresql, :clean
expect_any_instance_of(Gitlab::Git::Checksum).not_to receive(:calculate) expect_any_instance_of(Gitlab::Git::Checksum).not_to receive(:calculate)
subject.perform(project_with_repositories.id, Time.now) subject.perform(project_with_repositories.id)
end end
it 'does not raise an error when project could not be found' do it 'does not raise an error when project could not be found' do
expect { subject.perform(-1, Time.now) }.not_to raise_error expect { subject.perform(-1) }.not_to raise_error
end end
it 'calculates the checksum for unverified projects' do it 'calculates the checksum for unverified projects' do
subject.perform(project_with_repositories.id, Time.now) subject.perform(project_with_repositories.id)
expect(project_with_repositories.repository_state).to have_attributes( expect(project_with_repositories.repository_state).to have_attributes(
repository_verification_checksum: instance_of(String), repository_verification_checksum: instance_of(String),
last_repository_verification_failure: nil, last_repository_verification_failure: nil,
last_repository_verification_at: instance_of(Time),
last_repository_verification_failed: false,
wiki_verification_checksum: instance_of(String), wiki_verification_checksum: instance_of(String),
last_wiki_verification_failure: nil, last_wiki_verification_failure: nil
last_wiki_verification_at: instance_of(Time),
last_wiki_verification_failed: false
) )
end end
it 'calculates the checksum for outdated projects' do it 'calculates the checksum for outdated projects' do
repository_state = repository_state =
create(:repository_state, :repository_verified, :wiki_verified, create(:repository_state,
project: project_with_repositories,
repository_verification_checksum: nil,
wiki_verification_checksum: nil)
subject.perform(project_with_repositories.id)
repository_state.reload
expect(repository_state.repository_verification_checksum).not_to be_nil
expect(repository_state.wiki_verification_checksum).not_to be_nil
end
it 'calculates the checksum for outdated repositories' do
repository_state =
create(:repository_state,
project: project_with_repositories,
repository_verification_checksum: nil,
wiki_verification_checksum: 'e123')
subject.perform(project_with_repositories.id)
repository_state.reload
expect(repository_state.repository_verification_checksum).not_to be_nil
expect(repository_state.wiki_verification_checksum).to eq 'e123'
end
it 'calculates the checksum for outdated wikis' do
repository_state =
create(:repository_state,
project: project_with_repositories, project: project_with_repositories,
repository_verification_checksum: 'f123', repository_verification_checksum: 'f123',
last_repository_verification_at: Time.now - 1.hour, wiki_verification_checksum: nil)
wiki_verification_checksum: 'e123',
last_wiki_verification_at: Time.now - 1.hour)
subject.perform(project_with_repositories.id, Time.now) subject.perform(project_with_repositories.id)
repository_state.reload repository_state.reload
expect(repository_state.repository_verification_checksum).not_to eq 'f123' expect(repository_state.repository_verification_checksum).to eq 'f123'
expect(repository_state.last_repository_verification_at).to be_within(10.seconds).of(Time.now) expect(repository_state.wiki_verification_checksum).not_to be_nil
expect(repository_state.wiki_verification_checksum).not_to eq 'e123'
expect(repository_state.last_wiki_verification_at).to be_within(10.seconds).of(Time.now)
end end
it 'does not recalculate the checksum for projects up to date' do it 'does not recalculate the checksum for projects up to date' do
last_verification_at = Time.now
repository_state = repository_state =
create(:repository_state, :repository_verified, :wiki_verified, create(:repository_state,
project: project_with_repositories, project: project_with_repositories,
repository_verification_checksum: 'f123', repository_verification_checksum: 'f123',
last_repository_verification_at: last_verification_at, wiki_verification_checksum: 'e123')
wiki_verification_checksum: 'e123',
last_wiki_verification_at: last_verification_at)
subject.perform(project_with_repositories.id, Time.now - 1.hour) subject.perform(project_with_repositories.id - 1.hour)
expect(repository_state.reload).to have_attributes( expect(repository_state.reload).to have_attributes(
repository_verification_checksum: 'f123', repository_verification_checksum: 'f123',
last_repository_verification_at: be_within(1.second).of(last_verification_at), wiki_verification_checksum: 'e123'
wiki_verification_checksum: 'e123',
last_wiki_verification_at: be_within(1.second).of(last_verification_at)
) )
end end
it 'does not calculate the wiki checksum when wiki is not enabled for project' do it 'does not calculate the wiki checksum when wiki is not enabled for project' do
project_with_repositories.update!(wiki_enabled: false) project_with_repositories.update!(wiki_enabled: false)
subject.perform(project_with_repositories.id, Time.now) subject.perform(project_with_repositories.id)
expect(project_with_repositories.repository_state).to have_attributes( expect(project_with_repositories.repository_state).to have_attributes(
repository_verification_checksum: instance_of(String), repository_verification_checksum: instance_of(String),
last_repository_verification_failure: nil, last_repository_verification_failure: nil,
last_repository_verification_at: instance_of(Time),
last_repository_verification_failed: false,
wiki_verification_checksum: nil, wiki_verification_checksum: nil,
last_wiki_verification_failure: nil, last_wiki_verification_failure: nil
last_wiki_verification_at: nil,
last_wiki_verification_failed: false
) )
end end
it 'keeps track of failures when calculating the repository checksum' do it 'keeps track of failures when calculating the repository checksum' do
subject.perform(project_without_repositories.id, Time.now) subject.perform(project_without_repositories.id)
expect(project_without_repositories.repository_state).to have_attributes( expect(project_without_repositories.repository_state).to have_attributes(
repository_verification_checksum: nil, repository_verification_checksum: nil,
last_repository_verification_failure: /No repository for such path/, last_repository_verification_failure: /No repository for such path/,
last_repository_verification_at: instance_of(Time),
last_repository_verification_failed: true,
wiki_verification_checksum: nil, wiki_verification_checksum: nil,
last_wiki_verification_failure: /No repository for such path/, last_wiki_verification_failure: /No repository for such path/
last_wiki_verification_at: instance_of(Time),
last_wiki_verification_failed: true
) )
end end
end end
......
...@@ -26,8 +26,18 @@ describe PostReceive do ...@@ -26,8 +26,18 @@ describe PostReceive do
allow_any_instance_of(GitPushService).to receive(:execute).and_return(true) allow_any_instance_of(GitPushService).to receive(:execute).and_return(true)
end end
it 'calls Geo::RepositoryUpdatedEventStore' do it 'calls Geo::RepositoryUpdatedService when running on a Geo primary node' do
expect_any_instance_of(::Geo::RepositoryUpdatedEventStore).to receive(:create) allow(Gitlab::Geo).to receive(:primary?) { true }
expect_any_instance_of(::Geo::RepositoryUpdatedService).to receive(:execute)
described_class.new.perform(gl_repository, key_id, base64_changes)
end
it 'does not call Geo::RepositoryUpdatedEventStore when not running on a Geo primary node' do
allow(Gitlab::Geo).to receive(:primary?) { false }
expect_any_instance_of(::Geo::RepositoryUpdatedService).not_to receive(:execute)
described_class.new.perform(gl_repository, key_id, base64_changes) described_class.new.perform(gl_repository, key_id, base64_changes)
end end
...@@ -37,11 +47,18 @@ describe PostReceive do ...@@ -37,11 +47,18 @@ describe PostReceive do
describe '#process_wiki_changes' do describe '#process_wiki_changes' do
let(:gl_repository) { "wiki-#{project.id}" } let(:gl_repository) { "wiki-#{project.id}" }
it 'triggers Geo::RepositoryUpdatedEventStore when Geo is enabled' do it 'calls Geo::RepositoryUpdatedEventStore when running on a Geo primary node' do
allow(Gitlab::Geo).to receive(:enabled?) { true } allow(Gitlab::Geo).to receive(:primary?) { true }
expect_any_instance_of(::Geo::RepositoryUpdatedService).to receive(:execute)
described_class.new.perform(gl_repository, key_id, base64_changes)
end
it 'does not call Geo::RepositoryUpdatedEventStore when not running on a Geo primary node' do
allow(Gitlab::Geo).to receive(:primary?) { false }
expect(::Geo::RepositoryUpdatedEventStore).to receive(:new).with(instance_of(Project), source: ::Geo::RepositoryUpdatedEvent::WIKI).and_call_original expect_any_instance_of(::Geo::RepositoryUpdatedService).not_to receive(:execute)
expect_any_instance_of(::Geo::RepositoryUpdatedEventStore).to receive(:create)
described_class.new.perform(gl_repository, key_id, base64_changes) described_class.new.perform(gl_repository, key_id, base64_changes)
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