fluentd 1.20.0-aarch64-mingw-ucrt
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rubocop.yml +116 -0
- data/ADOPTERS.md +5 -0
- data/AGENTS.md +46 -0
- data/AUTHORS +2 -0
- data/CHANGELOG.md +3324 -0
- data/CONTRIBUTING.md +45 -0
- data/GOVERNANCE.md +55 -0
- data/GithubWorkflow.md +78 -0
- data/LICENSE +202 -0
- data/MAINTAINERS.md +19 -0
- data/README.md +73 -0
- data/Rakefile +89 -0
- data/SECURITY.md +47 -0
- data/bin/fluent-binlog-reader +7 -0
- data/bin/fluent-ca-generate +6 -0
- data/bin/fluent-cap-ctl +7 -0
- data/bin/fluent-cat +5 -0
- data/bin/fluent-ctl +7 -0
- data/bin/fluent-debug +5 -0
- data/bin/fluent-gem +9 -0
- data/bin/fluent-plugin-config-format +5 -0
- data/bin/fluent-plugin-generate +5 -0
- data/bin/fluentd +15 -0
- data/code-of-conduct.md +3 -0
- data/docs/SECURITY_AUDIT.pdf +0 -0
- data/example/copy_roundrobin.conf +39 -0
- data/example/counter.conf +18 -0
- data/example/filter_stdout.conf +22 -0
- data/example/in_forward.conf +14 -0
- data/example/in_forward_client.conf +37 -0
- data/example/in_forward_shared_key.conf +15 -0
- data/example/in_forward_tls.conf +14 -0
- data/example/in_forward_users.conf +24 -0
- data/example/in_forward_workers.conf +21 -0
- data/example/in_http.conf +16 -0
- data/example/in_out_forward.conf +17 -0
- data/example/in_sample_blocks.conf +17 -0
- data/example/in_sample_with_compression.conf +23 -0
- data/example/in_syslog.conf +15 -0
- data/example/in_tail.conf +14 -0
- data/example/in_tcp.conf +13 -0
- data/example/in_udp.conf +13 -0
- data/example/logevents.conf +25 -0
- data/example/multi_filters.conf +61 -0
- data/example/out_copy.conf +20 -0
- data/example/out_exec_filter.conf +42 -0
- data/example/out_file.conf +13 -0
- data/example/out_forward.conf +35 -0
- data/example/out_forward_buf_file.conf +23 -0
- data/example/out_forward_client.conf +109 -0
- data/example/out_forward_heartbeat_none.conf +16 -0
- data/example/out_forward_sd.conf +17 -0
- data/example/out_forward_shared_key.conf +36 -0
- data/example/out_forward_tls.conf +18 -0
- data/example/out_forward_users.conf +65 -0
- data/example/out_null.conf +36 -0
- data/example/sd.yaml +8 -0
- data/example/secondary_file.conf +42 -0
- data/example/suppress_config_dump.conf +7 -0
- data/example/v0_12_filter.conf +78 -0
- data/example/v1_literal_example.conf +36 -0
- data/example/worker_section.conf +36 -0
- data/fluent.conf +139 -0
- data/lib/fluent/agent.rb +168 -0
- data/lib/fluent/capability.rb +87 -0
- data/lib/fluent/clock.rb +66 -0
- data/lib/fluent/command/binlog_reader.rb +244 -0
- data/lib/fluent/command/bundler_injection.rb +45 -0
- data/lib/fluent/command/ca_generate.rb +184 -0
- data/lib/fluent/command/cap_ctl.rb +174 -0
- data/lib/fluent/command/cat.rb +365 -0
- data/lib/fluent/command/ctl.rb +180 -0
- data/lib/fluent/command/debug.rb +103 -0
- data/lib/fluent/command/fluentd.rb +385 -0
- data/lib/fluent/command/plugin_config_formatter.rb +308 -0
- data/lib/fluent/command/plugin_generator.rb +365 -0
- data/lib/fluent/compat/call_super_mixin.rb +76 -0
- data/lib/fluent/compat/detach_process_mixin.rb +33 -0
- data/lib/fluent/compat/exec_util.rb +144 -0
- data/lib/fluent/compat/file_util.rb +54 -0
- data/lib/fluent/compat/filter.rb +68 -0
- data/lib/fluent/compat/formatter.rb +117 -0
- data/lib/fluent/compat/formatter_utils.rb +85 -0
- data/lib/fluent/compat/handle_tag_and_time_mixin.rb +62 -0
- data/lib/fluent/compat/handle_tag_name_mixin.rb +53 -0
- data/lib/fluent/compat/input.rb +49 -0
- data/lib/fluent/compat/output.rb +721 -0
- data/lib/fluent/compat/output_chain.rb +60 -0
- data/lib/fluent/compat/parser.rb +310 -0
- data/lib/fluent/compat/parser_utils.rb +40 -0
- data/lib/fluent/compat/propagate_default.rb +62 -0
- data/lib/fluent/compat/record_filter_mixin.rb +34 -0
- data/lib/fluent/compat/set_tag_key_mixin.rb +50 -0
- data/lib/fluent/compat/set_time_key_mixin.rb +69 -0
- data/lib/fluent/compat/socket_util.rb +165 -0
- data/lib/fluent/compat/string_util.rb +34 -0
- data/lib/fluent/compat/structured_format_mixin.rb +26 -0
- data/lib/fluent/compat/type_converter.rb +90 -0
- data/lib/fluent/config/basic_parser.rb +122 -0
- data/lib/fluent/config/configure_proxy.rb +424 -0
- data/lib/fluent/config/dsl.rb +152 -0
- data/lib/fluent/config/element.rb +265 -0
- data/lib/fluent/config/error.rb +44 -0
- data/lib/fluent/config/literal_parser.rb +305 -0
- data/lib/fluent/config/parser.rb +119 -0
- data/lib/fluent/config/section.rb +272 -0
- data/lib/fluent/config/types.rb +257 -0
- data/lib/fluent/config/v1_parser.rb +200 -0
- data/lib/fluent/config/yaml_parser/fluent_value.rb +47 -0
- data/lib/fluent/config/yaml_parser/loader.rb +124 -0
- data/lib/fluent/config/yaml_parser/parser.rb +179 -0
- data/lib/fluent/config/yaml_parser/section_builder.rb +107 -0
- data/lib/fluent/config/yaml_parser.rb +56 -0
- data/lib/fluent/config.rb +89 -0
- data/lib/fluent/configurable.rb +202 -0
- data/lib/fluent/counter/base_socket.rb +44 -0
- data/lib/fluent/counter/client.rb +297 -0
- data/lib/fluent/counter/error.rb +86 -0
- data/lib/fluent/counter/mutex_hash.rb +163 -0
- data/lib/fluent/counter/server.rb +273 -0
- data/lib/fluent/counter/store.rb +205 -0
- data/lib/fluent/counter/validator.rb +145 -0
- data/lib/fluent/counter.rb +23 -0
- data/lib/fluent/daemon.rb +14 -0
- data/lib/fluent/daemonizer.rb +88 -0
- data/lib/fluent/engine.rb +269 -0
- data/lib/fluent/env.rb +45 -0
- data/lib/fluent/error.rb +37 -0
- data/lib/fluent/event.rb +369 -0
- data/lib/fluent/event_router.rb +315 -0
- data/lib/fluent/ext_monitor_require.rb +28 -0
- data/lib/fluent/file_wrapper.rb +137 -0
- data/lib/fluent/filter.rb +21 -0
- data/lib/fluent/fluent_log_event_router.rb +139 -0
- data/lib/fluent/formatter.rb +23 -0
- data/lib/fluent/input.rb +21 -0
- data/lib/fluent/label.rb +46 -0
- data/lib/fluent/load.rb +33 -0
- data/lib/fluent/log/console_adapter.rb +66 -0
- data/lib/fluent/log.rb +775 -0
- data/lib/fluent/match.rb +187 -0
- data/lib/fluent/mixin.rb +31 -0
- data/lib/fluent/msgpack_factory.rb +111 -0
- data/lib/fluent/oj_options.rb +61 -0
- data/lib/fluent/output.rb +29 -0
- data/lib/fluent/output_chain.rb +23 -0
- data/lib/fluent/parser.rb +23 -0
- data/lib/fluent/plugin/bare_output.rb +88 -0
- data/lib/fluent/plugin/base.rb +219 -0
- data/lib/fluent/plugin/buf_file.rb +256 -0
- data/lib/fluent/plugin/buf_file_single.rb +272 -0
- data/lib/fluent/plugin/buf_memory.rb +34 -0
- data/lib/fluent/plugin/buffer/chunk.rb +313 -0
- data/lib/fluent/plugin/buffer/file_chunk.rb +432 -0
- data/lib/fluent/plugin/buffer/file_single_chunk.rb +325 -0
- data/lib/fluent/plugin/buffer/memory_chunk.rb +91 -0
- data/lib/fluent/plugin/buffer.rb +1063 -0
- data/lib/fluent/plugin/compressable.rb +87 -0
- data/lib/fluent/plugin/exec_util.rb +22 -0
- data/lib/fluent/plugin/extractor.rb +132 -0
- data/lib/fluent/plugin/file_util.rb +22 -0
- data/lib/fluent/plugin/filter.rb +119 -0
- data/lib/fluent/plugin/filter_grep.rb +189 -0
- data/lib/fluent/plugin/filter_parser.rb +124 -0
- data/lib/fluent/plugin/filter_record_transformer.rb +325 -0
- data/lib/fluent/plugin/filter_stdout.rb +53 -0
- data/lib/fluent/plugin/formatter.rb +75 -0
- data/lib/fluent/plugin/formatter_csv.rb +92 -0
- data/lib/fluent/plugin/formatter_hash.rb +35 -0
- data/lib/fluent/plugin/formatter_json.rb +62 -0
- data/lib/fluent/plugin/formatter_ltsv.rb +44 -0
- data/lib/fluent/plugin/formatter_msgpack.rb +33 -0
- data/lib/fluent/plugin/formatter_out_file.rb +55 -0
- data/lib/fluent/plugin/formatter_single_value.rb +36 -0
- data/lib/fluent/plugin/formatter_stdout.rb +76 -0
- data/lib/fluent/plugin/formatter_tsv.rb +40 -0
- data/lib/fluent/plugin/in_debug_agent.rb +71 -0
- data/lib/fluent/plugin/in_dummy.rb +18 -0
- data/lib/fluent/plugin/in_exec.rb +124 -0
- data/lib/fluent/plugin/in_forward.rb +489 -0
- data/lib/fluent/plugin/in_gc_stat.rb +72 -0
- data/lib/fluent/plugin/in_http.rb +737 -0
- data/lib/fluent/plugin/in_monitor_agent.rb +406 -0
- data/lib/fluent/plugin/in_object_space.rb +93 -0
- data/lib/fluent/plugin/in_sample.rb +148 -0
- data/lib/fluent/plugin/in_syslog.rb +346 -0
- data/lib/fluent/plugin/in_tail/compatibility.rb +111 -0
- data/lib/fluent/plugin/in_tail/dir_watcher.rb +713 -0
- data/lib/fluent/plugin/in_tail/group_watch.rb +204 -0
- data/lib/fluent/plugin/in_tail/io_handler.rb +391 -0
- data/lib/fluent/plugin/in_tail/line_feeder.rb +230 -0
- data/lib/fluent/plugin/in_tail/position_file.rb +269 -0
- data/lib/fluent/plugin/in_tail/stat_watcher.rb +52 -0
- data/lib/fluent/plugin/in_tail/tail_watcher.rb +276 -0
- data/lib/fluent/plugin/in_tail/worker_pool.rb +298 -0
- data/lib/fluent/plugin/in_tail.rb +917 -0
- data/lib/fluent/plugin/in_tcp.rb +230 -0
- data/lib/fluent/plugin/in_udp.rb +101 -0
- data/lib/fluent/plugin/in_unix.rb +197 -0
- data/lib/fluent/plugin/input.rb +71 -0
- data/lib/fluent/plugin/metrics.rb +119 -0
- data/lib/fluent/plugin/metrics_local.rb +96 -0
- data/lib/fluent/plugin/multi_output.rb +179 -0
- data/lib/fluent/plugin/out_buffer.rb +40 -0
- data/lib/fluent/plugin/out_copy.rb +120 -0
- data/lib/fluent/plugin/out_exec.rb +105 -0
- data/lib/fluent/plugin/out_exec_filter.rb +319 -0
- data/lib/fluent/plugin/out_file.rb +371 -0
- data/lib/fluent/plugin/out_forward/ack_handler.rb +176 -0
- data/lib/fluent/plugin/out_forward/connection_manager.rb +131 -0
- data/lib/fluent/plugin/out_forward/error.rb +28 -0
- data/lib/fluent/plugin/out_forward/failure_detector.rb +84 -0
- data/lib/fluent/plugin/out_forward/handshake_protocol.rb +125 -0
- data/lib/fluent/plugin/out_forward/load_balancer.rb +114 -0
- data/lib/fluent/plugin/out_forward/socket_cache.rb +177 -0
- data/lib/fluent/plugin/out_forward.rb +865 -0
- data/lib/fluent/plugin/out_http.rb +419 -0
- data/lib/fluent/plugin/out_null.rb +74 -0
- data/lib/fluent/plugin/out_relabel.rb +32 -0
- data/lib/fluent/plugin/out_roundrobin.rb +84 -0
- data/lib/fluent/plugin/out_secondary_file.rb +148 -0
- data/lib/fluent/plugin/out_stdout.rb +81 -0
- data/lib/fluent/plugin/out_stream.rb +130 -0
- data/lib/fluent/plugin/output.rb +1688 -0
- data/lib/fluent/plugin/owned_by_mixin.rb +41 -0
- data/lib/fluent/plugin/parser.rb +274 -0
- data/lib/fluent/plugin/parser_apache.rb +28 -0
- data/lib/fluent/plugin/parser_apache2.rb +88 -0
- data/lib/fluent/plugin/parser_apache_error.rb +26 -0
- data/lib/fluent/plugin/parser_csv.rb +124 -0
- data/lib/fluent/plugin/parser_json.rb +118 -0
- data/lib/fluent/plugin/parser_ltsv.rb +49 -0
- data/lib/fluent/plugin/parser_msgpack.rb +72 -0
- data/lib/fluent/plugin/parser_multiline.rb +152 -0
- data/lib/fluent/plugin/parser_nginx.rb +28 -0
- data/lib/fluent/plugin/parser_none.rb +36 -0
- data/lib/fluent/plugin/parser_regexp.rb +68 -0
- data/lib/fluent/plugin/parser_syslog.rb +543 -0
- data/lib/fluent/plugin/parser_tsv.rb +47 -0
- data/lib/fluent/plugin/sd_file.rb +157 -0
- data/lib/fluent/plugin/sd_srv.rb +135 -0
- data/lib/fluent/plugin/sd_static.rb +58 -0
- data/lib/fluent/plugin/service_discovery.rb +65 -0
- data/lib/fluent/plugin/socket_util.rb +22 -0
- data/lib/fluent/plugin/storage.rb +84 -0
- data/lib/fluent/plugin/storage_local.rb +166 -0
- data/lib/fluent/plugin/string_util.rb +22 -0
- data/lib/fluent/plugin.rb +206 -0
- data/lib/fluent/plugin_helper/cert_option.rb +199 -0
- data/lib/fluent/plugin_helper/child_process.rb +369 -0
- data/lib/fluent/plugin_helper/compat_parameters.rb +343 -0
- data/lib/fluent/plugin_helper/counter.rb +51 -0
- data/lib/fluent/plugin_helper/event_emitter.rb +112 -0
- data/lib/fluent/plugin_helper/event_loop.rb +170 -0
- data/lib/fluent/plugin_helper/extract.rb +104 -0
- data/lib/fluent/plugin_helper/formatter.rb +147 -0
- data/lib/fluent/plugin_helper/http_server/app.rb +81 -0
- data/lib/fluent/plugin_helper/http_server/methods.rb +35 -0
- data/lib/fluent/plugin_helper/http_server/request.rb +53 -0
- data/lib/fluent/plugin_helper/http_server/router.rb +54 -0
- data/lib/fluent/plugin_helper/http_server/server.rb +116 -0
- data/lib/fluent/plugin_helper/http_server/ssl_context_builder.rb +41 -0
- data/lib/fluent/plugin_helper/http_server.rb +129 -0
- data/lib/fluent/plugin_helper/inject.rb +154 -0
- data/lib/fluent/plugin_helper/metrics.rb +136 -0
- data/lib/fluent/plugin_helper/parser.rb +147 -0
- data/lib/fluent/plugin_helper/record_accessor.rb +207 -0
- data/lib/fluent/plugin_helper/retry_state.rb +219 -0
- data/lib/fluent/plugin_helper/server.rb +840 -0
- data/lib/fluent/plugin_helper/service_discovery/manager.rb +146 -0
- data/lib/fluent/plugin_helper/service_discovery/round_robin_balancer.rb +43 -0
- data/lib/fluent/plugin_helper/service_discovery.rb +125 -0
- data/lib/fluent/plugin_helper/socket.rb +288 -0
- data/lib/fluent/plugin_helper/socket_option.rb +98 -0
- data/lib/fluent/plugin_helper/storage.rb +349 -0
- data/lib/fluent/plugin_helper/thread.rb +180 -0
- data/lib/fluent/plugin_helper/timer.rb +92 -0
- data/lib/fluent/plugin_helper.rb +75 -0
- data/lib/fluent/plugin_id.rb +93 -0
- data/lib/fluent/process.rb +22 -0
- data/lib/fluent/registry.rb +117 -0
- data/lib/fluent/root_agent.rb +468 -0
- data/lib/fluent/rpc.rb +95 -0
- data/lib/fluent/source_only_buffer_agent.rb +102 -0
- data/lib/fluent/static_config_analysis.rb +195 -0
- data/lib/fluent/supervisor.rb +1347 -0
- data/lib/fluent/system_config.rb +213 -0
- data/lib/fluent/test/base.rb +83 -0
- data/lib/fluent/test/driver/base.rb +231 -0
- data/lib/fluent/test/driver/base_owned.rb +83 -0
- data/lib/fluent/test/driver/base_owner.rb +135 -0
- data/lib/fluent/test/driver/event_feeder.rb +98 -0
- data/lib/fluent/test/driver/filter.rb +61 -0
- data/lib/fluent/test/driver/formatter.rb +30 -0
- data/lib/fluent/test/driver/input.rb +31 -0
- data/lib/fluent/test/driver/multi_output.rb +53 -0
- data/lib/fluent/test/driver/output.rb +102 -0
- data/lib/fluent/test/driver/parser.rb +30 -0
- data/lib/fluent/test/driver/storage.rb +30 -0
- data/lib/fluent/test/driver/test_event_router.rb +45 -0
- data/lib/fluent/test/filter_test.rb +77 -0
- data/lib/fluent/test/formatter_test.rb +65 -0
- data/lib/fluent/test/helpers.rb +138 -0
- data/lib/fluent/test/input_test.rb +174 -0
- data/lib/fluent/test/log.rb +79 -0
- data/lib/fluent/test/output_test.rb +156 -0
- data/lib/fluent/test/parser_test.rb +70 -0
- data/lib/fluent/test/startup_shutdown.rb +44 -0
- data/lib/fluent/test.rb +61 -0
- data/lib/fluent/time.rb +513 -0
- data/lib/fluent/timezone.rb +171 -0
- data/lib/fluent/tls.rb +105 -0
- data/lib/fluent/unique_id.rb +39 -0
- data/lib/fluent/variable_store.rb +40 -0
- data/lib/fluent/version.rb +21 -0
- data/lib/fluent/win32api.rb +38 -0
- data/lib/fluent/winsvc.rb +130 -0
- data/templates/new_gem/.gitignore +8 -0
- data/templates/new_gem/Gemfile +3 -0
- data/templates/new_gem/README.md.erb +43 -0
- data/templates/new_gem/Rakefile +13 -0
- data/templates/new_gem/fluent-plugin.gemspec.erb +28 -0
- data/templates/new_gem/lib/fluent/plugin/filter.rb.erb +14 -0
- data/templates/new_gem/lib/fluent/plugin/formatter.rb.erb +14 -0
- data/templates/new_gem/lib/fluent/plugin/input.rb.erb +11 -0
- data/templates/new_gem/lib/fluent/plugin/output.rb.erb +11 -0
- data/templates/new_gem/lib/fluent/plugin/parser.rb.erb +15 -0
- data/templates/new_gem/lib/fluent/plugin/storage.rb.erb +40 -0
- data/templates/new_gem/test/helper.rb.erb +7 -0
- data/templates/new_gem/test/plugin/test_filter.rb.erb +18 -0
- data/templates/new_gem/test/plugin/test_formatter.rb.erb +18 -0
- data/templates/new_gem/test/plugin/test_input.rb.erb +18 -0
- data/templates/new_gem/test/plugin/test_output.rb.erb +18 -0
- data/templates/new_gem/test/plugin/test_parser.rb.erb +18 -0
- data/templates/new_gem/test/plugin/test_storage.rb.erb +18 -0
- data/templates/plugin_config_formatter/param.md-compact.erb +25 -0
- data/templates/plugin_config_formatter/param.md-table.erb +10 -0
- data/templates/plugin_config_formatter/param.md.erb +34 -0
- data/templates/plugin_config_formatter/section.md.erb +12 -0
- metadata +996 -0
|
@@ -0,0 +1,1063 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Fluentd
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
|
|
17
|
+
require 'fluent/plugin/base'
|
|
18
|
+
require 'fluent/plugin/owned_by_mixin'
|
|
19
|
+
require 'fluent/plugin_id'
|
|
20
|
+
require 'fluent/plugin_helper'
|
|
21
|
+
require 'fluent/unique_id'
|
|
22
|
+
require 'fluent/ext_monitor_require'
|
|
23
|
+
|
|
24
|
+
module Fluent
|
|
25
|
+
module Plugin
|
|
26
|
+
class Buffer < Base
|
|
27
|
+
include OwnedByMixin
|
|
28
|
+
include UniqueId::Mixin
|
|
29
|
+
include PluginId
|
|
30
|
+
include MonitorMixin
|
|
31
|
+
include PluginHelper::Mixin # for metrics
|
|
32
|
+
|
|
33
|
+
class BufferError < StandardError; end
|
|
34
|
+
class BufferOverflowError < BufferError; end
|
|
35
|
+
class BufferChunkOverflowError < BufferError; end # A record size is larger than chunk size limit
|
|
36
|
+
|
|
37
|
+
MINIMUM_APPEND_ATTEMPT_RECORDS = 10
|
|
38
|
+
|
|
39
|
+
DEFAULT_CHUNK_LIMIT_SIZE = 8 * 1024 * 1024 # 8MB
|
|
40
|
+
DEFAULT_TOTAL_LIMIT_SIZE = 512 * 1024 * 1024 # 512MB, same with v0.12 (BufferedOutput + buf_memory: 64 x 8MB)
|
|
41
|
+
|
|
42
|
+
DEFAULT_CHUNK_FULL_THRESHOLD = 0.95
|
|
43
|
+
|
|
44
|
+
configured_in :buffer
|
|
45
|
+
|
|
46
|
+
helpers_internal :metrics
|
|
47
|
+
|
|
48
|
+
# TODO: system total buffer limit size in bytes by SystemConfig
|
|
49
|
+
|
|
50
|
+
config_param :chunk_limit_size, :size, default: DEFAULT_CHUNK_LIMIT_SIZE
|
|
51
|
+
config_param :total_limit_size, :size, default: DEFAULT_TOTAL_LIMIT_SIZE
|
|
52
|
+
|
|
53
|
+
# If user specify this value and (chunk_size * queue_length) is smaller than total_size,
|
|
54
|
+
# then total_size is automatically configured to that value
|
|
55
|
+
config_param :queue_limit_length, :integer, default: nil
|
|
56
|
+
|
|
57
|
+
# optional new limitations
|
|
58
|
+
config_param :chunk_limit_records, :integer, default: nil
|
|
59
|
+
|
|
60
|
+
# if chunk size (or records) is 95% or more after #write, then that chunk will be enqueued
|
|
61
|
+
config_param :chunk_full_threshold, :float, default: DEFAULT_CHUNK_FULL_THRESHOLD
|
|
62
|
+
|
|
63
|
+
desc 'The max number of queued chunks.'
|
|
64
|
+
config_param :queued_chunks_limit_size, :integer, default: nil
|
|
65
|
+
|
|
66
|
+
desc 'Compress buffered data.'
|
|
67
|
+
config_param :compress, :enum, list: [:text, :gzip, :zstd], default: :text
|
|
68
|
+
|
|
69
|
+
desc 'The size limit of the decompressed element.'
|
|
70
|
+
config_param :decompression_size_limit, :size, default: Compressable::DEFAULT_DECOMPRESSION_SIZE_LIMIT
|
|
71
|
+
|
|
72
|
+
desc 'If true, chunks are thrown away when unrecoverable error happens'
|
|
73
|
+
config_param :disable_chunk_backup, :bool, default: false
|
|
74
|
+
|
|
75
|
+
desc 'The approximate size limit of the evacuated chunk files in the evacuation directory. When the retry limit is reached, queued chunks that fit within the remaining capacity are evacuated, and the others are purged without being evacuated. The total size may exceed this limit due to chunk metadata or concurrent evacuation by multiple workers. Set to 0 to disable evacuation. nil (the default) means no limit.'
|
|
76
|
+
config_param :evacuate_limit_size, :size, default: nil
|
|
77
|
+
|
|
78
|
+
Metadata = Struct.new(:timekey, :tag, :variables, :seq) do
|
|
79
|
+
def initialize(timekey, tag, variables)
|
|
80
|
+
super(timekey, tag, variables, 0)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def dup_next
|
|
84
|
+
m = dup
|
|
85
|
+
m.seq = seq + 1
|
|
86
|
+
m
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def empty?
|
|
90
|
+
timekey.nil? && tag.nil? && variables.nil?
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def cmp_variables(v1, v2)
|
|
94
|
+
if v1.nil? && v2.nil?
|
|
95
|
+
return 0
|
|
96
|
+
elsif v1.nil? # v2 is non-nil
|
|
97
|
+
return -1
|
|
98
|
+
elsif v2.nil? # v1 is non-nil
|
|
99
|
+
return 1
|
|
100
|
+
end
|
|
101
|
+
# both of v1 and v2 are non-nil
|
|
102
|
+
v1_sorted_keys = v1.keys.sort
|
|
103
|
+
v2_sorted_keys = v2.keys.sort
|
|
104
|
+
if v1_sorted_keys != v2_sorted_keys
|
|
105
|
+
if v1_sorted_keys.size == v2_sorted_keys.size
|
|
106
|
+
v1_sorted_keys <=> v2_sorted_keys
|
|
107
|
+
else
|
|
108
|
+
v1_sorted_keys.size <=> v2_sorted_keys.size
|
|
109
|
+
end
|
|
110
|
+
else
|
|
111
|
+
v1_sorted_keys.each do |k|
|
|
112
|
+
a = v1[k]
|
|
113
|
+
b = v2[k]
|
|
114
|
+
if a && b && a != b
|
|
115
|
+
return a <=> b
|
|
116
|
+
elsif a && b || (!a && !b) # same value (including both are nil)
|
|
117
|
+
next
|
|
118
|
+
elsif a # b is nil
|
|
119
|
+
return 1
|
|
120
|
+
else # a is nil (but b is non-nil)
|
|
121
|
+
return -1
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
0
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def <=>(o)
|
|
130
|
+
timekey2 = o.timekey
|
|
131
|
+
tag2 = o.tag
|
|
132
|
+
variables2 = o.variables
|
|
133
|
+
if (!!timekey ^ !!timekey2) || (!!tag ^ !!tag2) || (!!variables ^ !!variables2)
|
|
134
|
+
# One has value in a field, but another doesn't have value in same field
|
|
135
|
+
# This case occurs very rarely
|
|
136
|
+
if timekey == timekey2 # including the case of nil == nil
|
|
137
|
+
if tag == tag2
|
|
138
|
+
cmp_variables(variables, variables2)
|
|
139
|
+
elsif tag.nil?
|
|
140
|
+
-1
|
|
141
|
+
elsif tag2.nil?
|
|
142
|
+
1
|
|
143
|
+
else
|
|
144
|
+
tag <=> tag2
|
|
145
|
+
end
|
|
146
|
+
elsif timekey.nil?
|
|
147
|
+
-1
|
|
148
|
+
elsif timekey2.nil?
|
|
149
|
+
1
|
|
150
|
+
else
|
|
151
|
+
timekey <=> timekey2
|
|
152
|
+
end
|
|
153
|
+
else
|
|
154
|
+
# objects have values in same field pairs (comparison with non-nil and nil doesn't occur here)
|
|
155
|
+
(timekey <=> timekey2 || 0).nonzero? || # if `a <=> b` is nil, then both are nil
|
|
156
|
+
(tag <=> tag2 || 0).nonzero? ||
|
|
157
|
+
cmp_variables(variables, variables2)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# This is an optimization code. Current Struct's implementation is comparing all data.
|
|
162
|
+
# https://github.com/ruby/ruby/blob/0623e2b7cc621b1733a760b72af246b06c30cf96/struct.c#L1200-L1203
|
|
163
|
+
# Actually this overhead is very small but this class is generated *per chunk* (and used in hash object).
|
|
164
|
+
# This means that this class is one of the most called object in Fluentd.
|
|
165
|
+
# See https://github.com/fluent/fluentd/pull/2560
|
|
166
|
+
def hash
|
|
167
|
+
timekey.hash
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# for metrics
|
|
172
|
+
attr_reader :stage_size_metrics, :stage_length_metrics, :queue_size_metrics, :queue_length_metrics
|
|
173
|
+
attr_reader :available_buffer_space_ratios_metrics, :total_queued_size_metrics
|
|
174
|
+
attr_reader :newest_timekey_metrics, :oldest_timekey_metrics
|
|
175
|
+
# for tests
|
|
176
|
+
attr_reader :stage, :queue, :dequeued, :queued_num
|
|
177
|
+
|
|
178
|
+
def initialize
|
|
179
|
+
super
|
|
180
|
+
|
|
181
|
+
@chunk_limit_size = nil
|
|
182
|
+
@total_limit_size = nil
|
|
183
|
+
@queue_limit_length = nil
|
|
184
|
+
@chunk_limit_records = nil
|
|
185
|
+
|
|
186
|
+
@stage = {} #=> Hash (metadata -> chunk) : not flushed yet
|
|
187
|
+
@queue = [] #=> Array (chunks) : already flushed (not written)
|
|
188
|
+
@dequeued = {} #=> Hash (unique_id -> chunk): already written (not purged)
|
|
189
|
+
@queued_num = {} # metadata => int (number of queued chunks)
|
|
190
|
+
@dequeued_num = {} # metadata => int (number of dequeued chunks)
|
|
191
|
+
|
|
192
|
+
@stage_length_metrics = nil
|
|
193
|
+
@stage_size_metrics = nil
|
|
194
|
+
@queue_length_metrics = nil
|
|
195
|
+
@queue_size_metrics = nil
|
|
196
|
+
@available_buffer_space_ratios_metrics = nil
|
|
197
|
+
@total_queued_size_metrics = nil
|
|
198
|
+
@newest_timekey_metrics = nil
|
|
199
|
+
@oldest_timekey_metrics = nil
|
|
200
|
+
@timekeys = Hash.new(0)
|
|
201
|
+
@enable_update_timekeys = false
|
|
202
|
+
@mutex = Mutex.new
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# The metrics_create method defines getter methods named stage_byte_size and queue_byte_size.
|
|
206
|
+
# For compatibility, stage_size, stage_size=, queue_size, and queue_size= are still available.
|
|
207
|
+
def stage_size
|
|
208
|
+
@stage_size_metrics.get
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def stage_size=(value)
|
|
212
|
+
@stage_size_metrics.set(value)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def queue_size
|
|
216
|
+
@queue_size_metrics.get
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def queue_size=(value)
|
|
220
|
+
@queue_size_metrics.set(value)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def persistent?
|
|
224
|
+
false
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def configure(conf)
|
|
228
|
+
super
|
|
229
|
+
|
|
230
|
+
unless @queue_limit_length.nil?
|
|
231
|
+
@total_limit_size = @chunk_limit_size * @queue_limit_length
|
|
232
|
+
end
|
|
233
|
+
if @evacuate_limit_size
|
|
234
|
+
raise Fluent::ConfigError, "evacuate_limit_size must be 0 or larger" if @evacuate_limit_size < 0
|
|
235
|
+
if !persistent?
|
|
236
|
+
log.warn "evacuate_limit_size is ignored because this buffer does not evacuate chunks"
|
|
237
|
+
@evacuate_limit_size = nil
|
|
238
|
+
elsif @evacuate_limit_size > 0
|
|
239
|
+
unless owner.plugin_id_configured?
|
|
240
|
+
log.warn "evacuate_limit_size should be used with @id, because the evacuation directory without @id can change when the configuration is changed or reloaded"
|
|
241
|
+
end
|
|
242
|
+
if @evacuate_limit_size < @chunk_limit_size
|
|
243
|
+
log.warn "evacuate_limit_size is smaller than chunk_limit_size, so full chunks may not be evacuated", evacuate_limit_size: @evacuate_limit_size, chunk_limit_size: @chunk_limit_size
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
@stage_length_metrics = metrics_create(namespace: "fluentd", subsystem: "buffer", name: "stage_length",
|
|
248
|
+
help_text: 'Length of stage buffers', prefer_gauge: true)
|
|
249
|
+
@stage_length_metrics.set(0)
|
|
250
|
+
@stage_size_metrics = metrics_create(namespace: "fluentd", subsystem: "buffer", name: "stage_byte_size",
|
|
251
|
+
help_text: 'Total size of stage buffers', prefer_gauge: true)
|
|
252
|
+
@stage_size_metrics.set(0) # Ensure zero.
|
|
253
|
+
@queue_length_metrics = metrics_create(namespace: "fluentd", subsystem: "buffer", name: "queue_length",
|
|
254
|
+
help_text: 'Length of queue buffers', prefer_gauge: true)
|
|
255
|
+
@queue_length_metrics.set(0)
|
|
256
|
+
@queue_size_metrics = metrics_create(namespace: "fluentd", subsystem: "buffer", name: "queue_byte_size",
|
|
257
|
+
help_text: 'Total size of queue buffers', prefer_gauge: true)
|
|
258
|
+
@queue_size_metrics.set(0) # Ensure zero.
|
|
259
|
+
@available_buffer_space_ratios_metrics = metrics_create(namespace: "fluentd", subsystem: "buffer", name: "available_buffer_space_ratios",
|
|
260
|
+
help_text: 'Ratio of available space in buffer', prefer_gauge: true)
|
|
261
|
+
@available_buffer_space_ratios_metrics.set(100) # Default is 100%.
|
|
262
|
+
@total_queued_size_metrics = metrics_create(namespace: "fluentd", subsystem: "buffer", name: "total_queued_size",
|
|
263
|
+
help_text: 'Total size of stage and queue buffers', prefer_gauge: true)
|
|
264
|
+
@total_queued_size_metrics.set(0)
|
|
265
|
+
@newest_timekey_metrics = metrics_create(namespace: "fluentd", subsystem: "buffer", name: "newest_timekey",
|
|
266
|
+
help_text: 'Newest timekey in buffer', prefer_gauge: true)
|
|
267
|
+
@oldest_timekey_metrics = metrics_create(namespace: "fluentd", subsystem: "buffer", name: "oldest_timekey",
|
|
268
|
+
help_text: 'Oldest timekey in buffer', prefer_gauge: true)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def enable_update_timekeys
|
|
272
|
+
@enable_update_timekeys = true
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def start
|
|
276
|
+
super
|
|
277
|
+
|
|
278
|
+
@stage, @queue = resume
|
|
279
|
+
@stage.each_pair do |metadata, chunk|
|
|
280
|
+
@stage_size_metrics.add(chunk.bytesize)
|
|
281
|
+
end
|
|
282
|
+
@queue.each do |chunk|
|
|
283
|
+
@queued_num[chunk.metadata] ||= 0
|
|
284
|
+
@queued_num[chunk.metadata] += 1
|
|
285
|
+
@queue_size_metrics.add(chunk.bytesize)
|
|
286
|
+
end
|
|
287
|
+
update_timekeys
|
|
288
|
+
log.debug "buffer started", instance: self.object_id, stage_size: @stage_size_metrics.get, queue_size: @queue_size_metrics.get
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def close
|
|
292
|
+
super
|
|
293
|
+
synchronize do
|
|
294
|
+
log.debug "closing buffer", instance: self.object_id
|
|
295
|
+
@dequeued.each_pair do |chunk_id, chunk|
|
|
296
|
+
chunk.close
|
|
297
|
+
end
|
|
298
|
+
until @queue.empty?
|
|
299
|
+
@queue.shift.close
|
|
300
|
+
end
|
|
301
|
+
@stage.each_pair do |metadata, chunk|
|
|
302
|
+
chunk.close
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def terminate
|
|
308
|
+
super
|
|
309
|
+
@dequeued = @stage = @queue = @queued_num = nil
|
|
310
|
+
@stage_length_metrics = @stage_size_metrics = @queue_length_metrics = @queue_size_metrics = nil
|
|
311
|
+
@available_buffer_space_ratios_metrics = @total_queued_size_metrics = nil
|
|
312
|
+
@newest_timekey_metrics = @oldest_timekey_metrics = nil
|
|
313
|
+
@timekeys.clear
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def storable?
|
|
317
|
+
@total_limit_size > @stage_size_metrics.get + @queue_size_metrics.get
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
## TODO: for back pressure feature
|
|
321
|
+
# def used?(ratio)
|
|
322
|
+
# @total_limit_size * ratio > @stage_size_metrics.get + @queue_size_metrics.get
|
|
323
|
+
# end
|
|
324
|
+
|
|
325
|
+
def resume
|
|
326
|
+
# return {}, []
|
|
327
|
+
raise NotImplementedError, "Implement this method in child class"
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def generate_chunk(metadata)
|
|
331
|
+
raise NotImplementedError, "Implement this method in child class"
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def new_metadata(timekey: nil, tag: nil, variables: nil)
|
|
335
|
+
Metadata.new(timekey, tag, variables)
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# Keep this method for existing code
|
|
339
|
+
def metadata(timekey: nil, tag: nil, variables: nil)
|
|
340
|
+
Metadata.new(timekey, tag, variables)
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def timekeys
|
|
344
|
+
@timekeys.keys
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
# metadata MUST have consistent object_id for each variation
|
|
348
|
+
# data MUST be Array of serialized events, or EventStream
|
|
349
|
+
# metadata_and_data MUST be a hash of { metadata => data }
|
|
350
|
+
def write(metadata_and_data, format: nil, size: nil, enqueue: false)
|
|
351
|
+
return if metadata_and_data.size < 1
|
|
352
|
+
raise BufferOverflowError, "buffer space has too many data" unless storable?
|
|
353
|
+
|
|
354
|
+
log.on_trace { log.trace "writing events into buffer", instance: self.object_id, metadata_size: metadata_and_data.size }
|
|
355
|
+
|
|
356
|
+
operated_chunks = []
|
|
357
|
+
unstaged_chunks = {} # metadata => [chunk, chunk, ...]
|
|
358
|
+
chunks_to_enqueue = []
|
|
359
|
+
staged_bytesizes_by_chunk = {}
|
|
360
|
+
# track internal BufferChunkOverflowError in write_step_by_step
|
|
361
|
+
buffer_chunk_overflow_errors = []
|
|
362
|
+
|
|
363
|
+
begin
|
|
364
|
+
# sort metadata to get lock of chunks in same order with other threads
|
|
365
|
+
metadata_and_data.keys.sort.each do |metadata|
|
|
366
|
+
data = metadata_and_data[metadata]
|
|
367
|
+
write_once(metadata, data, format: format, size: size) do |chunk, adding_bytesize, error|
|
|
368
|
+
chunk.mon_enter # add lock to prevent to be committed/rollbacked from other threads
|
|
369
|
+
operated_chunks << chunk
|
|
370
|
+
if chunk.staged?
|
|
371
|
+
#
|
|
372
|
+
# https://github.com/fluent/fluentd/issues/2712
|
|
373
|
+
# write_once is supposed to write to a chunk only once
|
|
374
|
+
# but this block **may** run multiple times from write_step_by_step and previous write may be rollbacked
|
|
375
|
+
# So we should be counting the stage_size only for the last successful write
|
|
376
|
+
#
|
|
377
|
+
staged_bytesizes_by_chunk[chunk] = adding_bytesize
|
|
378
|
+
elsif chunk.unstaged?
|
|
379
|
+
unstaged_chunks[metadata] ||= []
|
|
380
|
+
unstaged_chunks[metadata] << chunk
|
|
381
|
+
end
|
|
382
|
+
if error && !error.empty?
|
|
383
|
+
buffer_chunk_overflow_errors << error
|
|
384
|
+
end
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
return if operated_chunks.empty?
|
|
389
|
+
|
|
390
|
+
# Now, this thread acquires many locks of chunks... getting buffer-global lock causes dead lock.
|
|
391
|
+
# Any operations needs buffer-global lock (including enqueueing) should be done after releasing locks.
|
|
392
|
+
|
|
393
|
+
first_chunk = operated_chunks.shift
|
|
394
|
+
# Following commits for other chunks also can finish successfully if the first commit operation
|
|
395
|
+
# finishes without any exceptions.
|
|
396
|
+
# In most cases, #commit just requires very small disk spaces, so major failure reason are
|
|
397
|
+
# permission errors, disk failures and other permanent(fatal) errors.
|
|
398
|
+
begin
|
|
399
|
+
first_chunk.commit
|
|
400
|
+
if enqueue || first_chunk.unstaged? || chunk_size_full?(first_chunk)
|
|
401
|
+
chunks_to_enqueue << first_chunk
|
|
402
|
+
end
|
|
403
|
+
if (bytesize = staged_bytesizes_by_chunk[first_chunk])
|
|
404
|
+
# Account while the chunk lock is still held so enqueue_chunk
|
|
405
|
+
# cannot remove the chunk and subtract its bytes first.
|
|
406
|
+
@stage_size_metrics.add(bytesize)
|
|
407
|
+
log.on_trace { log.trace { "chunk #{first_chunk.path} size_added: #{bytesize} new_size: #{first_chunk.bytesize}" } }
|
|
408
|
+
end
|
|
409
|
+
first_chunk.mon_exit
|
|
410
|
+
rescue
|
|
411
|
+
operated_chunks.unshift(first_chunk)
|
|
412
|
+
raise
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
errors = []
|
|
416
|
+
# Buffer plugin estimates there's no serious error cause: will commit for all chunks either way
|
|
417
|
+
operated_chunks.each do |chunk|
|
|
418
|
+
begin
|
|
419
|
+
chunk.commit
|
|
420
|
+
if enqueue || chunk.unstaged? || chunk_size_full?(chunk)
|
|
421
|
+
chunks_to_enqueue << chunk
|
|
422
|
+
end
|
|
423
|
+
if (bytesize = staged_bytesizes_by_chunk[chunk])
|
|
424
|
+
@stage_size_metrics.add(bytesize)
|
|
425
|
+
log.on_trace { log.trace { "chunk #{chunk.path} size_added: #{bytesize} new_size: #{chunk.bytesize}" } }
|
|
426
|
+
end
|
|
427
|
+
chunk.mon_exit
|
|
428
|
+
rescue => e
|
|
429
|
+
chunk.rollback
|
|
430
|
+
chunk.mon_exit
|
|
431
|
+
errors << e
|
|
432
|
+
end
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
# All locks about chunks are released. Stage-size accounting for the
|
|
436
|
+
# staged chunks happened inside each chunk monitor above, so a
|
|
437
|
+
# concurrent enqueue_chunk cannot observe a transiently negative
|
|
438
|
+
# stage_size (issue #2712 race).
|
|
439
|
+
|
|
440
|
+
enqueue_errors = []
|
|
441
|
+
chunks_to_enqueue.each do |c|
|
|
442
|
+
begin
|
|
443
|
+
if c.staged? && (enqueue || chunk_size_full?(c))
|
|
444
|
+
m = c.metadata
|
|
445
|
+
enqueue_chunk(m)
|
|
446
|
+
if unstaged_chunks[m] && !unstaged_chunks[m].empty?
|
|
447
|
+
u = unstaged_chunks[m].pop
|
|
448
|
+
u.synchronize do
|
|
449
|
+
if u.unstaged? && !chunk_size_full?(u)
|
|
450
|
+
# `u.metadata.seq` and `m.seq` can be different but Buffer#enqueue_chunk expect them to be the same value
|
|
451
|
+
u.metadata.seq = 0
|
|
452
|
+
synchronize {
|
|
453
|
+
@stage[m] = u.staged!
|
|
454
|
+
@stage_size_metrics.add(u.bytesize)
|
|
455
|
+
}
|
|
456
|
+
end
|
|
457
|
+
end
|
|
458
|
+
end
|
|
459
|
+
elsif c.unstaged?
|
|
460
|
+
enqueue_unstaged_chunk(c)
|
|
461
|
+
else
|
|
462
|
+
# previously staged chunk is already enqueued, closed or purged.
|
|
463
|
+
# no problem.
|
|
464
|
+
end
|
|
465
|
+
rescue => e
|
|
466
|
+
enqueue_errors << e
|
|
467
|
+
end
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
enqueue_errors.each do |e|
|
|
471
|
+
log.warn "error occurs in enqueueing a chunk", error: e
|
|
472
|
+
log.warn_backtrace e.backtrace
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
operated_chunks.clear if errors.empty?
|
|
476
|
+
|
|
477
|
+
if errors.size > 0
|
|
478
|
+
log.warn "error occurs in committing chunks: only first one raised", errors: errors.map(&:class)
|
|
479
|
+
raise errors.first
|
|
480
|
+
end
|
|
481
|
+
ensure
|
|
482
|
+
operated_chunks.each do |chunk|
|
|
483
|
+
chunk.rollback rescue nil # nothing possible to do for #rollback failure
|
|
484
|
+
if chunk.unstaged?
|
|
485
|
+
chunk.purge rescue nil # to prevent leakage of unstaged chunks
|
|
486
|
+
end
|
|
487
|
+
chunk.mon_exit rescue nil # this may raise ThreadError for chunks already committed
|
|
488
|
+
end
|
|
489
|
+
unless buffer_chunk_overflow_errors.empty?
|
|
490
|
+
# Notify delayed BufferChunkOverflowError here
|
|
491
|
+
raise BufferChunkOverflowError, buffer_chunk_overflow_errors.join(", ")
|
|
492
|
+
end
|
|
493
|
+
end
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
def queue_full?
|
|
497
|
+
synchronize { @queue.size } >= @queued_chunks_limit_size
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
def queued_records
|
|
501
|
+
synchronize { @queue.reduce(0){|r, chunk| r + chunk.size } }
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
def queued?(metadata = nil, optimistic: false)
|
|
505
|
+
if optimistic
|
|
506
|
+
optimistic_queued?(metadata)
|
|
507
|
+
else
|
|
508
|
+
synchronize do
|
|
509
|
+
optimistic_queued?(metadata)
|
|
510
|
+
end
|
|
511
|
+
end
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
def enqueue_chunk(metadata)
|
|
515
|
+
log.on_trace { log.trace "enqueueing chunk", instance: self.object_id, metadata: metadata }
|
|
516
|
+
|
|
517
|
+
chunk = synchronize do
|
|
518
|
+
@stage.delete(metadata)
|
|
519
|
+
end
|
|
520
|
+
return nil unless chunk
|
|
521
|
+
|
|
522
|
+
chunk.synchronize do
|
|
523
|
+
synchronize do
|
|
524
|
+
bytesize = chunk.bytesize
|
|
525
|
+
@stage_size_metrics.sub(bytesize)
|
|
526
|
+
@queue_size_metrics.add(bytesize)
|
|
527
|
+
if chunk.empty?
|
|
528
|
+
chunk.close
|
|
529
|
+
else
|
|
530
|
+
chunk.metadata.seq = 0 # metadata.seq should be 0 for counting @queued_num
|
|
531
|
+
@queue << chunk
|
|
532
|
+
@queued_num[metadata] = @queued_num.fetch(metadata, 0) + 1
|
|
533
|
+
chunk.enqueued!
|
|
534
|
+
end
|
|
535
|
+
end
|
|
536
|
+
end
|
|
537
|
+
nil
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
def enqueue_unstaged_chunk(chunk)
|
|
541
|
+
log.on_trace { log.trace "enqueueing unstaged chunk", instance: self.object_id, metadata: chunk.metadata }
|
|
542
|
+
|
|
543
|
+
synchronize do
|
|
544
|
+
chunk.synchronize do
|
|
545
|
+
metadata = chunk.metadata
|
|
546
|
+
metadata.seq = 0 # metadata.seq should be 0 for counting @queued_num
|
|
547
|
+
@queue << chunk
|
|
548
|
+
@queued_num[metadata] = @queued_num.fetch(metadata, 0) + 1
|
|
549
|
+
@queue_size_metrics.add(chunk.bytesize)
|
|
550
|
+
chunk.enqueued!
|
|
551
|
+
end
|
|
552
|
+
end
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
def update_timekeys
|
|
556
|
+
synchronize do
|
|
557
|
+
chunks = @stage.values
|
|
558
|
+
chunks.concat(@queue)
|
|
559
|
+
@timekeys = chunks.each_with_object({}) do |chunk, keys|
|
|
560
|
+
if chunk.metadata&.timekey
|
|
561
|
+
t = chunk.metadata.timekey
|
|
562
|
+
keys[t] = keys.fetch(t, 0) + 1
|
|
563
|
+
end
|
|
564
|
+
end
|
|
565
|
+
end
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
# At flush_at_shutdown, all staged chunks should be enqueued for buffer flush. Set true to force_enqueue for it.
|
|
569
|
+
def enqueue_all(force_enqueue = false)
|
|
570
|
+
log.on_trace { log.trace "enqueueing all chunks in buffer", instance: self.object_id }
|
|
571
|
+
update_timekeys if @enable_update_timekeys
|
|
572
|
+
|
|
573
|
+
if block_given?
|
|
574
|
+
synchronize{ @stage.keys }.each do |metadata|
|
|
575
|
+
return if !force_enqueue && queue_full?
|
|
576
|
+
# NOTE: The following line might cause data race depending on Ruby implementations except CRuby
|
|
577
|
+
# cf. https://github.com/fluent/fluentd/pull/1721#discussion_r146170251
|
|
578
|
+
chunk = @stage[metadata]
|
|
579
|
+
next unless chunk
|
|
580
|
+
v = yield metadata, chunk
|
|
581
|
+
enqueue_chunk(metadata) if v
|
|
582
|
+
end
|
|
583
|
+
else
|
|
584
|
+
synchronize{ @stage.keys }.each do |metadata|
|
|
585
|
+
return if !force_enqueue && queue_full?
|
|
586
|
+
enqueue_chunk(metadata)
|
|
587
|
+
end
|
|
588
|
+
end
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
def dequeue_chunk
|
|
592
|
+
return nil if @queue.empty?
|
|
593
|
+
log.on_trace { log.trace "dequeueing a chunk", instance: self.object_id }
|
|
594
|
+
|
|
595
|
+
synchronize do
|
|
596
|
+
chunk = @queue.shift
|
|
597
|
+
|
|
598
|
+
# this buffer is dequeued by other thread just before "synchronize" in this thread
|
|
599
|
+
return nil unless chunk
|
|
600
|
+
|
|
601
|
+
@dequeued[chunk.unique_id] = chunk
|
|
602
|
+
@queued_num[chunk.metadata] -= 1 # BUG if nil, 0 or subzero
|
|
603
|
+
@dequeued_num[chunk.metadata] ||= 0
|
|
604
|
+
@dequeued_num[chunk.metadata] += 1
|
|
605
|
+
log.trace "chunk dequeued", instance: self.object_id, metadata: chunk.metadata
|
|
606
|
+
chunk
|
|
607
|
+
end
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
def takeback_chunk(chunk_id)
|
|
611
|
+
log.on_trace { log.trace "taking back a chunk", instance: self.object_id, chunk_id: dump_unique_id_hex(chunk_id) }
|
|
612
|
+
|
|
613
|
+
synchronize do
|
|
614
|
+
chunk = @dequeued.delete(chunk_id)
|
|
615
|
+
return false unless chunk # already purged by other thread
|
|
616
|
+
@queue.unshift(chunk)
|
|
617
|
+
log.on_trace { log.trace "chunk taken back", instance: self.object_id, chunk_id: dump_unique_id_hex(chunk_id), metadata: chunk.metadata }
|
|
618
|
+
@queued_num[chunk.metadata] += 1 # BUG if nil
|
|
619
|
+
@dequeued_num[chunk.metadata] -= 1
|
|
620
|
+
end
|
|
621
|
+
true
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
def purge_chunk(chunk_id)
|
|
625
|
+
metadata = nil
|
|
626
|
+
synchronize do
|
|
627
|
+
chunk = @dequeued.delete(chunk_id)
|
|
628
|
+
return nil unless chunk # purged by other threads
|
|
629
|
+
|
|
630
|
+
metadata = chunk.metadata
|
|
631
|
+
log.on_trace { log.trace "purging a chunk", instance: self.object_id, chunk_id: dump_unique_id_hex(chunk_id), metadata: metadata }
|
|
632
|
+
|
|
633
|
+
bytesize = chunk.bytesize
|
|
634
|
+
begin
|
|
635
|
+
chunk.purge
|
|
636
|
+
rescue => e
|
|
637
|
+
log.error "failed to purge buffer chunk", chunk_id: dump_unique_id_hex(chunk_id), error_class: e.class, error: e
|
|
638
|
+
log.error_backtrace
|
|
639
|
+
ensure
|
|
640
|
+
# Always release the queued byte counter, even when purge raises
|
|
641
|
+
# (e.g. unlink/close failure on the buffer path). Otherwise
|
|
642
|
+
# @queue_size is never decremented for a dequeued chunk that is
|
|
643
|
+
# gone from both @queue and @dequeued, so the leak ratchets toward
|
|
644
|
+
# total_limit_size and storable? becomes permanently false (#5468).
|
|
645
|
+
@queue_size_metrics.sub(bytesize)
|
|
646
|
+
end
|
|
647
|
+
|
|
648
|
+
@dequeued_num[chunk.metadata] -= 1
|
|
649
|
+
if metadata && !@stage[metadata] && (!@queued_num[metadata] || @queued_num[metadata] < 1) && @dequeued_num[metadata].zero?
|
|
650
|
+
@queued_num.delete(metadata)
|
|
651
|
+
@dequeued_num.delete(metadata)
|
|
652
|
+
end
|
|
653
|
+
log.on_trace { log.trace "chunk purged", instance: self.object_id, chunk_id: dump_unique_id_hex(chunk_id), metadata: metadata }
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
nil
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
def clear_queue!
|
|
660
|
+
log.on_trace { log.trace "clearing queue", instance: self.object_id }
|
|
661
|
+
|
|
662
|
+
evacuate_budget = nil
|
|
663
|
+
skipped_num = 0
|
|
664
|
+
skipped_bytesize = 0
|
|
665
|
+
|
|
666
|
+
synchronize do
|
|
667
|
+
until @queue.empty?
|
|
668
|
+
begin
|
|
669
|
+
q = @queue.shift
|
|
670
|
+
if @evacuate_limit_size.nil?
|
|
671
|
+
evacuate_chunk(q)
|
|
672
|
+
elsif @evacuate_limit_size > 0
|
|
673
|
+
evacuate_budget ||= remaining_evacuate_size
|
|
674
|
+
if q.bytesize <= evacuate_budget
|
|
675
|
+
evacuate_chunk(q)
|
|
676
|
+
evacuate_budget -= q.bytesize
|
|
677
|
+
else
|
|
678
|
+
skipped_num += 1
|
|
679
|
+
skipped_bytesize += q.bytesize
|
|
680
|
+
end
|
|
681
|
+
end
|
|
682
|
+
log.trace("purging a chunk in queue"){ {id: dump_unique_id_hex(q.unique_id), bytesize: q.bytesize, size: q.size} }
|
|
683
|
+
q.purge
|
|
684
|
+
rescue => e
|
|
685
|
+
log.error "unexpected error while clearing buffer queue", error_class: e.class, error: e
|
|
686
|
+
log.error_backtrace
|
|
687
|
+
end
|
|
688
|
+
end
|
|
689
|
+
@queue_size_metrics.set(0)
|
|
690
|
+
end
|
|
691
|
+
|
|
692
|
+
if skipped_num > 0
|
|
693
|
+
log.warn "some chunks are purged without being evacuated to stay within evacuate_limit_size.", evacuate_limit_size: @evacuate_limit_size, chunks: skipped_num, bytesize: skipped_bytesize
|
|
694
|
+
end
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
def evacuate_chunk(chunk)
|
|
698
|
+
# Overwrite this on demand.
|
|
699
|
+
#
|
|
700
|
+
# Note: Difference from the `backup` feature.
|
|
701
|
+
# The `backup` feature is for unrecoverable errors, mainly for bad chunks.
|
|
702
|
+
# On the other hand, this feature is for normal chunks.
|
|
703
|
+
# The main motivation for this feature is to enable recovery by evacuating buffer files
|
|
704
|
+
# when the retry limit is reached due to external factors such as network issues.
|
|
705
|
+
#
|
|
706
|
+
# Note: Difference from the `secondary` feature.
|
|
707
|
+
# The `secondary` feature is not suitable for recovery.
|
|
708
|
+
# It can be difficult to recover files made by `out_secondary_file` because the metadata
|
|
709
|
+
# is lost.
|
|
710
|
+
# For file buffers, the easiest way for recovery is to evacuate the chunk files as is.
|
|
711
|
+
# Once the issue is recovered, we can put back the chunk files, and restart Fluentd to
|
|
712
|
+
# load them.
|
|
713
|
+
# This feature enables it.
|
|
714
|
+
end
|
|
715
|
+
|
|
716
|
+
def chunk_size_over?(chunk)
|
|
717
|
+
chunk.bytesize > @chunk_limit_size || (@chunk_limit_records && chunk.size > @chunk_limit_records)
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
def chunk_size_full?(chunk)
|
|
721
|
+
chunk.bytesize >= @chunk_limit_size * @chunk_full_threshold || (@chunk_limit_records && chunk.size >= @chunk_limit_records * @chunk_full_threshold)
|
|
722
|
+
end
|
|
723
|
+
|
|
724
|
+
class ShouldRetry < StandardError; end
|
|
725
|
+
|
|
726
|
+
# write once into a chunk
|
|
727
|
+
# 1. append whole data into existing chunk
|
|
728
|
+
# 2. commit it & return unless chunk_size_over?
|
|
729
|
+
# 3. enqueue existing chunk & retry whole method if chunk was not empty
|
|
730
|
+
# 4. go to step_by_step writing
|
|
731
|
+
|
|
732
|
+
def write_once(metadata, data, format: nil, size: nil, &block)
|
|
733
|
+
return if data.empty?
|
|
734
|
+
|
|
735
|
+
stored = false
|
|
736
|
+
adding_bytesize = nil
|
|
737
|
+
|
|
738
|
+
chunk = synchronize { @stage[metadata] ||= generate_chunk(metadata).staged! }
|
|
739
|
+
enqueue_chunk_before_retry = false
|
|
740
|
+
chunk.synchronize do
|
|
741
|
+
# retry this method if chunk is already queued (between getting chunk and entering critical section)
|
|
742
|
+
raise ShouldRetry unless chunk.staged?
|
|
743
|
+
|
|
744
|
+
empty_chunk = chunk.empty?
|
|
745
|
+
|
|
746
|
+
original_bytesize = chunk.bytesize
|
|
747
|
+
begin
|
|
748
|
+
if format
|
|
749
|
+
serialized = format.call(data)
|
|
750
|
+
chunk.concat(serialized, size ? size.call : data.size)
|
|
751
|
+
else
|
|
752
|
+
chunk.append(data, compress: @compress)
|
|
753
|
+
end
|
|
754
|
+
adding_bytesize = chunk.bytesize - original_bytesize
|
|
755
|
+
|
|
756
|
+
if chunk_size_over?(chunk)
|
|
757
|
+
if format && empty_chunk
|
|
758
|
+
if chunk.bytesize > @chunk_limit_size
|
|
759
|
+
log.warn "chunk bytes limit exceeds for an emitted event stream: #{adding_bytesize}bytes"
|
|
760
|
+
else
|
|
761
|
+
log.warn "chunk size limit exceeds for an emitted event stream: #{chunk.size}records"
|
|
762
|
+
end
|
|
763
|
+
end
|
|
764
|
+
chunk.rollback
|
|
765
|
+
|
|
766
|
+
if format && !empty_chunk
|
|
767
|
+
# Event streams should be appended into a chunk at once
|
|
768
|
+
# as far as possible, to improve performance of formatting.
|
|
769
|
+
# Event stream may be a MessagePackEventStream. We don't want to split it into
|
|
770
|
+
# 2 or more chunks (except for a case that the event stream is larger than chunk limit).
|
|
771
|
+
enqueue_chunk_before_retry = true
|
|
772
|
+
raise ShouldRetry
|
|
773
|
+
end
|
|
774
|
+
else
|
|
775
|
+
stored = true
|
|
776
|
+
end
|
|
777
|
+
rescue
|
|
778
|
+
chunk.rollback
|
|
779
|
+
raise
|
|
780
|
+
end
|
|
781
|
+
|
|
782
|
+
if stored
|
|
783
|
+
block.call(chunk, adding_bytesize)
|
|
784
|
+
end
|
|
785
|
+
end
|
|
786
|
+
|
|
787
|
+
unless stored
|
|
788
|
+
# try step-by-step appending if data can't be stored into existing a chunk in non-bulk mode
|
|
789
|
+
#
|
|
790
|
+
# 1/10 size of original event stream (splits_count == 10) seems enough small
|
|
791
|
+
# to try emitting events into existing chunk.
|
|
792
|
+
# it does not matter to split event stream into very small splits, because chunks have less
|
|
793
|
+
# overhead to write data many times (even about file buffer chunks).
|
|
794
|
+
write_step_by_step(metadata, data, format, 10, &block)
|
|
795
|
+
end
|
|
796
|
+
rescue ShouldRetry
|
|
797
|
+
enqueue_chunk(metadata) if enqueue_chunk_before_retry
|
|
798
|
+
retry
|
|
799
|
+
end
|
|
800
|
+
|
|
801
|
+
# EventStream can be split into many streams
|
|
802
|
+
# because (es1 + es2).to_msgpack_stream == es1.to_msgpack_stream + es2.to_msgpack_stream
|
|
803
|
+
|
|
804
|
+
# 1. split event streams into many (10 -> 100 -> 1000 -> ...) chunks
|
|
805
|
+
# 2. append splits into the staged chunks as much as possible
|
|
806
|
+
# 3. create unstaged chunk and append rest splits -> repeat it for all splits
|
|
807
|
+
|
|
808
|
+
def write_step_by_step(metadata, data, format, splits_count, &block)
|
|
809
|
+
splits = []
|
|
810
|
+
if splits_count > data.size
|
|
811
|
+
splits_count = data.size
|
|
812
|
+
end
|
|
813
|
+
slice_size = if data.size % splits_count == 0
|
|
814
|
+
data.size / splits_count
|
|
815
|
+
else
|
|
816
|
+
data.size / (splits_count - 1)
|
|
817
|
+
end
|
|
818
|
+
slice_origin = 0
|
|
819
|
+
while slice_origin < data.size
|
|
820
|
+
splits << data.slice(slice_origin, slice_size)
|
|
821
|
+
slice_origin += slice_size
|
|
822
|
+
end
|
|
823
|
+
|
|
824
|
+
# This method will append events into the staged chunk at first.
|
|
825
|
+
# Then, will generate chunks not staged (not queued) to append rest data.
|
|
826
|
+
staged_chunk_used = false
|
|
827
|
+
modified_chunks = []
|
|
828
|
+
modified_metadata = metadata
|
|
829
|
+
get_next_chunk = ->(){
|
|
830
|
+
if staged_chunk_used
|
|
831
|
+
# Staging new chunk here is bad idea:
|
|
832
|
+
# Recovering whole state including newly staged chunks is much harder than current implementation.
|
|
833
|
+
modified_metadata = modified_metadata.dup_next
|
|
834
|
+
generate_chunk(modified_metadata)
|
|
835
|
+
else
|
|
836
|
+
synchronize { @stage[modified_metadata] ||= generate_chunk(modified_metadata).staged! }
|
|
837
|
+
end
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
writing_splits_index = 0
|
|
841
|
+
enqueue_chunk_before_retry = false
|
|
842
|
+
|
|
843
|
+
while writing_splits_index < splits.size
|
|
844
|
+
chunk = get_next_chunk.call
|
|
845
|
+
errors = []
|
|
846
|
+
# The chunk must be locked until being passed to &block.
|
|
847
|
+
chunk.mon_enter
|
|
848
|
+
modified_chunks << {chunk: chunk, adding_bytesize: 0, errors: errors}
|
|
849
|
+
|
|
850
|
+
raise ShouldRetry unless chunk.writable?
|
|
851
|
+
staged_chunk_used = true if chunk.staged?
|
|
852
|
+
|
|
853
|
+
original_bytesize = committed_bytesize = chunk.bytesize
|
|
854
|
+
begin
|
|
855
|
+
while writing_splits_index < splits.size
|
|
856
|
+
split = splits[writing_splits_index]
|
|
857
|
+
formatted_split = format ? format.call(split) : nil
|
|
858
|
+
|
|
859
|
+
if split.size == 1 # Check BufferChunkOverflowError
|
|
860
|
+
determined_bytesize = nil
|
|
861
|
+
if @compress != :text
|
|
862
|
+
determined_bytesize = nil
|
|
863
|
+
elsif formatted_split
|
|
864
|
+
determined_bytesize = formatted_split.bytesize
|
|
865
|
+
elsif split.first.respond_to?(:bytesize)
|
|
866
|
+
determined_bytesize = split.first.bytesize
|
|
867
|
+
end
|
|
868
|
+
|
|
869
|
+
if determined_bytesize && determined_bytesize > @chunk_limit_size
|
|
870
|
+
# It is a obvious case that BufferChunkOverflowError should be raised here.
|
|
871
|
+
# But if it raises here, already processed 'split' or
|
|
872
|
+
# the proceeding 'split' will be lost completely.
|
|
873
|
+
# So it is a last resort to delay raising such a exception
|
|
874
|
+
errors << "a #{determined_bytesize} bytes record (nth: #{writing_splits_index}) is larger than buffer chunk limit size (#{@chunk_limit_size})"
|
|
875
|
+
writing_splits_index += 1
|
|
876
|
+
next
|
|
877
|
+
end
|
|
878
|
+
|
|
879
|
+
if determined_bytesize.nil? || chunk.bytesize + determined_bytesize > @chunk_limit_size
|
|
880
|
+
# The split will (might) cause size over so keep already processed
|
|
881
|
+
# 'split' content here (allow performance regression a bit).
|
|
882
|
+
chunk.commit
|
|
883
|
+
committed_bytesize = chunk.bytesize
|
|
884
|
+
end
|
|
885
|
+
end
|
|
886
|
+
|
|
887
|
+
if format
|
|
888
|
+
chunk.concat(formatted_split, split.size)
|
|
889
|
+
else
|
|
890
|
+
chunk.append(split, compress: @compress)
|
|
891
|
+
end
|
|
892
|
+
adding_bytes = chunk.bytesize - committed_bytesize
|
|
893
|
+
|
|
894
|
+
if chunk_size_over?(chunk) # split size is larger than difference between size_full? and size_over?
|
|
895
|
+
chunk.rollback
|
|
896
|
+
committed_bytesize = chunk.bytesize
|
|
897
|
+
|
|
898
|
+
if split.size == 1 # Check BufferChunkOverflowError again
|
|
899
|
+
if adding_bytes > @chunk_limit_size
|
|
900
|
+
errors << "concatenated/appended a #{adding_bytes} bytes record (nth: #{writing_splits_index}) is larger than buffer chunk limit size (#{@chunk_limit_size})"
|
|
901
|
+
writing_splits_index += 1
|
|
902
|
+
next
|
|
903
|
+
else
|
|
904
|
+
# As already processed content is kept after rollback, then unstaged chunk should be queued.
|
|
905
|
+
# After that, re-process current split again.
|
|
906
|
+
# New chunk should be allocated, to do it, modify @stage and so on.
|
|
907
|
+
synchronize do
|
|
908
|
+
@stage.delete(modified_metadata)
|
|
909
|
+
# Subtract this chunk's already-counted staged bytes here;
|
|
910
|
+
# otherwise @stage_size leaks upward over the buffer's lifetime.
|
|
911
|
+
@stage_size_metrics.sub(original_bytesize) if chunk.staged?
|
|
912
|
+
end
|
|
913
|
+
staged_chunk_used = false
|
|
914
|
+
chunk.unstaged!
|
|
915
|
+
break
|
|
916
|
+
end
|
|
917
|
+
end
|
|
918
|
+
|
|
919
|
+
if chunk_size_full?(chunk) || split.size == 1
|
|
920
|
+
enqueue_chunk_before_retry = true
|
|
921
|
+
else
|
|
922
|
+
splits_count *= 10
|
|
923
|
+
end
|
|
924
|
+
|
|
925
|
+
raise ShouldRetry
|
|
926
|
+
end
|
|
927
|
+
|
|
928
|
+
writing_splits_index += 1
|
|
929
|
+
|
|
930
|
+
if chunk_size_full?(chunk)
|
|
931
|
+
break
|
|
932
|
+
end
|
|
933
|
+
end
|
|
934
|
+
rescue
|
|
935
|
+
chunk.purge if chunk.unstaged? # unstaged chunk will leak unless purge it
|
|
936
|
+
raise
|
|
937
|
+
end
|
|
938
|
+
|
|
939
|
+
modified_chunks.last[:adding_bytesize] = chunk.bytesize - original_bytesize
|
|
940
|
+
end
|
|
941
|
+
modified_chunks.each do |data|
|
|
942
|
+
block.call(data[:chunk], data[:adding_bytesize], data[:errors])
|
|
943
|
+
end
|
|
944
|
+
rescue ShouldRetry
|
|
945
|
+
modified_chunks.each do |data|
|
|
946
|
+
chunk = data[:chunk]
|
|
947
|
+
chunk.rollback rescue nil
|
|
948
|
+
if chunk.unstaged?
|
|
949
|
+
chunk.purge rescue nil
|
|
950
|
+
end
|
|
951
|
+
chunk.mon_exit rescue nil
|
|
952
|
+
end
|
|
953
|
+
enqueue_chunk(metadata) if enqueue_chunk_before_retry
|
|
954
|
+
retry
|
|
955
|
+
ensure
|
|
956
|
+
modified_chunks.each do |data|
|
|
957
|
+
chunk = data[:chunk]
|
|
958
|
+
chunk.mon_exit
|
|
959
|
+
end
|
|
960
|
+
end
|
|
961
|
+
|
|
962
|
+
STATS_KEYS = [
|
|
963
|
+
'stage_length',
|
|
964
|
+
'stage_byte_size',
|
|
965
|
+
'queue_length',
|
|
966
|
+
'queue_byte_size',
|
|
967
|
+
'available_buffer_space_ratios',
|
|
968
|
+
'total_queued_size',
|
|
969
|
+
'oldest_timekey',
|
|
970
|
+
'newest_timekey'
|
|
971
|
+
]
|
|
972
|
+
|
|
973
|
+
def statistics
|
|
974
|
+
# Export-only clamp: internal gauges may go transiently negative during
|
|
975
|
+
# the deferred stage_size add vs enqueue_chunk sub race (#5303, #2712).
|
|
976
|
+
# Clamping the gauge store itself would turn that into a permanent
|
|
977
|
+
# over-count and break Buffer#storable? -- keep raw gauge semantics.
|
|
978
|
+
stage_size = [@stage_size_metrics.get, 0].max
|
|
979
|
+
queue_size = [@queue_size_metrics.get, 0].max
|
|
980
|
+
denom = @total_limit_size.to_f
|
|
981
|
+
# denom > 0 already excludes 0/0 NaN; stage/queue are floored above.
|
|
982
|
+
buffer_space = denom > 0.0 ? (1.0 - (stage_size + queue_size).to_f / denom).clamp(0.0, 1.0) : 0.0
|
|
983
|
+
@stage_length_metrics.set(@stage.size)
|
|
984
|
+
@queue_length_metrics.set(@queue.size)
|
|
985
|
+
@available_buffer_space_ratios_metrics.set(buffer_space * 100)
|
|
986
|
+
@total_queued_size_metrics.set(stage_size + queue_size)
|
|
987
|
+
stats = {
|
|
988
|
+
'stage_length' => @stage_length_metrics.get,
|
|
989
|
+
'stage_byte_size' => stage_size,
|
|
990
|
+
'queue_length' => @queue_length_metrics.get,
|
|
991
|
+
'queue_byte_size' => queue_size,
|
|
992
|
+
'available_buffer_space_ratios' => @available_buffer_space_ratios_metrics.get.round(1),
|
|
993
|
+
'total_queued_size' => @total_queued_size_metrics.get,
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
tkeys = timekeys
|
|
997
|
+
if (m = tkeys.min)
|
|
998
|
+
@oldest_timekey_metrics.set(m)
|
|
999
|
+
stats['oldest_timekey'] = @oldest_timekey_metrics.get
|
|
1000
|
+
end
|
|
1001
|
+
if (m = tkeys.max)
|
|
1002
|
+
@newest_timekey_metrics.set(m)
|
|
1003
|
+
stats['newest_timekey'] = @newest_timekey_metrics.get
|
|
1004
|
+
end
|
|
1005
|
+
|
|
1006
|
+
{ 'buffer' => stats }
|
|
1007
|
+
end
|
|
1008
|
+
|
|
1009
|
+
def backup(chunk_unique_id)
|
|
1010
|
+
unique_id = dump_unique_id_hex(chunk_unique_id)
|
|
1011
|
+
|
|
1012
|
+
if @disable_chunk_backup
|
|
1013
|
+
log.warn "disable_chunk_backup is true. #{unique_id} chunk is not backed up."
|
|
1014
|
+
return
|
|
1015
|
+
end
|
|
1016
|
+
|
|
1017
|
+
backup_file = File.join(backup_base_dir, 'backup', "worker#{fluentd_worker_id}", safe_owner_id, "#{unique_id}.log")
|
|
1018
|
+
backup_dir = File.dirname(backup_file)
|
|
1019
|
+
|
|
1020
|
+
log.warn "bad chunk is moved to #{backup_file}"
|
|
1021
|
+
FileUtils.mkdir_p(backup_dir, mode: system_config.dir_permission || Fluent::DEFAULT_DIR_PERMISSION) unless Dir.exist?(backup_dir)
|
|
1022
|
+
File.open(backup_file, 'ab', system_config.file_permission || Fluent::DEFAULT_FILE_PERMISSION) { |f| yield f }
|
|
1023
|
+
end
|
|
1024
|
+
|
|
1025
|
+
private
|
|
1026
|
+
|
|
1027
|
+
def optimistic_queued?(metadata = nil)
|
|
1028
|
+
if metadata
|
|
1029
|
+
n = @queued_num[metadata]
|
|
1030
|
+
n&.nonzero?
|
|
1031
|
+
else
|
|
1032
|
+
!@queue.empty?
|
|
1033
|
+
end
|
|
1034
|
+
end
|
|
1035
|
+
|
|
1036
|
+
def safe_owner_id
|
|
1037
|
+
owner.plugin_id.gsub(/[ "\/\\:;|*<>?]/, '_')
|
|
1038
|
+
end
|
|
1039
|
+
|
|
1040
|
+
def backup_base_dir
|
|
1041
|
+
system_config.root_dir || DEFAULT_BACKUP_DIR
|
|
1042
|
+
end
|
|
1043
|
+
|
|
1044
|
+
def evacuate_dir
|
|
1045
|
+
File.join(backup_base_dir, 'buffer', safe_owner_id)
|
|
1046
|
+
end
|
|
1047
|
+
|
|
1048
|
+
def evacuated_bytesize
|
|
1049
|
+
dir = evacuate_dir
|
|
1050
|
+
Dir.children(dir, encoding: dir.encoding).sum { |name| File.size?(File.join(dir, name)).to_i }
|
|
1051
|
+
rescue Errno::ENOENT
|
|
1052
|
+
0
|
|
1053
|
+
end
|
|
1054
|
+
|
|
1055
|
+
def remaining_evacuate_size
|
|
1056
|
+
@evacuate_limit_size - evacuated_bytesize
|
|
1057
|
+
rescue => e
|
|
1058
|
+
log.warn "failed to get the size of the evacuated chunk files. queued chunks are purged without being evacuated.", error: e
|
|
1059
|
+
0
|
|
1060
|
+
end
|
|
1061
|
+
end
|
|
1062
|
+
end
|
|
1063
|
+
end
|