Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/models/ci/build_trace_chunk_spec.rb')
-rw-r--r--spec/models/ci/build_trace_chunk_spec.rb138
1 files changed, 118 insertions, 20 deletions
diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb
index fefe5e3bfca..cdbdd2b1d20 100644
--- a/spec/models/ci/build_trace_chunk_spec.rb
+++ b/spec/models/ci/build_trace_chunk_spec.rb
@@ -502,22 +502,12 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
describe '#persist_data!' do
let(:build) { create(:ci_build, :running) }
- subject { build_trace_chunk.persist_data! }
-
- shared_examples_for 'Atomic operation' do
- context 'when the other process is persisting' do
- let(:lease_key) { "trace_write:#{build_trace_chunk.build.id}:chunks:#{build_trace_chunk.chunk_index}" }
-
- before do
- stub_exclusive_lease_taken(lease_key)
- end
-
- it 'raise an error' do
- expect { subject }.to raise_error('Failed to obtain a lock')
- end
- end
+ before do
+ build_trace_chunk.save!
end
+ subject { build_trace_chunk.persist_data! }
+
context 'when data_store is redis' do
let(:data_store) { :redis }
@@ -548,8 +538,6 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
expect(build_trace_chunk.reload.checksum).to eq '3398914352'
end
-
- it_behaves_like 'Atomic operation'
end
context 'when data size has not reached CHUNK_SIZE' do
@@ -575,6 +563,62 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
expect(build_trace_chunk.fog?).to be_truthy
end
end
+
+ context 'when the chunk has been modifed by a different worker' do
+ it 'reloads the chunk before migration' do
+ described_class
+ .find(build_trace_chunk.id)
+ .update!(data_store: :fog)
+
+ build_trace_chunk.persist_data!
+ end
+
+ it 'verifies the operation using optimistic locking' do
+ allow(build_trace_chunk)
+ .to receive(:save!)
+ .and_raise(ActiveRecord::StaleObjectError)
+
+ expect { build_trace_chunk.persist_data! }
+ .to raise_error(described_class::FailedToPersistDataError)
+ end
+
+ it 'does not allow flushing unpersisted chunk' do
+ build_trace_chunk.checksum = '12345'
+
+ expect { build_trace_chunk.persist_data! }
+ .to raise_error(described_class::FailedToPersistDataError,
+ /Modifed build trace chunk detected/)
+ end
+ end
+
+ context 'when the chunk is being locked by a different worker' do
+ let(:metrics) { spy('metrics') }
+
+ it 'does not raise an exception' do
+ lock_chunk do
+ expect { build_trace_chunk.persist_data! }.not_to raise_error
+ end
+ end
+
+ it 'increments stalled chunk trace metric' do
+ allow(build_trace_chunk)
+ .to receive(:metrics)
+ .and_return(metrics)
+
+ lock_chunk { build_trace_chunk.persist_data! }
+
+ expect(metrics)
+ .to have_received(:increment_trace_operation)
+ .with(operation: :stalled)
+ .once
+ end
+
+ def lock_chunk(&block)
+ "trace_write:#{build.id}:chunks:#{chunk_index}".then do |key|
+ build_trace_chunk.in_lock(key, &block)
+ end
+ end
+ end
end
end
@@ -609,8 +653,6 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
expect(Ci::BuildTraceChunks::Database.new.data(build_trace_chunk)).to be_nil
expect(Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk)).to eq(data)
end
-
- it_behaves_like 'Atomic operation'
end
context 'when data size has not reached CHUNK_SIZE' do
@@ -670,8 +712,6 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
expect(Ci::BuildTraceChunks::Database.new.data(build_trace_chunk)).to be_nil
expect(Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk)).to eq(data)
end
-
- it_behaves_like 'Atomic operation'
end
context 'when data size has not reached CHUNK_SIZE' do
@@ -779,4 +819,62 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
it_behaves_like 'deletes all build_trace_chunk and data in redis'
end
end
+
+ describe 'comparable build trace chunks' do
+ describe '#<=>' do
+ context 'when chunks are associated with different builds' do
+ let(:first) { create(:ci_build_trace_chunk, build: build, chunk_index: 1) }
+ let(:second) { create(:ci_build_trace_chunk, chunk_index: 1) }
+
+ it 'returns nil' do
+ expect(first <=> second).to be_nil
+ end
+ end
+
+ context 'when there are two chunks with different indexes' do
+ let(:first) { create(:ci_build_trace_chunk, build: build, chunk_index: 1) }
+ let(:second) { create(:ci_build_trace_chunk, build: build, chunk_index: 0) }
+
+ it 'indicates the the first one is greater than then second' do
+ expect(first <=> second).to eq 1
+ end
+ end
+
+ context 'when there are two chunks with the same index within the same build' do
+ let(:chunk) { create(:ci_build_trace_chunk) }
+
+ it 'indicates the these are equal' do
+ expect(chunk <=> chunk).to be_zero # rubocop:disable Lint/UselessComparison
+ end
+ end
+ end
+
+ describe '#==' do
+ context 'when chunks have the same index' do
+ let(:chunk) { create(:ci_build_trace_chunk) }
+
+ it 'indicates that the chunks are equal' do
+ expect(chunk).to eq chunk
+ end
+ end
+
+ context 'when chunks have different indexes' do
+ let(:first) { create(:ci_build_trace_chunk, build: build, chunk_index: 1) }
+ let(:second) { create(:ci_build_trace_chunk, build: build, chunk_index: 0) }
+
+ it 'indicates that the chunks are not equal' do
+ expect(first).not_to eq second
+ end
+ end
+
+ context 'when chunks are associated with different builds' do
+ let(:first) { create(:ci_build_trace_chunk, build: build, chunk_index: 1) }
+ let(:second) { create(:ci_build_trace_chunk, chunk_index: 1) }
+
+ it 'indicates that the chunks are not equal' do
+ expect(first).not_to eq second
+ end
+ end
+ end
+ end
end