diff options
Diffstat (limited to 'app/models/ci/build_trace_chunks/fog.rb')
-rw-r--r-- | app/models/ci/build_trace_chunks/fog.rb | 35 |
1 files changed, 34 insertions, 1 deletions
diff --git a/app/models/ci/build_trace_chunks/fog.rb b/app/models/ci/build_trace_chunks/fog.rb index fab85fae33d..3bfac2b33c0 100644 --- a/app/models/ci/build_trace_chunks/fog.rb +++ b/app/models/ci/build_trace_chunks/fog.rb @@ -25,14 +25,36 @@ module Ci files.create(create_attributes(model, new_data)) end + # This is the sequence that causes append_data to be called: + # + # 1. Runner sends a PUT /api/v4/jobs/:id to indicate the job is canceled or finished. + # 2. UpdateBuildStateService#accept_build_state! persists all live job logs to object storage (or filesystem). + # 3. UpdateBuildStateService#accept_build_state! returns a 202 to the runner. + # 4. The runner continues to send PATCH requests with job logs until all logs have been sent and received. + # 5. If the last PATCH request arrives after the job log has been persisted, we + # retrieve the data from object storage to append the remaining lines. def append_data(model, new_data, offset) if offset > 0 truncated_data = data(model).to_s.byteslice(0, offset) - new_data = truncated_data + new_data + new_data = append_strings(truncated_data, new_data) end set_data(model, new_data) new_data.bytesize + rescue Encoding::CompatibilityError => e + Gitlab::ErrorTracking.track_and_raise_exception( + e, + build_id: model.build_id, + chunk_index: model.chunk_index, + chunk_start_offset: model.start_offset, + chunk_end_offset: model.end_offset, + chunk_size: model.size, + chunk_data_store: model.data_store, + offset: offset, + old_data_encoding: truncated_data.encoding.to_s, + new_data: new_data, + new_data_size: new_data.bytesize, + new_data_encoding: new_data.encoding.to_s) end def size(model) @@ -57,6 +79,17 @@ module Ci private + def append_strings(old_data, new_data) + if Feature.enabled?(:ci_job_trace_force_encode, default_enabled: :yaml) + # When object storage is in use, old_data may be retrieved in UTF-8. + old_data = old_data.force_encoding(Encoding::ASCII_8BIT) + # new_data should already be in ASCII-8BIT, but just in case it isn't, do this. + new_data = new_data.force_encoding(Encoding::ASCII_8BIT) + end + + old_data + new_data + end + def key(model) key_raw(model.build_id, model.chunk_index) end |