Class: Fluent::Plugin::FileSingleBuffer

Inherits:
Buffer
  • Object
show all
Includes:
SystemConfig::Mixin
Defined in:
lib/fluent/plugin/buf_file_single.rb

Constant Summary collapse

DEFAULT_CHUNK_LIMIT_SIZE =

256MB

256 * 1024 * 1024
DEFAULT_TOTAL_LIMIT_SIZE =

64GB

64 * 1024 * 1024 * 1024
PATH_SUFFIX =
".#{Fluent::Plugin::Buffer::FileSingleChunk::PATH_EXT}"

Constants inherited from Buffer

Buffer::DEFAULT_CHUNK_FULL_THRESHOLD, Buffer::MINIMUM_APPEND_ATTEMPT_RECORDS, Buffer::STATS_KEYS

Constants included from Configurable

Configurable::CONFIG_TYPE_REGISTRY

Instance Attribute Summary

Attributes inherited from Buffer

#available_buffer_space_ratios_metrics, #dequeued, #newest_timekey_metrics, #oldest_timekey_metrics, #queue, #queue_length_metrics, #queue_size_metrics, #queued_num, #stage, #stage_length_metrics, #stage_size_metrics, #total_queued_size_metrics

Attributes inherited from Base

#under_plugin_development

Instance Method Summary collapse

Methods included from SystemConfig::Mixin

#system_config, #system_config_override

Methods inherited from Buffer

#backup, #chunk_size_full?, #chunk_size_over?, #clear_queue!, #close, #dequeue_chunk, #enable_update_timekeys, #enqueue_all, #enqueue_chunk, #enqueue_unstaged_chunk, #metadata, #new_metadata, #purge_chunk, #queue_full?, #queue_size, #queue_size=, #queued?, #queued_records, #stage_size, #stage_size=, #statistics, #storable?, #takeback_chunk, #terminate, #timekeys, #update_timekeys, #write, #write_once, #write_step_by_step

Methods included from Fluent::PluginHelper::Mixin

included

Methods included from Fluent::PluginId

#plugin_id, #plugin_id_configured?, #plugin_id_for_test?, #plugin_root_dir

Methods included from UniqueId::Mixin

#dump_unique_id_hex, #generate_unique_id

Methods included from OwnedByMixin

#log, #owner, #owner=

Methods inherited from Base

#acquire_worker_lock, #after_shutdown, #after_shutdown?, #after_start, #after_started?, #before_shutdown, #before_shutdown?, #called_in_test?, #close, #closed?, #configured?, #context_router, #context_router=, #fluentd_worker_id, #get_lock_path, #has_router?, #inspect, #plugin_root_dir, #reloadable_plugin?, #shutdown, #shutdown?, #started?, #stopped?, #string_safe_encoding, #terminate, #terminated?

Methods included from Configurable

#config, #configure_proxy_generate, #configured_section_create, included, lookup_type, register_type

Constructor Details

#initializeFileSingleBuffer

Returns a new instance of FileSingleBuffer.



51
52
53
54
55
56
57
# File 'lib/fluent/plugin/buf_file_single.rb', line 51

def initialize
  super

  @multi_workers_available = false
  @additional_resume_path = nil
  @variable_store = nil
end

Instance Method Details

#configure(conf) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/fluent/plugin/buf_file_single.rb', line 59

def configure(conf)
  super

  @variable_store = Fluent::VariableStore.fetch_or_build(:buf_file_single)

  if @chunk_format == :auto
    @chunk_format = owner.formatted_to_msgpack_binary? ? :msgpack : :text
  end

  @key_in_path = nil
  if owner.chunk_keys.empty?
    log.debug "use event tag for buffer key"
  else
    if owner.chunk_key_tag
      raise Fluent::ConfigError, "chunk keys must be tag or one field"
    elsif owner.chunk_keys.size > 1
      raise Fluent::ConfigError, "2 or more chunk keys is not allowed"
    else
      @key_in_path = owner.chunk_keys.first.to_sym
    end
  end

  multi_workers_configured = owner.system_config.workers > 1
  using_plugin_root_dir = false
  unless @path
    if root_dir = owner.plugin_root_dir
      @path = File.join(root_dir, 'buffer')
      using_plugin_root_dir = true # plugin_root_dir path contains worker id
    else
      raise Fluent::ConfigError, "buffer path is not configured. specify 'path' in <buffer>"
    end
  end

  specified_directory_exists = File.exist?(@path) && File.directory?(@path)
  unexisting_path_for_directory = !File.exist?(@path) && !@path.include?('.*')

  if specified_directory_exists || unexisting_path_for_directory # directory
    if using_plugin_root_dir || !multi_workers_configured
      @path = File.join(@path, "fsb.*#{PATH_SUFFIX}")
    else
      @path = File.join(@path, "worker#{fluentd_worker_id}", "fsb.*#{PATH_SUFFIX}")
      if fluentd_worker_id == 0
        # worker 0 always checks unflushed buffer chunks to be resumed (might be created while non-multi-worker configuration)
        @additional_resume_path = File.join(File.expand_path("../../", @path), "fsb.*#{PATH_SUFFIX}")
      end
    end
    @multi_workers_available = true
  else # specified path is file path
    if File.basename(@path).include?('.*.')
      new_path = File.join(File.dirname(@path), "fsb.*#{PATH_SUFFIX}")
      log.warn "file_single doesn't allow user specified 'prefix.*.suffix' style path. Use '#{new_path}' for file instead: #{@path}"
      @path = new_path
    elsif File.basename(@path).end_with?('.*')
      @path = @path + PATH_SUFFIX
    else
      # existing file will be ignored
      @path = @path + ".*#{PATH_SUFFIX}"
    end
    @multi_workers_available = false
  end

  type_of_owner = Plugin.lookup_type_from_class(@_owner.class)
  if @variable_store.has_key?(@path) && !called_in_test?
    type_using_this_path = @variable_store[@path]
    raise Fluent::ConfigError, "Other '#{type_using_this_path}' plugin already uses same buffer path: type = #{type_of_owner}, buffer path = #{@path}"
  end

  @variable_store[@path] = type_of_owner
  @dir_permission = if @dir_permission
                      @dir_permission.to_i(8)
                    else
                      system_config.dir_permission || Fluent::DEFAULT_DIR_PERMISSION
                    end
end

#generate_chunk(metadata) ⇒ Object



216
217
218
219
220
221
222
223
224
# File 'lib/fluent/plugin/buf_file_single.rb', line 216

def generate_chunk()
  # FileChunk generates real path with unique_id
  perm = @file_permission || system_config.file_permission
  chunk = Fluent::Plugin::Buffer::FileSingleChunk.new(, @path, :create, @key_in_path, perm: perm, compress: @compress)

  log.debug "Created new chunk", chunk_id: dump_unique_id_hex(chunk.unique_id), metadata: 

  chunk
end

#handle_broken_files(path, mode, e) ⇒ Object



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/fluent/plugin/buf_file_single.rb', line 226

def handle_broken_files(path, mode, e)
  log.error "found broken chunk file during resume.", :path => path, :mode => mode, :err_msg => e.message
  unique_id, _ = Fluent::Plugin::Buffer::FileSingleChunk.unique_id_and_key_from_path(path)
  backup(unique_id) { |f|
    File.open(path, 'rb') { |chunk|
      chunk.set_encoding(Encoding::ASCII_8BIT)
      chunk.sync = true
      chunk.binmode
      IO.copy_stream(chunk, f)
    }
  }
rescue => error
  log.error "backup failed. Delete corresponding files.", :err_msg => error.message
ensure
  log.warn "disable_chunk_backup is true. #{dump_unique_id_hex(unique_id)} chunk is thrown away." if @disable_chunk_backup
  File.unlink(path) rescue nil
end

#multi_workers_ready?Boolean

This method is called only when multi worker is configured

Returns:

  • (Boolean)


135
136
137
138
139
140
# File 'lib/fluent/plugin/buf_file_single.rb', line 135

def multi_workers_ready?
  unless @multi_workers_available
    log.error "file_single buffer with multi workers should be configured to use directory 'path', or system root_dir and plugin id"
  end
  @multi_workers_available
end

#persistent?Boolean

Returns:

  • (Boolean)


156
157
158
# File 'lib/fluent/plugin/buf_file_single.rb', line 156

def persistent?
  true
end

#resumeObject



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/fluent/plugin/buf_file_single.rb', line 160

def resume
  stage = {}
  queue = []
  exist_broken_file = false

  patterns = [@path]
  patterns.unshift @additional_resume_path if @additional_resume_path
  Dir.glob(escaped_patterns(patterns)) do |path|
    next unless File.file?(path)

    if owner.respond_to?(:buffer_config) && owner.buffer_config&.flush_at_shutdown
      # When `flush_at_shutdown` is `true`, the remaining chunk files during resuming are possibly broken
      # since there may be a power failure or similar failure.
      log.warn { "restoring buffer file: path = #{path}" }
    else
      log.debug { "restoring buffer file: path = #{path}" }
    end

    m = () # this metadata will be updated in FileSingleChunk.new
    mode = Fluent::Plugin::Buffer::FileSingleChunk.assume_chunk_state(path)
    if mode == :unknown
      log.debug "unknown state chunk found", path: path
      next
    end

    begin
      chunk = Fluent::Plugin::Buffer::FileSingleChunk.new(m, path, mode, @key_in_path, compress: @compress)
      chunk.restore_size(@chunk_format) if @calc_num_records
    rescue Fluent::Plugin::Buffer::FileSingleChunk::FileChunkError => e
      exist_broken_file = true
      handle_broken_files(path, mode, e)
      next
    end

    case chunk.state
    when :staged
      stage[chunk.] = chunk
    when :queued
      queue << chunk
    end
  end

  queue.sort_by!(&:modified_at)

  # If one of the files is corrupted, other files may also be corrupted and be undetected.
  # The time priods of each chunk are helpful to check the data.
  if exist_broken_file
    log.info "Since a broken chunk file was found, it is possible that other files remaining at the time of resuming were also broken. Here is the list of the files."
    (stage.values + queue).each { |chunk|
      log.info "  #{chunk.path}:", :created_at => chunk.created_at, :modified_at => chunk.modified_at
    }
  end

  return stage, queue
end

#startObject



142
143
144
145
146
# File 'lib/fluent/plugin/buf_file_single.rb', line 142

def start
  FileUtils.mkdir_p(File.dirname(@path), mode: @dir_permission)

  super
end

#stopObject



148
149
150
151
152
153
154
# File 'lib/fluent/plugin/buf_file_single.rb', line 148

def stop
  if @variable_store
    @variable_store.delete(@path)
  end

  super
end