1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
# frozen_string_literal: true
class ProjectImportState < ApplicationRecord
include AfterCommitQueue
include ImportState::SidekiqJobTracker
self.table_name = "project_mirror_data"
after_commit :expire_etag_cache
belongs_to :project, inverse_of: :import_state
validates :project, presence: true
validates :checksums, json_schema: { filename: "project_import_stats" }
alias_attribute :correlation_id, :correlation_id_value
state_machine :status, initial: :none do
event :schedule do
transition [:none, :finished, :failed] => :scheduled
end
event :force_start do
transition [:none, :finished, :failed] => :started
end
event :start do
transition scheduled: :started
end
event :finish do
transition started: :finished
end
event :cancel do
transition [:none, :scheduled, :started] => :canceled
end
event :fail_op do
transition [:scheduled, :started] => :failed
end
state :scheduled
state :started
state :finished
state :failed
state :canceled
after_transition [:none, :finished, :failed] => :scheduled do |state, _|
state.run_after_commit do
job_id = project.add_import_job
if job_id
correlation_id = Labkit::Correlation::CorrelationId.current_or_new_id
update(jid: job_id, correlation_id_value: correlation_id)
end
end
end
after_transition any => [:canceled, :finished] do |state, _|
if state.jid.present?
Gitlab::SidekiqStatus.unset(state.jid)
state.update_column(:jid, nil)
end
end
after_transition any => [:canceled, :failed] do |state, _|
state.project.remove_import_data
end
before_transition started: [:finished, :canceled, :failed] do |state, _|
project = state.project
if project.github_import?
import_stats = ::Gitlab::GithubImport::ObjectCounter.summary(state.project)
state.update_column(:checksums, import_stats)
end
end
after_transition started: :finished do |state, _|
project = state.project
project.reset_cache_and_import_attrs
if Gitlab::ImportSources.importer_names.include?(project.import_type) && project.repo_exists?
state.run_after_commit do
Projects::AfterImportWorker.perform_async(project.id)
end
end
end
end
def expire_etag_cache
if realtime_changes_path
Gitlab::EtagCaching::Store.new.tap do |store|
store.touch(realtime_changes_path)
rescue Gitlab::EtagCaching::Store::InvalidKeyError
# no-op: not every realtime changes endpoint is using etag caching
end
end
end
def realtime_changes_path
Gitlab::Routing.url_helpers.polymorphic_path([:realtime_changes_import, project.import_type.to_sym], format: :json)
rescue NoMethodError
# polymorphic_path throws NoMethodError when no such path exists
nil
end
def relation_hard_failures(limit:)
project.import_failures.hard_failures_by_correlation_id(correlation_id).limit(limit)
end
def mark_as_failed(error_message)
original_errors = errors.dup
sanitized_message = Gitlab::UrlSanitizer.sanitize(error_message)
fail_op
update_column(:last_error, sanitized_message)
rescue ActiveRecord::ActiveRecordError => e
Gitlab::Import::Logger.error(
message: 'Error setting import status to failed',
error: e.message,
original_error: sanitized_message
)
ensure
@errors = original_errors
end
alias_method :no_import?, :none?
def in_progress?
scheduled? || started?
end
def started?
# import? does SQL work so only run it if it looks like there's an import running
status == 'started' && project.import?
end
end
ProjectImportState.prepend_mod_with('ProjectImportState')
|