diff options
| author | Shinya Maeda <shinya@gitlab.com> | 2018-04-07 05:13:30 +0900 |
|---|---|---|
| committer | Shinya Maeda <shinya@gitlab.com> | 2018-04-07 05:13:30 +0900 |
| commit | 2077065d91999de597dc1208447d3fd41c6d4ac8 (patch) | |
| tree | 58d3b93893793d9b162db04f25651c315cec6dae | |
| parent | 76485cbf8ba555c929fd2f54ca2051a382760f20 (diff) | |
| download | gitlab-ce-2077065d91999de597dc1208447d3fd41c6d4ac8.tar.gz | |
Avoid destroy_all. Hook ActiveRecord.delete_all and flush redis
| -rw-r--r-- | app/models/ci/build.rb | 7 | ||||
| -rw-r--r-- | app/models/ci/job_trace_chunk.rb | 39 | ||||
| -rw-r--r-- | lib/gitlab/ci/trace.rb | 4 | ||||
| -rw-r--r-- | lib/gitlab/ci/trace/chunked_io.rb | 6 | ||||
| -rw-r--r-- | spec/lib/gitlab/ci/trace/chunked_io_spec.rb | 4 | ||||
| -rw-r--r-- | spec/models/ci/job_trace_chunk_spec.rb | 52 |
6 files changed, 86 insertions, 26 deletions
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index b471fb80536..a7d2424508e 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -25,7 +25,7 @@ module Ci has_one :job_artifacts_metadata, -> { where(file_type: Ci::JobArtifact.file_types[:metadata]) }, class_name: 'Ci::JobArtifact', inverse_of: :job, foreign_key: :job_id has_one :job_artifacts_trace, -> { where(file_type: Ci::JobArtifact.file_types[:trace]) }, class_name: 'Ci::JobArtifact', inverse_of: :job, foreign_key: :job_id - has_many :chunks, class_name: 'Ci::JobTraceChunk', foreign_key: :job_id, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :chunks, class_name: 'Ci::JobTraceChunk', foreign_key: :job_id has_one :metadata, class_name: 'Ci::BuildMetadata' delegate :timeout, to: :metadata, prefix: true, allow_nil: true @@ -91,6 +91,7 @@ module Ci before_save :update_artifacts_size, if: :artifacts_file_changed? before_save :ensure_token before_destroy { unscoped_project } + before_destroy :flush_redis_chunks before_create :ensure_metadata after_create unless: :importing? do |build| @@ -677,5 +678,9 @@ module Ci update_project_statistics end end + + def flush_redis_chunks + chunks.redis.map(&:redis_delete_data) + end end end diff --git a/app/models/ci/job_trace_chunk.rb b/app/models/ci/job_trace_chunk.rb index 383c6596a51..a5f1734ddfd 100644 --- a/app/models/ci/job_trace_chunk.rb +++ b/app/models/ci/job_trace_chunk.rb @@ -4,8 +4,6 @@ module Ci belongs_to :job, class_name: "Ci::Build", foreign_key: :job_id - after_destroy :redis_delete_data, if: :redis? - default_value_for :data_store, :redis WriteError = Class.new(StandardError) @@ -21,6 +19,28 @@ module Ci db: 2 } + # Hook delete_all method to flush redis before the record has been destoryed + def self.delete_all(*args) + if self.delete_all_truncate_condition(args.first) || + self.delete_all_delete_condition(args.first) + Ci::JobTraceChunk.where(args.first).redis.map(&:redis_delete_data) + end + + super + end + + def destory + raise 'You are going to be punished by yorik' + end + + def self.delete_all_truncate_condition(query) + query.is_a?(String) && /^job_id = \d+ AND chunk_index > \d+$/ =~ query + end + + def self.delete_all_delete_condition(query) + query.is_a?(Hash) && query.keys.count == 1 && query.keys.first == :job_id + end + def data if redis? redis_data @@ -44,8 +64,9 @@ module Ci end save! if changed? - schedule_to_db if fullfilled? end + + schedule_to_db if fullfilled? end def truncate(offset = 0) @@ -86,6 +107,12 @@ module Ci end end + def redis_delete_data + Gitlab::Redis::SharedState.with do |redis| + redis.del(redis_data_key) + end + end + private def schedule_to_db @@ -110,12 +137,6 @@ module Ci end end - def redis_delete_data - Gitlab::Redis::SharedState.with do |redis| - redis.del(redis_data_key) - end - end - def redis_data_key "gitlab:ci:trace:#{job_id}:chunks:#{chunk_index}:data" end diff --git a/lib/gitlab/ci/trace.rb b/lib/gitlab/ci/trace.rb index 6554c924e5c..abf9212d901 100644 --- a/lib/gitlab/ci/trace.rb +++ b/lib/gitlab/ci/trace.rb @@ -100,7 +100,7 @@ module Gitlab FileUtils.rm(trace_path, force: true) end - job.chunks.destroy_all + ::Ci::JobTraceChunk.delete_all(job_id: job.id) job.erase_old_trace! end @@ -111,7 +111,7 @@ module Gitlab if job.chunks.any? Gitlab::Ci::Trace::ChunkedIO.new(job) do |stream| archive_stream!(stream) - stream.destroy! + stream.delete! end elsif current_path File.open(current_path) do |stream| diff --git a/lib/gitlab/ci/trace/chunked_io.rb b/lib/gitlab/ci/trace/chunked_io.rb index 2caebe3c95e..56d1f0c2fa4 100644 --- a/lib/gitlab/ci/trace/chunked_io.rb +++ b/lib/gitlab/ci/trace/chunked_io.rb @@ -141,7 +141,7 @@ module Gitlab @size = offset # remove all next chunks - job_chunks.where('chunk_index > ?', chunk_index).destroy_all + ::Ci::JobTraceChunk.delete_all("job_id = #{job.id} AND chunk_index > #{chunk_index}") # truncate current chunk current_chunk.truncate(chunk_offset) if chunk_offset != 0 @@ -157,8 +157,8 @@ module Gitlab true end - def destroy! - job_chunks.destroy_all + def delete! + ::Ci::JobTraceChunk.delete_all(job_id: job.id) @tell = @size = 0 ensure invalidate_chunk_cache diff --git a/spec/lib/gitlab/ci/trace/chunked_io_spec.rb b/spec/lib/gitlab/ci/trace/chunked_io_spec.rb index bcef21d5f71..2ef72ee4d6f 100644 --- a/spec/lib/gitlab/ci/trace/chunked_io_spec.rb +++ b/spec/lib/gitlab/ci/trace/chunked_io_spec.rb @@ -366,8 +366,8 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do end end - context "#destroy!" do - subject { chunked_io.destroy! } + context "#delete!" do + subject { chunked_io.delete! } before do job.trace.set(sample_trace_raw) diff --git a/spec/models/ci/job_trace_chunk_spec.rb b/spec/models/ci/job_trace_chunk_spec.rb index eb240de188f..d9d30a07f03 100644 --- a/spec/models/ci/job_trace_chunk_spec.rb +++ b/spec/models/ci/job_trace_chunk_spec.rb @@ -336,37 +336,71 @@ describe Ci::JobTraceChunk, :clean_gitlab_redis_shared_state do create(:ci_build, :running, :trace_live, pipeline: pipeline, project: project) create(:ci_build, :running, :trace_live, pipeline: pipeline, project: project) create(:ci_build, :running, :trace_live, pipeline: pipeline, project: project) + @build_ids = project.builds.map(&:id) end - shared_examples_for 'deletes all job_trace_chunk and data in redis' do + shared_examples_for 'deletes all job_trace_chunk and data in redis' do |expected_count = 0| it do - project.builds.each do |build| + count = 0 + + @build_ids.each do |build_id| Gitlab::Redis::SharedState.with do |redis| - redis.scan_each(match: "gitlab:ci:trace:#{build.id}:chunks:?") do |key| + redis.scan_each(match: "gitlab:ci:trace:#{build_id}:chunks:?:data") do |key| expect(redis.exists(key)).to be_truthy + count += 1 end end end + raise 'Sample is not processed correctly' unless count == 3 + expect(described_class.count).not_to eq(0) subject - expect(described_class.count).to eq(0) + expect(described_class.count).to eq(expected_count) - project.builds.each do |build| + @build_ids.each do |build_id| Gitlab::Redis::SharedState.with do |redis| - redis.scan_each(match: "gitlab:ci:trace:#{build.id}:chunks:?") do |key| - expect(redis.exists(key)).to be_falsey + redis.scan_each(match: "gitlab:ci:trace:#{build_id}:chunks:?:data") do |key| + raise 'Redis key still exists' end end end end end - context 'when job_trace_chunk is destroyed' do + context 'when trace is truncated' do + include ChunkedIOHelpers + + let(:subject) do + project.builds.each { |build| build.trace.set(sample_trace_raw) } + project.builds.each { |build| build.trace.write { |stream| stream.truncate(0) } } + end + + it_behaves_like 'deletes all job_trace_chunk and data in redis', 3 + end + + context 'when trace is erased' do + let(:subject) do + project.builds.each { |build| build.trace.erase! } + end + + it_behaves_like 'deletes all job_trace_chunk and data in redis' + end + + context 'when trace is archived' do + let(:subject) do + allow_any_instance_of(Ci::Build).to receive(:complete?) { true } + project.builds.each { |build| build.trace.archive! } + end + + it_behaves_like 'deletes all job_trace_chunk and data in redis' + end + + context 'when job_trace_chunk are deleted' do let(:subject) do - project.builds.each { |build| build.chunks.destroy_all } + project.builds.each { |build| ::Ci::JobTraceChunk.delete_all(job_id: build.id) } end it_behaves_like 'deletes all job_trace_chunk and data in redis' |
