Class: Gitlab::Ci::Trace::ChunkedIO

Inherits:
Object
  • Object
show all
Defined in:
lib/gitlab/ci/trace/chunked_io.rb

Constant Summary collapse

CHUNK_SIZE =
::Ci::BuildTraceChunk::CHUNK_SIZE
FailedToGetChunkError =
Class.new(StandardError)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(build) {|_self| ... } ⇒ ChunkedIO

Returns a new instance of ChunkedIO.

Yields:

  • (_self)

Yield Parameters:


20
21
22
23
24
25
26
# File 'lib/gitlab/ci/trace/chunked_io.rb', line 20

def initialize(build, &block)
  @build = build
  @chunks_cache = []
  @tell = 0
  @size = calculate_size
  yield self if block_given?
end

Instance Attribute Details

#buildObject (readonly)

Returns the value of attribute build


14
15
16
# File 'lib/gitlab/ci/trace/chunked_io.rb', line 14

def build
  @build
end

#chunk_dataObject (readonly)

Returns the value of attribute chunk_data


16
17
18
# File 'lib/gitlab/ci/trace/chunked_io.rb', line 16

def chunk_data
  @chunk_data
end

#chunk_rangeObject (readonly)

Returns the value of attribute chunk_range


16
17
18
# File 'lib/gitlab/ci/trace/chunked_io.rb', line 16

def chunk_range
  @chunk_range
end

#sizeObject (readonly)

Returns the value of attribute size


15
16
17
# File 'lib/gitlab/ci/trace/chunked_io.rb', line 15

def size
  @size
end

#tellObject (readonly) Also known as: pos

Returns the value of attribute tell


15
16
17
# File 'lib/gitlab/ci/trace/chunked_io.rb', line 15

def tell
  @tell
end

Instance Method Details

#binmodeObject


32
33
34
# File 'lib/gitlab/ci/trace/chunked_io.rb', line 32

def binmode
  # no-op
end

#binmode?Boolean

Returns:

  • (Boolean)

36
37
38
# File 'lib/gitlab/ci/trace/chunked_io.rb', line 36

def binmode?
  true
end

#closeObject


28
29
30
# File 'lib/gitlab/ci/trace/chunked_io.rb', line 28

def close
  # no-op
end

#destroy!Object


168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/gitlab/ci/trace/chunked_io.rb', line 168

def destroy!
  # TODO: Remove this logging once we confirmed new live trace architecture is functional.
  # See https://gitlab.com/gitlab-com/gl-infra/infrastructure/issues/4667.
  unless build.has_archived_trace?
    Sidekiq.logger.warn(message: 'The job does not have archived trace but going to be destroyed.',
                        job_id: build.id)
  end

  trace_chunks.fast_destroy_all
  @tell = @size = 0
ensure
  invalidate_chunk_cache
end

#each_lineObject


62
63
64
65
66
67
68
69
# File 'lib/gitlab/ci/trace/chunked_io.rb', line 62

def each_line
  until eof?
    line = readline
    break if line.nil?

    yield(line)
  end
end

#eof?Boolean

Returns:

  • (Boolean)

58
59
60
# File 'lib/gitlab/ci/trace/chunked_io.rb', line 58

def eof?
  tell == size
end

#flushObject

rubocop: enable CodeReuse/ActiveRecord


160
161
162
# File 'lib/gitlab/ci/trace/chunked_io.rb', line 160

def flush
  # no-op
end

#present?Boolean

Returns:

  • (Boolean)

164
165
166
# File 'lib/gitlab/ci/trace/chunked_io.rb', line 164

def present?
  true
end

#read(length = nil, outbuf = nil) ⇒ Object


71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/gitlab/ci/trace/chunked_io.rb', line 71

def read(length = nil, outbuf = nil)
  out = []

  length ||= size - tell

  until length <= 0 || eof?
    data = chunk_slice_from_offset
    raise FailedToGetChunkError if data.empty?

    chunk_bytes = [CHUNK_SIZE - chunk_offset, length].min
    chunk_data_slice = data.byteslice(0, chunk_bytes)

    out << chunk_data_slice
    @tell += chunk_data_slice.bytesize
    length -= chunk_data_slice.bytesize
  end

  out = out.join

  # If outbuf is passed, we put the output into the buffer. This supports IO.copy_stream functionality
  if outbuf
    outbuf.replace(out)
  end

  out
end

#readlineObject


98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/gitlab/ci/trace/chunked_io.rb', line 98

def readline
  out = []

  until eof?
    data = chunk_slice_from_offset
    raise FailedToGetChunkError if data.empty?

    new_line = data.index("\n")

    if !new_line.nil?
      raw_data = data[0..new_line]
      out << raw_data
      @tell += raw_data.bytesize
      break
    else
      out << data
      @tell += data.bytesize
    end
  end

  out.join
end

#seek(pos, where = IO::SEEK_SET) ⇒ Object

Raises:

  • (ArgumentError)

40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/gitlab/ci/trace/chunked_io.rb', line 40

def seek(pos, where = IO::SEEK_SET)
  new_pos =
    case where
    when IO::SEEK_END
      size + pos
    when IO::SEEK_SET
      pos
    when IO::SEEK_CUR
      tell + pos
    else
      -1
    end

  raise ArgumentError, 'new position is outside of file' if new_pos < 0 || new_pos > size

  @tell = new_pos
end

#truncate(offset) ⇒ Object

rubocop: disable CodeReuse/ActiveRecord


143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/gitlab/ci/trace/chunked_io.rb', line 143

def truncate(offset)
  raise ArgumentError, 'Outside of file' if offset > size || offset < 0
  return if offset == size # Skip the following process as it doesn't affect anything

  @tell = offset
  @size = offset

  # remove all next chunks
  trace_chunks.where('chunk_index > ?', chunk_index).fast_destroy_all

  # truncate current chunk
  current_chunk.truncate(chunk_offset)
ensure
  invalidate_chunk_cache
end

#write(data) ⇒ Object


121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/gitlab/ci/trace/chunked_io.rb', line 121

def write(data)
  start_pos = tell

  while tell < start_pos + data.bytesize
    # get slice from current offset till the end where it falls into chunk
    chunk_bytes = CHUNK_SIZE - chunk_offset
    data_slice = data.byteslice(tell - start_pos, chunk_bytes)

    # append data to chunk, overwriting from that point
    ensure_chunk.append(data_slice, chunk_offset)

    # move offsets within buffer
    @tell += data_slice.bytesize
    @size = [size, tell].max
  end

  tell - start_pos
ensure
  invalidate_chunk_cache
end