Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
24ba0989
Commit
24ba0989
authored
Jun 25, 2018
by
Shinya Maeda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added spec for build trace chunk
parent
49467379
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
321 additions
and
180 deletions
+321
-180
app/models/ci/build_trace_chunk.rb
app/models/ci/build_trace_chunk.rb
+11
-10
app/services/concerns/exclusive_lease_lock.rb
app/services/concerns/exclusive_lease_lock.rb
+3
-1
spec/models/ci/build_trace_chunk_spec.rb
spec/models/ci/build_trace_chunk_spec.rb
+302
-169
spec/support/helpers/stub_object_storage.rb
spec/support/helpers/stub_object_storage.rb
+5
-0
No files found.
app/models/ci/build_trace_chunk.rb
View file @
24ba0989
...
...
@@ -8,8 +8,6 @@ module Ci
default_value_for
:data_store
,
:redis
WriteError
=
Class
.
new
(
StandardError
)
CHUNK_SIZE
=
128
.
kilobytes
WRITE_LOCK_RETRY
=
10
WRITE_LOCK_SLEEP
=
0.01
.
seconds
...
...
@@ -65,6 +63,7 @@ module Ci
end
def
truncate
(
offset
=
0
)
raise
ArgumentError
,
'Fog store does not support truncating'
if
fog?
# If data is null, get_data returns Excon::Error::NotFound
raise
ArgumentError
,
'Offset is out of range'
if
offset
>
size
||
offset
<
0
return
if
offset
==
size
# Skip the following process as it doesn't affect anything
...
...
@@ -72,6 +71,8 @@ module Ci
end
def
append
(
new_data
,
offset
)
raise
ArgumentError
,
'Fog store does not support appending'
if
fog?
# If data is null, get_data returns Excon::Error::NotFound
raise
ArgumentError
,
'New data is nil'
unless
new_data
raise
ArgumentError
,
'Offset is out of range'
if
offset
>
size
||
offset
<
0
raise
ArgumentError
,
'Chunk size overflow'
if
CHUNK_SIZE
<
(
offset
+
new_data
.
bytesize
)
...
...
@@ -98,21 +99,17 @@ module Ci
(
start_offset
...
end_offset
)
end
def
data_persisted?
!
redis?
end
def
persist_data!
in_lock
(
*
lock_params
)
do
# Write opetation is atomic
unsafe_
migrate
_to!
(
self
.
class
.
persist_store
)
unsafe_
persist
_to!
(
self
.
class
.
persist_store
)
end
end
private
def
unsafe_
migrate
_to!
(
new_store
)
def
unsafe_
persist
_to!
(
new_store
)
return
if
data_store
==
new_store
.
to_s
r
eturn
unless
size
>
0
r
aise
ArgumentError
,
'Can not persist empty data'
unless
size
>
0
old_store_class
=
self
.
class
.
get_store_class
(
data_store
)
...
...
@@ -130,7 +127,7 @@ module Ci
end
def
unsafe_set_data!
(
value
)
raise
ArgumentError
,
'
too much data
'
if
value
.
bytesize
>
CHUNK_SIZE
raise
ArgumentError
,
'
New data size exceeds chunk size
'
if
value
.
bytesize
>
CHUNK_SIZE
self
.
class
.
get_store_class
(
data_store
).
set_data
(
self
,
value
)
@data
=
value
...
...
@@ -144,6 +141,10 @@ module Ci
Ci
::
BuildTraceChunkFlushWorker
.
perform_async
(
id
)
end
def
data_persisted?
!
redis?
end
def
full?
size
==
CHUNK_SIZE
end
...
...
app/services/concerns/exclusive_lease_lock.rb
View file @
24ba0989
module
ExclusiveLeaseLock
extend
ActiveSupport
::
Concern
FailedToObtainLockError
=
Class
.
new
(
StandardError
)
def
in_lock
(
key
,
ttl:
1
.
minute
,
retry_max:
10
,
sleep_sec:
0.01
.
seconds
)
lease
=
Gitlab
::
ExclusiveLease
.
new
(
key
,
timeout:
ttl
)
retry_count
=
0
...
...
@@ -12,7 +14,7 @@ module ExclusiveLeaseLock
break
if
retry_max
<
(
retry_count
+=
1
)
end
raise
WriteError
,
'Failed to obtain write
lock'
unless
uuid
raise
FailedToObtainLockError
,
'Failed to obtain a
lock'
unless
uuid
return
yield
ensure
...
...
spec/models/ci/build_trace_chunk_spec.rb
View file @
24ba0989
...
...
@@ -12,6 +12,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
before
do
stub_feature_flags
(
ci_enable_live_trace:
true
)
stub_artifacts_object_storage
end
context
'FastDestroyAll'
do
...
...
@@ -42,154 +43,220 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
let
(
:data_store
)
{
:redis
}
before
do
build_trace_chunk
.
send
(
:
redis_set_data
,
'Sample data in redis'
)
build_trace_chunk
.
send
(
:
unsafe_set_data!
,
'Sample data in redis'
)
end
it
{
is_expected
.
to
eq
(
'Sample data in redis'
)
}
end
context
'when data_store is database'
do
let
(
:data_store
)
{
:d
b
}
let
(
:raw_data
)
{
'Sample data in d
b
'
}
let
(
:data_store
)
{
:d
atabase
}
let
(
:raw_data
)
{
'Sample data in d
atabase
'
}
it
{
is_expected
.
to
eq
(
'Sample data in d
b
'
)
}
it
{
is_expected
.
to
eq
(
'Sample data in d
atabase
'
)
}
end
end
describe
'#set_data'
do
subject
{
build_trace_chunk
.
send
(
:set_data
,
value
)
}
let
(
:value
)
{
'Sample data'
}
context
'when data_store is fog'
do
let
(
:data_store
)
{
:fog
}
context
'when value bytesize is bigger than CHUNK_SIZE'
do
let
(
:value
)
{
'a'
*
(
described_class
::
CHUNK_SIZE
+
1
)
}
before
do
::
Fog
::
Storage
.
new
(
JobArtifactUploader
.
object_store_credentials
).
tap
do
|
connection
|
connection
.
put_object
(
'artifacts'
,
"tmp/builds/
#{
build
.
id
}
/chunks/
#{
chunk_index
}
.log"
,
'Sample data in fog'
)
end
end
it
{
expect
{
subject
}.
to
raise_error
(
'too much data
'
)
}
it
{
is_expected
.
to
eq
(
'Sample data in fog
'
)
}
end
end
context
'when data_store is redis
'
do
let
(
:data_store
)
{
:redis
}
describe
'#append
'
do
subject
{
build_trace_chunk
.
append
(
new_data
,
offset
)
}
it
do
expect
(
build_trace_chunk
.
send
(
:redis_data
)).
to
be_nil
let
(
:new_data
)
{
'Sample new data'
}
let
(
:offset
)
{
0
}
let
(
:merged_data
)
{
data
+
new_data
.
to_s
}
subject
shared_examples_for
'Appending correctly'
do
context
'when offset is negative'
do
let
(
:offset
)
{
-
1
}
expect
(
build_trace_chunk
.
send
(
:redis_data
)).
to
eq
(
value
)
it
{
expect
{
subject
}.
to
raise_error
(
'Offset is out of range'
)
}
end
context
'when fullfilled chunk size'
do
let
(
:value
)
{
'a'
*
described_class
::
CHUNK_SIZE
}
it
'schedules stashing data'
do
expect
(
Ci
::
BuildTraceChunkFlushWorker
).
to
receive
(
:perform_async
).
once
context
'when offset is bigger than data size'
do
let
(
:offset
)
{
data
.
bytesize
+
1
}
subject
end
it
{
expect
{
subject
}.
to
raise_error
(
'Offset is out of range'
)
}
end
end
context
'when data_store is databas
e'
do
let
(
:data_store
)
{
:db
}
context
'when new data overflows chunk siz
e'
do
let
(
:new_data
)
{
'a'
*
(
described_class
::
CHUNK_SIZE
+
1
)
}
it
'sets data'
do
expect
(
build_trace_chunk
.
raw_data
).
to
be_nil
it
{
expect
{
subject
}.
to
raise_error
(
'Chunk size overflow'
)
}
end
subject
context
'when offset is EOF'
do
let
(
:offset
)
{
data
.
bytesize
}
expect
(
build_trace_chunk
.
raw_data
).
to
eq
(
value
)
expect
(
build_trace_chunk
.
persisted?
).
to
be_truthy
end
it
'appends'
do
subject
context
'when raw_data is not changed'
do
it
'does not execute UPDATE'
do
expect
(
build_trace_chunk
.
raw_data
).
to
be_nil
build_trace_chunk
.
save!
expect
(
build_trace_chunk
.
data
).
to
eq
(
merged_data
)
end
# First set
expect
(
ActiveRecord
::
QueryRecorder
.
new
{
subject
}.
count
).
to
be
>
0
expect
(
build_trace_chunk
.
raw_data
).
to
eq
(
value
)
expect
(
build_trace_chunk
.
persisted?
).
to
be_truthy
context
'when new_data is nil'
do
let
(
:new_data
)
{
nil
}
it
'raises an error'
do
expect
{
subject
}.
to
raise_error
(
'New data is nil'
)
end
end
# Second set
build_trace_chunk
.
reload
expect
(
ActiveRecord
::
QueryRecorder
.
new
{
subject
}.
count
).
to
be
(
0
)
context
'when new_data is empty'
do
let
(
:new_data
)
{
''
}
it
'does not append'
do
subject
expect
(
build_trace_chunk
.
data
).
to
eq
(
data
)
end
it
'does not execute UPDATE'
do
ActiveRecord
::
QueryRecorder
.
new
{
subject
}.
log
.
map
do
|
query
|
expect
(
query
).
not_to
include
(
'UPDATE'
)
end
end
end
end
context
'when fullfilled chunk size'
do
it
'does not schedule stashing data'
do
expect
(
Ci
::
BuildTraceChunkFlushWorker
).
not_to
receive
(
:perform_async
)
context
'when offset is middle of datasize'
do
let
(
:offset
)
{
data
.
bytesize
/
2
}
it
'appends'
do
subject
expect
(
build_trace_chunk
.
data
).
to
eq
(
data
.
byteslice
(
0
,
offset
)
+
new_data
)
end
end
end
context
'when data_store is others'
do
before
do
build_trace_chunk
.
send
(
:write_attribute
,
:data_store
,
-
1
)
end
shared_examples_for
'Scheduling sidekiq worker to flush data to persist store'
do
context
'when new data fullfilled chunk size'
do
let
(
:new_data
)
{
'a'
*
described_class
::
CHUNK_SIZE
}
it
{
expect
{
subject
}.
to
raise_error
(
'Unsupported data store'
)
}
end
end
it
'schedules trace chunk flush worker'
do
expect
(
Ci
::
BuildTraceChunkFlushWorker
).
to
receive
(
:perform_async
).
once
describe
'#truncate'
do
subject
{
build_trace_chunk
.
truncate
(
offset
)
}
subject
end
shared_examples_for
'truncates
'
do
context
'when offset is negative'
do
let
(
:offset
)
{
-
1
}
it
'migrates data to object storage
'
do
Sidekiq
::
Testing
.
inline!
do
subject
it
{
expect
{
subject
}.
to
raise_error
(
'Offset is out of range'
)
}
build_trace_chunk
.
reload
expect
(
build_trace_chunk
.
fog?
).
to
be_truthy
expect
(
build_trace_chunk
.
data
).
to
eq
(
new_data
)
end
end
end
end
context
'when offset is bigger than data size'
do
let
(
:offset
)
{
data
.
bytesize
+
1
}
it
{
expect
{
subject
}.
to
raise_error
(
'Offset is out of range'
)
}
end
shared_examples_for
'Scheduling no sidekiq worker'
do
context
'when new data fullfilled chunk size'
do
let
(
:new_data
)
{
'a'
*
described_class
::
CHUNK_SIZE
}
context
'when offset is 10
'
do
let
(
:offset
)
{
10
}
it
'does not schedule trace chunk flush worker
'
do
expect
(
Ci
::
BuildTraceChunkFlushWorker
).
not_to
receive
(
:perform_async
)
it
'truncates'
do
subject
end
expect
(
build_trace_chunk
.
data
).
to
eq
(
data
.
byteslice
(
0
,
offset
))
it
'does not migrate data to object storage'
do
Sidekiq
::
Testing
.
inline!
do
data_store
=
build_trace_chunk
.
data_store
subject
build_trace_chunk
.
reload
expect
(
build_trace_chunk
.
data_store
).
to
eq
(
data_store
)
end
end
end
end
context
'when data_store is redis'
do
let
(
:data_store
)
{
:redis
}
let
(
:data
)
{
'Sample data in redis'
}
before
do
build_trace_chunk
.
send
(
:redis_set_data
,
data
)
context
'when there are no data'
do
let
(
:data
)
{
''
}
it
'has no data'
do
expect
(
build_trace_chunk
.
data
).
to
be_empty
end
it_behaves_like
'Appending correctly'
it_behaves_like
'Scheduling sidekiq worker to flush data to persist store'
end
it_behaves_like
'truncates'
context
'when there are some data'
do
let
(
:data
)
{
'Sample data in redis'
}
before
do
build_trace_chunk
.
send
(
:unsafe_set_data!
,
data
)
end
it
'has data'
do
expect
(
build_trace_chunk
.
data
).
to
eq
(
data
)
end
it_behaves_like
'Appending correctly'
it_behaves_like
'Scheduling sidekiq worker to flush data to persist store'
end
end
context
'when data_store is database'
do
let
(
:data_store
)
{
:db
}
let
(
:raw_data
)
{
'Sample data in db'
}
let
(
:data
)
{
raw_data
}
let
(
:data_store
)
{
:database
}
it_behaves_like
'truncates'
context
'when there are no data'
do
let
(
:data
)
{
''
}
it
'has no data'
do
expect
(
build_trace_chunk
.
data
).
to
be_empty
end
it_behaves_like
'Appending correctly'
it_behaves_like
'Scheduling no sidekiq worker'
end
context
'when there are some data'
do
let
(
:raw_data
)
{
'Sample data in database'
}
let
(
:data
)
{
raw_data
}
it
'has data'
do
expect
(
build_trace_chunk
.
data
).
to
eq
(
data
)
end
it_behaves_like
'Appending correctly'
it_behaves_like
'Scheduling no sidekiq worker'
end
end
end
describe
'#append'
do
subject
{
build_trace_chunk
.
append
(
new_data
,
offset
)
}
context
'when data_store is fog'
do
let
(
:data_store
)
{
:fog
}
let
(
:data
)
{
''
}
let
(
:offset
)
{
0
}
let
(
:new_data
)
{
'Sample new data'
}
let
(
:offset
)
{
0
}
let
(
:total_data
)
{
data
+
new_data
}
it
'can not append'
do
expect
{
subject
}.
to
raise_error
(
'Fog store does not support appending'
)
end
end
end
describe
'#truncate'
do
subject
{
build_trace_chunk
.
truncate
(
offset
)
}
shared_examples_for
'
append
s'
do
shared_examples_for
'
truncate
s'
do
context
'when offset is negative'
do
let
(
:offset
)
{
-
1
}
...
...
@@ -202,29 +269,13 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
it
{
expect
{
subject
}.
to
raise_error
(
'Offset is out of range'
)
}
end
context
'when offset is bigger than data size'
do
let
(
:new_data
)
{
'a'
*
(
described_class
::
CHUNK_SIZE
+
1
)
}
it
{
expect
{
subject
}.
to
raise_error
(
'Chunk size overflow'
)
}
end
context
'when offset is EOF'
do
let
(
:offset
)
{
data
.
bytesize
}
it
'appends'
do
subject
expect
(
build_trace_chunk
.
data
).
to
eq
(
total_data
)
end
end
context
'when offset is 10'
do
let
(
:offset
)
{
10
}
it
'
append
s'
do
it
'
truncate
s'
do
subject
expect
(
build_trace_chunk
.
data
).
to
eq
(
data
.
byteslice
(
0
,
offset
)
+
new_data
)
expect
(
build_trace_chunk
.
data
).
to
eq
(
data
.
byteslice
(
0
,
offset
))
end
end
end
...
...
@@ -234,18 +285,28 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
let
(
:data
)
{
'Sample data in redis'
}
before
do
build_trace_chunk
.
send
(
:
redis_set_data
,
data
)
build_trace_chunk
.
send
(
:
unsafe_set_data!
,
data
)
end
it_behaves_like
'
append
s'
it_behaves_like
'
truncate
s'
end
context
'when data_store is database'
do
let
(
:data_store
)
{
:d
b
}
let
(
:raw_data
)
{
'Sample data in d
b
'
}
let
(
:data_store
)
{
:d
atabase
}
let
(
:raw_data
)
{
'Sample data in d
atabase
'
}
let
(
:data
)
{
raw_data
}
it_behaves_like
'appends'
it_behaves_like
'truncates'
end
context
'when data_store is fog'
do
let
(
:data_store
)
{
:fog
}
let
(
:data
)
{
''
}
let
(
:offset
)
{
0
}
it
'can not truncate'
do
expect
{
subject
}.
to
raise_error
(
'Fog store does not support truncating'
)
end
end
end
...
...
@@ -259,7 +320,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
let
(
:data
)
{
'Sample data in redis'
}
before
do
build_trace_chunk
.
send
(
:
redis_set_data
,
data
)
build_trace_chunk
.
send
(
:
unsafe_set_data!
,
data
)
end
it
{
is_expected
.
to
eq
(
data
.
bytesize
)
}
...
...
@@ -271,10 +332,10 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
context
'when data_store is database'
do
let
(
:data_store
)
{
:d
b
}
let
(
:data_store
)
{
:d
atabase
}
context
'when data exists'
do
let
(
:raw_data
)
{
'Sample data in d
b
'
}
let
(
:raw_data
)
{
'Sample data in d
atabase
'
}
let
(
:data
)
{
raw_data
}
it
{
is_expected
.
to
eq
(
data
.
bytesize
)
}
...
...
@@ -284,6 +345,25 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
it
{
is_expected
.
to
eq
(
0
)
}
end
end
context
'when data_store is fog'
do
let
(
:data_store
)
{
:fog
}
context
'when data exists'
do
let
(
:data
)
{
'Sample data in fog'
}
let
(
:key
)
{
"tmp/builds/
#{
build
.
id
}
/chunks/
#{
chunk_index
}
.log"
}
before
do
build_trace_chunk
.
send
(
:unsafe_set_data!
,
data
)
end
it
{
is_expected
.
to
eq
(
data
.
bytesize
)
}
end
context
'when data does not exist'
do
it
{
expect
{
subject
}.
to
raise_error
(
Excon
::
Error
::
NotFound
)
}
end
end
end
describe
'#persist_data!'
do
...
...
@@ -296,93 +376,146 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
let
(
:data
)
{
'Sample data in redis'
}
before
do
build_trace_chunk
.
send
(
:
redis_set_data
,
data
)
build_trace_chunk
.
send
(
:
unsafe_set_data!
,
data
)
end
it
'stashes the data'
do
expect
(
build_trace_chunk
.
data_store
).
to
eq
(
'redis'
)
expect
(
build_trace_chunk
.
send
(
:redis_data
)).
to
eq
(
data
)
expect
(
build_trace_chunk
.
raw_data
).
to
be_nil
it
'persists the data'
do
expect
(
build_trace_chunk
.
redis?
).
to
be_truthy
expect
(
Ci
::
BuildTraceChunks
::
Redis
.
new
.
data
(
build_trace_chunk
)).
to
eq
(
data
)
expect
(
Ci
::
BuildTraceChunks
::
Database
.
new
.
data
(
build_trace_chunk
)).
to
be_nil
expect
{
Ci
::
BuildTraceChunks
::
Fog
.
new
.
data
(
build_trace_chunk
)
}.
to
raise_error
(
Excon
::
Error
::
NotFound
)
subject
expect
(
build_trace_chunk
.
data_store
).
to
eq
(
'db'
)
expect
(
build_trace_chunk
.
send
(
:redis_data
)).
to
be_nil
expect
(
build_trace_chunk
.
raw_data
).
to
eq
(
data
)
expect
(
build_trace_chunk
.
fog?
).
to
be_truthy
expect
(
Ci
::
BuildTraceChunks
::
Redis
.
new
.
data
(
build_trace_chunk
)).
to
be_nil
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
end
context
'when data does not exist'
do
it
'does not
call UPDATE
'
do
expect
(
ActiveRecord
::
QueryRecorder
.
new
{
subject
}.
count
).
to
eq
(
0
)
it
'does not
persist
'
do
expect
{
subject
}.
to
raise_error
(
'Can not persist empty data'
)
end
end
end
context
'when data_store is database'
do
let
(
:data_store
)
{
:db
}
it
'does not call UPDATE'
do
expect
(
ActiveRecord
::
QueryRecorder
.
new
{
subject
}.
count
).
to
eq
(
0
)
end
end
end
describe
'ExclusiveLock'
do
before
do
allow_any_instance_of
(
Gitlab
::
ExclusiveLease
).
to
receive
(
:try_obtain
)
{
nil
}
stub_const
(
'Ci::BuildTraceChunk::WRITE_LOCK_RETRY'
,
1
)
end
it
'raise an error'
do
expect
{
build_trace_chunk
.
append
(
'ABC'
,
0
)
}.
to
raise_error
(
'Failed to obtain write lock'
)
end
end
describe
'deletes data in redis after a parent record destroyed'
do
let
(
:project
)
{
create
(
:project
)
}
let
(
:data_store
)
{
:database
}
before
do
pipeline
=
create
(
:ci_pipeline
,
project:
project
)
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
)
end
context
'when data exists'
do
let
(
:data
)
{
'Sample data in database'
}
shared_examples_for
'deletes all build_trace_chunk and data in redis'
do
it
do
Gitlab
::
Redis
::
SharedState
.
with
do
|
redis
|
expect
(
redis
.
scan_each
(
match:
"gitlab:ci:trace:*:chunks:*"
).
to_a
.
size
).
to
eq
(
3
)
before
do
build_trace_chunk
.
send
(
:unsafe_set_data!
,
data
)
end
expect
(
described_class
.
count
).
to
eq
(
3
)
it
'persists the data'
do
expect
(
build_trace_chunk
.
database?
).
to
be_truthy
expect
(
Ci
::
BuildTraceChunks
::
Redis
.
new
.
data
(
build_trace_chunk
)).
to
be_nil
expect
(
Ci
::
BuildTraceChunks
::
Database
.
new
.
data
(
build_trace_chunk
)).
to
eq
(
data
)
expect
{
Ci
::
BuildTraceChunks
::
Fog
.
new
.
data
(
build_trace_chunk
)
}.
to
raise_error
(
Excon
::
Error
::
NotFound
)
subject
subject
expect
(
described_class
.
count
).
to
eq
(
0
)
expect
(
build_trace_chunk
.
fog?
).
to
be_truthy
expect
(
Ci
::
BuildTraceChunks
::
Redis
.
new
.
data
(
build_trace_chunk
)).
to
be_nil
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
end
Gitlab
::
Redis
::
SharedState
.
with
do
|
redis
|
expect
(
redis
.
scan_each
(
match:
"gitlab:ci:trace:*:chunks:*"
).
to_a
.
size
).
to
eq
(
0
)
context
'when data does not exist'
do
it
'does not persist'
do
expect
{
subject
}.
to
raise_error
(
'Can not persist empty data'
)
end
end
end
context
'when traces are archived'
do
let
(
:subject
)
do
project
.
builds
.
each
do
|
build
|
build
.
success!
context
'when data_store is fog'
do
let
(
:data_store
)
{
:fog
}
context
'when data exists'
do
let
(
:data
)
{
'Sample data in fog'
}
before
do
build_trace_chunk
.
send
(
:unsafe_set_data!
,
data
)
end
end
it_behaves_like
'deletes all build_trace_chunk and data in redis'
end
it
'does not change data store'
do
expect
(
build_trace_chunk
.
fog?
).
to
be_truthy
expect
(
Ci
::
BuildTraceChunks
::
Redis
.
new
.
data
(
build_trace_chunk
)).
to
be_nil
expect
(
Ci
::
BuildTraceChunks
::
Database
.
new
.
data
(
build_trace_chunk
)).
to
be_nil
expect
(
Ci
::
BuildTraceChunks
::
Fog
.
new
.
data
(
build_trace_chunk
)).
to
eq
(
data
)
context
'when project is destroyed'
do
let
(
:subject
)
do
project
.
destroy!
end
subject
it_behaves_like
'deletes all build_trace_chunk and data in redis'
expect
(
build_trace_chunk
.
fog?
).
to
be_truthy
expect
(
Ci
::
BuildTraceChunks
::
Redis
.
new
.
data
(
build_trace_chunk
)).
to
be_nil
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
end
end
end
## TODO:
# describe 'ExclusiveLock' do
# before do
# allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain) { nil }
# stub_const('Ci::BuildTraceChunk::WRITE_LOCK_RETRY', 1)
# end
# it 'raise an error' do
# expect { build_trace_chunk.append('ABC', 0) }.to raise_error('Failed to obtain write lock')
# end
# end
# describe 'deletes data in redis after a parent record destroyed' do
# let(:project) { create(:project) }
# before do
# pipeline = create(:ci_pipeline, project: project)
# 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)
# end
# shared_examples_for 'deletes all build_trace_chunk and data in redis' do
# it do
# Gitlab::Redis::SharedState.with do |redis|
# expect(redis.scan_each(match: "gitlab:ci:trace:*:chunks:*").to_a.size).to eq(3)
# end
# expect(described_class.count).to eq(3)
# subject
# expect(described_class.count).to eq(0)
# Gitlab::Redis::SharedState.with do |redis|
# expect(redis.scan_each(match: "gitlab:ci:trace:*:chunks:*").to_a.size).to eq(0)
# end
# end
# end
# context 'when traces are archived' do
# let(:subject) do
# project.builds.each do |build|
# build.success!
# end
# end
# it_behaves_like 'deletes all build_trace_chunk and data in redis'
# end
# context 'when project is destroyed' do
# let(:subject) do
# project.destroy!
# end
# it_behaves_like 'deletes all build_trace_chunk and data in redis'
# end
# end
end
spec/support/helpers/stub_object_storage.rb
View file @
24ba0989
...
...
@@ -20,6 +20,11 @@ module StubObjectStorage
::
Fog
::
Storage
.
new
(
uploader
.
object_store_credentials
).
tap
do
|
connection
|
begin
connection
.
directories
.
create
(
key:
remote_directory
)
# Cleanup remaining files
connection
.
directories
.
each
do
|
directory
|
directory
.
files
.
map
(
&
:destroy
)
end
rescue
Excon
::
Error
::
Conflict
end
end
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment