Class: Fluent::Plugin::FileBuffer

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

Constant Summary collapse

DEFAULT_CHUNK_LIMIT_SIZE =

256MB

256 * 1024 * 1024
DEFAULT_TOTAL_LIMIT_SIZE =

64GB, same with v0.12 (TimeSlicedOutput + buf_file)

64 * 1024 * 1024 * 1024

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

#initializeFileBuffer

Returns a new instance of FileBuffer.



45
46
47
48
49
50
51
52
# File 'lib/fluent/plugin/buf_file.rb', line 45

def initialize
  super
  @symlink_path = nil
  @multi_workers_available = false
  @additional_resume_path = nil
  @buffer_path = nil
  @variable_store = nil
end

Instance Method Details

#configure(conf) ⇒ Object



54
55
56
57
58
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
# File 'lib/fluent/plugin/buf_file.rb', line 54

def configure(conf)
  super

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

  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

  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 ConfigError, "Other '#{type_using_this_path}' plugin already use same buffer path: type = #{type_of_owner}, buffer path = #{@path}"
  end

  @buffer_path = @path
  @variable_store[@buffer_path] = type_of_owner

  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, "buffer.*#{@path_suffix}")
    else
      @path = File.join(@path, "worker#{fluentd_worker_id}", "buffer.*#{@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), "buffer.*#{@path_suffix}")
      end
    end
    @multi_workers_available = true
  else # specified path is file path
    if File.basename(@path).include?('.*.')
      # valid file 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

  if @dir_permission
    @dir_permission = @dir_permission.to_i(8) if @dir_permission.is_a?(String)
  else
    @dir_permission = system_config.dir_permission || Fluent::DEFAULT_DIR_PERMISSION
  end
end

#generate_chunk(metadata) ⇒ Object



205
206
207
208
209
210
211
212
# File 'lib/fluent/plugin/buf_file.rb', line 205

def generate_chunk()
  # FileChunk generates real path with unique_id
  perm = @file_permission || system_config.file_permission
  chunk = Fluent::Plugin::Buffer::FileChunk.new(, @path, :create, perm: perm, compress: @compress)
  log.debug "Created new chunk", chunk_id: dump_unique_id_hex(chunk.unique_id), metadata: 

  return chunk
end

#handle_broken_files(path, mode, e) ⇒ Object



214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/fluent/plugin/buf_file.rb', line 214

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::FileChunk.unique_id_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, path + '.meta') rescue nil
end

#multi_workers_ready?Boolean

This method is called only when multi worker is configured

Returns:

  • (Boolean)


114
115
116
117
118
119
# File 'lib/fluent/plugin/buf_file.rb', line 114

def multi_workers_ready?
  unless @multi_workers_available
    log.error "file 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)


135
136
137
# File 'lib/fluent/plugin/buf_file.rb', line 135

def persistent?
  true
end

#resumeObject



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
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
# File 'lib/fluent/plugin/buf_file.rb', line 139

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 overwritten by resuming .meta file content
                       # so it should not added into @metadata_list for now
    mode = Fluent::Plugin::Buffer::FileChunk.assume_chunk_state(path)
    if mode == :unknown
      log.debug "unknown state chunk found", path: path
      next
    end

    begin
      chunk = Fluent::Plugin::Buffer::FileChunk.new(m, path, mode, compress: @compress) # file chunk resumes contents of metadata
    rescue Fluent::Plugin::Buffer::FileChunk::FileChunkError => e
      exist_broken_file = true
      handle_broken_files(path, mode, e)
      next
    end

    case chunk.state
    when :staged
      # unstaged chunk created at Buffer#write_step_by_step is identified as the staged chunk here because FileChunk#assume_chunk_state checks only the file name.
      # https://github.com/fluent/fluentd/blob/9d113029d4550ce576d8825bfa9612aa3e55bff0/lib/fluent/plugin/buffer.rb#L663
      # This case can happen when fluentd process is killed by signal or other reasons between creating unstaged chunks and changing them to staged mode in Buffer#write
      # these chunks(unstaged chunks) has shared the same metadata
      # So perform enqueue step again https://github.com/fluent/fluentd/blob/9d113029d4550ce576d8825bfa9612aa3e55bff0/lib/fluent/plugin/buffer.rb#L364
      if chunk_size_full?(chunk) || stage.key?(chunk.)
        chunk..seq = 0 # metadata.seq should be 0 for counting @queued_num
        queue << chunk.enqueued!
      else
        stage[chunk.] = chunk
      end
    when :queued
      queue << chunk
    end
  end

  queue.sort_by!{ |chunk| chunk.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



121
122
123
124
125
# File 'lib/fluent/plugin/buf_file.rb', line 121

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

  super
end

#stopObject



127
128
129
130
131
132
133
# File 'lib/fluent/plugin/buf_file.rb', line 127

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

  super
end