2018-03-26 07:45:18 -04:00
|
|
|
module Ci
|
|
|
|
class JobTraceChunk < ActiveRecord::Base
|
|
|
|
extend Gitlab::Ci::Model
|
|
|
|
|
|
|
|
belongs_to :job, class_name: "Ci::Build", foreign_key: :job_id
|
2018-04-04 06:19:17 -04:00
|
|
|
|
|
|
|
after_destroy :redis_delete_data, if: :redis?
|
|
|
|
|
|
|
|
default_value_for :data_store, :redis
|
|
|
|
|
2018-04-05 07:39:35 -04:00
|
|
|
CHUNK_SIZE = 128.kilobytes
|
2018-04-06 06:30:23 -04:00
|
|
|
CHUNK_REDIS_TTL = 1.day
|
2018-04-04 06:19:17 -04:00
|
|
|
|
|
|
|
enum data_store: {
|
|
|
|
redis: 1,
|
2018-04-05 11:57:05 -04:00
|
|
|
db: 2
|
2018-04-04 06:19:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
def data
|
2018-04-05 11:57:05 -04:00
|
|
|
if redis?
|
2018-04-04 06:19:17 -04:00
|
|
|
redis_data
|
2018-04-05 11:57:05 -04:00
|
|
|
elsif db?
|
2018-04-04 06:19:17 -04:00
|
|
|
raw_data
|
|
|
|
else
|
|
|
|
raise 'Unsupported data store'
|
2018-04-05 11:19:41 -04:00
|
|
|
end&.force_encoding(Encoding::BINARY)
|
2018-04-04 06:19:17 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def set_data(value)
|
2018-04-06 06:30:23 -04:00
|
|
|
raise ArgumentError, 'too much data' if value.bytesize > CHUNK_SIZE
|
2018-04-04 06:19:17 -04:00
|
|
|
|
2018-04-05 11:57:05 -04:00
|
|
|
if redis?
|
2018-04-04 06:19:17 -04:00
|
|
|
redis_set_data(value)
|
2018-04-05 11:57:05 -04:00
|
|
|
elsif db?
|
2018-04-04 06:19:17 -04:00
|
|
|
self.raw_data = value
|
|
|
|
else
|
|
|
|
raise 'Unsupported data store'
|
|
|
|
end
|
|
|
|
|
2018-04-05 05:59:07 -04:00
|
|
|
save! if changed?
|
2018-04-04 06:19:17 -04:00
|
|
|
schedule_to_db if fullfilled?
|
|
|
|
end
|
|
|
|
|
|
|
|
def truncate(offset = 0)
|
|
|
|
self.append("", offset)
|
|
|
|
end
|
|
|
|
|
|
|
|
def append(new_data, offset)
|
2018-04-06 06:30:23 -04:00
|
|
|
current_data = self.data.to_s
|
|
|
|
raise ArgumentError, 'Offset is out of bound' if offset > current_data.bytesize || offset < 0
|
|
|
|
raise ArgumentError, 'Outside of chunk size' if CHUNK_SIZE < offset + new_data.bytesize
|
2018-04-04 06:19:17 -04:00
|
|
|
|
|
|
|
self.set_data(current_data.byteslice(0, offset) + new_data)
|
|
|
|
end
|
|
|
|
|
|
|
|
def size
|
|
|
|
data&.bytesize.to_i
|
|
|
|
end
|
|
|
|
|
|
|
|
def start_offset
|
|
|
|
chunk_index * CHUNK_SIZE
|
|
|
|
end
|
|
|
|
|
|
|
|
def end_offset
|
|
|
|
start_offset + size
|
|
|
|
end
|
|
|
|
|
|
|
|
def range
|
|
|
|
(start_offset...end_offset)
|
|
|
|
end
|
|
|
|
|
|
|
|
def use_database!
|
|
|
|
return if db?
|
2018-04-05 05:59:07 -04:00
|
|
|
return unless size > 0
|
2018-04-04 06:19:17 -04:00
|
|
|
|
|
|
|
self.update!(raw_data: data, data_store: :db)
|
|
|
|
redis_delete_data
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def schedule_to_db
|
|
|
|
return if db?
|
|
|
|
|
2018-04-05 08:15:18 -04:00
|
|
|
SwapTraceChunkWorker.perform_async(id)
|
2018-04-04 06:19:17 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def fullfilled?
|
|
|
|
size == CHUNK_SIZE
|
|
|
|
end
|
|
|
|
|
|
|
|
def redis_data
|
|
|
|
Gitlab::Redis::SharedState.with do |redis|
|
|
|
|
redis.get(redis_key)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def redis_set_data(data)
|
|
|
|
Gitlab::Redis::SharedState.with do |redis|
|
|
|
|
redis.set(redis_key, data, ex: CHUNK_REDIS_TTL)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def redis_delete_data
|
|
|
|
Gitlab::Redis::SharedState.with do |redis|
|
|
|
|
redis.del(redis_key)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def redis_key
|
|
|
|
"gitlab:ci:trace:#{job_id}:chunks:#{chunk_index}"
|
|
|
|
end
|
2018-03-26 07:45:18 -04:00
|
|
|
end
|
|
|
|
end
|