fluentd 0.12.40 → 1.6.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of fluentd might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
- data/.github/ISSUE_TEMPLATE.md +17 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +13 -0
- data/.gitignore +5 -0
- data/.gitlab/cicd-template.yaml +10 -0
- data/.gitlab-ci.yml +147 -0
- data/.travis.yml +56 -20
- data/ADOPTERS.md +5 -0
- data/CHANGELOG.md +1369 -0
- data/CONTRIBUTING.md +16 -5
- data/GOVERNANCE.md +55 -0
- data/Gemfile +5 -0
- data/GithubWorkflow.md +78 -0
- data/LICENSE +202 -0
- data/MAINTAINERS.md +7 -0
- data/README.md +23 -11
- data/Rakefile +48 -2
- data/Vagrantfile +17 -0
- data/appveyor.yml +37 -0
- data/bin/fluent-binlog-reader +7 -0
- data/bin/fluent-ca-generate +6 -0
- data/bin/fluent-plugin-config-format +5 -0
- data/bin/fluent-plugin-generate +5 -0
- data/bin/fluentd +3 -0
- data/code-of-conduct.md +3 -0
- data/example/copy_roundrobin.conf +39 -0
- data/example/counter.conf +18 -0
- data/example/in_dummy_blocks.conf +17 -0
- data/example/in_dummy_with_compression.conf +23 -0
- data/example/in_forward.conf +7 -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 +3 -1
- data/example/in_out_forward.conf +17 -0
- data/example/logevents.conf +25 -0
- data/example/multi_filters.conf +61 -0
- data/example/out_exec_filter.conf +42 -0
- data/example/out_forward.conf +13 -13
- 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_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/secondary_file.conf +42 -0
- data/example/suppress_config_dump.conf +7 -0
- data/example/worker_section.conf +36 -0
- data/fluent.conf +29 -0
- data/fluentd.gemspec +21 -11
- data/lib/fluent/agent.rb +67 -90
- data/lib/fluent/clock.rb +62 -0
- data/lib/fluent/command/binlog_reader.rb +244 -0
- data/lib/fluent/command/ca_generate.rb +181 -0
- data/lib/fluent/command/cat.rb +42 -18
- data/lib/fluent/command/debug.rb +12 -10
- data/lib/fluent/command/fluentd.rb +153 -5
- data/lib/fluent/command/plugin_config_formatter.rb +292 -0
- data/lib/fluent/command/plugin_generator.rb +324 -0
- data/lib/fluent/compat/call_super_mixin.rb +67 -0
- data/lib/fluent/compat/detach_process_mixin.rb +33 -0
- data/lib/fluent/compat/exec_util.rb +129 -0
- data/lib/fluent/compat/file_util.rb +54 -0
- data/lib/fluent/compat/filter.rb +68 -0
- data/lib/fluent/compat/formatter.rb +111 -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 +718 -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/configure_proxy.rb +210 -62
- data/lib/fluent/config/dsl.rb +12 -5
- data/lib/fluent/config/element.rb +107 -9
- data/lib/fluent/config/literal_parser.rb +9 -3
- data/lib/fluent/config/parser.rb +4 -4
- data/lib/fluent/config/section.rb +51 -14
- data/lib/fluent/config/types.rb +28 -13
- data/lib/fluent/config/v1_parser.rb +3 -5
- data/lib/fluent/config.rb +23 -20
- data/lib/fluent/configurable.rb +79 -21
- data/lib/fluent/counter/base_socket.rb +46 -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 +15 -0
- data/lib/fluent/engine.rb +102 -65
- data/lib/fluent/env.rb +7 -3
- data/lib/fluent/error.rb +30 -0
- data/lib/fluent/event.rb +197 -21
- data/lib/fluent/event_router.rb +93 -10
- data/lib/fluent/filter.rb +2 -50
- data/lib/fluent/formatter.rb +4 -293
- data/lib/fluent/input.rb +2 -32
- data/lib/fluent/label.rb +10 -2
- data/lib/fluent/load.rb +3 -3
- data/lib/fluent/log.rb +348 -81
- data/lib/fluent/match.rb +37 -36
- data/lib/fluent/mixin.rb +12 -176
- data/lib/fluent/msgpack_factory.rb +62 -0
- data/lib/fluent/output.rb +10 -612
- data/lib/fluent/output_chain.rb +23 -0
- data/lib/fluent/parser.rb +4 -800
- data/lib/fluent/plugin/bare_output.rb +63 -0
- data/lib/fluent/plugin/base.rb +192 -0
- data/lib/fluent/plugin/buf_file.rb +128 -174
- data/lib/fluent/plugin/buf_memory.rb +9 -92
- data/lib/fluent/plugin/buffer/chunk.rb +221 -0
- data/lib/fluent/plugin/buffer/file_chunk.rb +383 -0
- data/lib/fluent/plugin/buffer/memory_chunk.rb +90 -0
- data/lib/fluent/plugin/buffer.rb +779 -0
- data/lib/fluent/plugin/compressable.rb +92 -0
- data/lib/fluent/plugin/exec_util.rb +3 -108
- data/lib/fluent/plugin/file_util.rb +4 -34
- data/lib/fluent/plugin/file_wrapper.rb +120 -0
- data/lib/fluent/plugin/filter.rb +93 -0
- data/lib/fluent/plugin/filter_grep.rb +117 -34
- data/lib/fluent/plugin/filter_parser.rb +85 -62
- data/lib/fluent/plugin/filter_record_transformer.rb +27 -39
- data/lib/fluent/plugin/filter_stdout.rb +15 -12
- data/lib/fluent/plugin/formatter.rb +50 -0
- data/lib/fluent/plugin/formatter_csv.rb +52 -0
- data/lib/fluent/plugin/formatter_hash.rb +33 -0
- data/lib/fluent/plugin/formatter_json.rb +55 -0
- data/lib/fluent/plugin/formatter_ltsv.rb +42 -0
- data/lib/fluent/plugin/formatter_msgpack.rb +33 -0
- data/lib/fluent/plugin/formatter_out_file.rb +51 -0
- data/lib/fluent/plugin/formatter_single_value.rb +34 -0
- data/lib/fluent/plugin/formatter_stdout.rb +76 -0
- data/lib/fluent/plugin/formatter_tsv.rb +38 -0
- data/lib/fluent/plugin/in_debug_agent.rb +17 -6
- data/lib/fluent/plugin/in_dummy.rb +47 -20
- data/lib/fluent/plugin/in_exec.rb +55 -123
- data/lib/fluent/plugin/in_forward.rb +299 -216
- data/lib/fluent/plugin/in_gc_stat.rb +14 -36
- data/lib/fluent/plugin/in_http.rb +204 -91
- data/lib/fluent/plugin/in_monitor_agent.rb +186 -258
- data/lib/fluent/plugin/in_object_space.rb +13 -41
- data/lib/fluent/plugin/in_syslog.rb +112 -134
- data/lib/fluent/plugin/in_tail.rb +408 -745
- data/lib/fluent/plugin/in_tcp.rb +66 -9
- data/lib/fluent/plugin/in_udp.rb +60 -11
- data/lib/fluent/plugin/{in_stream.rb → in_unix.rb} +8 -4
- data/lib/fluent/plugin/input.rb +37 -0
- data/lib/fluent/plugin/multi_output.rb +158 -0
- data/lib/fluent/plugin/out_copy.rb +23 -35
- data/lib/fluent/plugin/out_exec.rb +67 -70
- data/lib/fluent/plugin/out_exec_filter.rb +204 -271
- data/lib/fluent/plugin/out_file.rb +267 -73
- data/lib/fluent/plugin/out_forward.rb +854 -325
- data/lib/fluent/plugin/out_null.rb +42 -9
- data/lib/fluent/plugin/out_relabel.rb +9 -5
- data/lib/fluent/plugin/out_roundrobin.rb +18 -37
- data/lib/fluent/plugin/out_secondary_file.rb +133 -0
- data/lib/fluent/plugin/out_stdout.rb +43 -10
- data/lib/fluent/plugin/out_stream.rb +7 -2
- data/lib/fluent/plugin/output.rb +1498 -0
- data/lib/fluent/plugin/owned_by_mixin.rb +42 -0
- data/lib/fluent/plugin/parser.rb +191 -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 +39 -0
- data/lib/fluent/plugin/parser_json.rb +94 -0
- data/lib/fluent/plugin/parser_ltsv.rb +49 -0
- data/lib/fluent/plugin/parser_msgpack.rb +50 -0
- data/lib/fluent/plugin/parser_multiline.rb +106 -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 +142 -0
- data/lib/fluent/plugin/parser_tsv.rb +42 -0
- data/lib/fluent/plugin/socket_util.rb +3 -143
- data/lib/fluent/plugin/storage.rb +84 -0
- data/lib/fluent/plugin/storage_local.rb +164 -0
- data/lib/fluent/plugin/string_util.rb +3 -15
- data/lib/fluent/plugin.rb +122 -121
- data/lib/fluent/plugin_helper/cert_option.rb +178 -0
- data/lib/fluent/plugin_helper/child_process.rb +364 -0
- data/lib/fluent/plugin_helper/compat_parameters.rb +333 -0
- data/lib/fluent/plugin_helper/counter.rb +51 -0
- data/lib/fluent/plugin_helper/event_emitter.rb +93 -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 +79 -0
- data/lib/fluent/plugin_helper/http_server/compat/server.rb +81 -0
- data/lib/fluent/plugin_helper/http_server/compat/webrick_handler.rb +58 -0
- data/lib/fluent/plugin_helper/http_server/methods.rb +35 -0
- data/lib/fluent/plugin_helper/http_server/request.rb +42 -0
- data/lib/fluent/plugin_helper/http_server/router.rb +54 -0
- data/lib/fluent/plugin_helper/http_server/server.rb +87 -0
- data/lib/fluent/plugin_helper/http_server.rb +76 -0
- data/lib/fluent/plugin_helper/inject.rb +151 -0
- data/lib/fluent/plugin_helper/parser.rb +147 -0
- data/lib/fluent/plugin_helper/record_accessor.rb +210 -0
- data/lib/fluent/plugin_helper/retry_state.rb +205 -0
- data/lib/fluent/plugin_helper/server.rb +807 -0
- data/lib/fluent/plugin_helper/socket.rb +250 -0
- data/lib/fluent/plugin_helper/socket_option.rb +80 -0
- data/lib/fluent/plugin_helper/storage.rb +349 -0
- data/lib/fluent/plugin_helper/thread.rb +179 -0
- data/lib/fluent/plugin_helper/timer.rb +92 -0
- data/lib/fluent/plugin_helper.rb +73 -0
- data/lib/fluent/plugin_id.rb +80 -0
- data/lib/fluent/process.rb +3 -489
- data/lib/fluent/registry.rb +52 -10
- data/lib/fluent/root_agent.rb +204 -42
- data/lib/fluent/supervisor.rb +597 -359
- data/lib/fluent/system_config.rb +131 -42
- data/lib/fluent/test/base.rb +6 -54
- data/lib/fluent/test/driver/base.rb +224 -0
- data/lib/fluent/test/driver/base_owned.rb +70 -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 +57 -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/test_event_router.rb +45 -0
- data/lib/fluent/test/filter_test.rb +0 -1
- data/lib/fluent/test/formatter_test.rb +4 -1
- data/lib/fluent/test/helpers.rb +58 -10
- data/lib/fluent/test/input_test.rb +27 -19
- data/lib/fluent/test/log.rb +79 -0
- data/lib/fluent/test/output_test.rb +28 -39
- data/lib/fluent/test/parser_test.rb +3 -1
- data/lib/fluent/test/startup_shutdown.rb +46 -0
- data/lib/fluent/test.rb +33 -1
- data/lib/fluent/time.rb +450 -1
- data/lib/fluent/timezone.rb +27 -3
- data/lib/fluent/{status.rb → unique_id.rb} +15 -24
- data/lib/fluent/version.rb +1 -1
- data/lib/fluent/winsvc.rb +85 -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 +27 -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/test/helper.rb.erb +8 -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/plugin_config_formatter/param.md-compact.erb +25 -0
- data/templates/plugin_config_formatter/param.md.erb +34 -0
- data/templates/plugin_config_formatter/section.md.erb +12 -0
- data/test/command/test_binlog_reader.rb +346 -0
- data/test/command/test_ca_generate.rb +70 -0
- data/test/command/test_fluentd.rb +901 -0
- data/test/command/test_plugin_config_formatter.rb +276 -0
- data/test/command/test_plugin_generator.rb +92 -0
- data/test/compat/test_calls_super.rb +166 -0
- data/test/compat/test_parser.rb +92 -0
- data/test/config/test_config_parser.rb +126 -2
- data/test/config/test_configurable.rb +946 -187
- data/test/config/test_configure_proxy.rb +424 -74
- data/test/config/test_dsl.rb +11 -11
- data/test/config/test_element.rb +500 -0
- data/test/config/test_literal_parser.rb +8 -0
- data/test/config/test_plugin_configuration.rb +56 -0
- data/test/config/test_section.rb +79 -7
- data/test/config/test_system_config.rb +122 -35
- data/test/config/test_types.rb +38 -0
- data/test/counter/test_client.rb +559 -0
- data/test/counter/test_error.rb +44 -0
- data/test/counter/test_mutex_hash.rb +179 -0
- data/test/counter/test_server.rb +589 -0
- data/test/counter/test_store.rb +258 -0
- data/test/counter/test_validator.rb +137 -0
- data/test/helper.rb +89 -6
- data/test/helpers/fuzzy_assert.rb +89 -0
- data/test/plugin/test_bare_output.rb +118 -0
- data/test/plugin/test_base.rb +115 -0
- data/test/plugin/test_buf_file.rb +823 -460
- data/test/plugin/test_buf_memory.rb +32 -194
- data/test/plugin/test_buffer.rb +1233 -0
- data/test/plugin/test_buffer_chunk.rb +198 -0
- data/test/plugin/test_buffer_file_chunk.rb +844 -0
- data/test/plugin/test_buffer_memory_chunk.rb +338 -0
- data/test/plugin/test_compressable.rb +84 -0
- data/test/plugin/test_filter.rb +357 -0
- data/test/plugin/test_filter_grep.rb +540 -29
- data/test/plugin/test_filter_parser.rb +439 -452
- data/test/plugin/test_filter_record_transformer.rb +123 -166
- data/test/plugin/test_filter_stdout.rb +160 -72
- data/test/plugin/test_formatter_csv.rb +111 -0
- data/test/plugin/test_formatter_hash.rb +35 -0
- data/test/plugin/test_formatter_json.rb +51 -0
- data/test/plugin/test_formatter_ltsv.rb +62 -0
- data/test/plugin/test_formatter_msgpack.rb +28 -0
- data/test/plugin/test_formatter_out_file.rb +95 -0
- data/test/plugin/test_formatter_single_value.rb +38 -0
- data/test/plugin/test_formatter_tsv.rb +68 -0
- data/test/plugin/test_in_debug_agent.rb +24 -1
- data/test/plugin/test_in_dummy.rb +111 -18
- data/test/plugin/test_in_exec.rb +200 -113
- data/test/plugin/test_in_forward.rb +990 -387
- data/test/plugin/test_in_gc_stat.rb +10 -8
- data/test/plugin/test_in_http.rb +600 -224
- data/test/plugin/test_in_monitor_agent.rb +690 -0
- data/test/plugin/test_in_object_space.rb +24 -8
- data/test/plugin/test_in_syslog.rb +154 -215
- data/test/plugin/test_in_tail.rb +1006 -707
- data/test/plugin/test_in_tcp.rb +125 -48
- data/test/plugin/test_in_udp.rb +204 -63
- data/test/plugin/{test_in_stream.rb → test_in_unix.rb} +14 -13
- data/test/plugin/test_input.rb +126 -0
- data/test/plugin/test_metadata.rb +89 -0
- data/test/plugin/test_multi_output.rb +180 -0
- data/test/plugin/test_out_copy.rb +117 -112
- data/test/plugin/test_out_exec.rb +258 -53
- data/test/plugin/test_out_exec_filter.rb +538 -115
- data/test/plugin/test_out_file.rb +865 -178
- data/test/plugin/test_out_forward.rb +998 -210
- data/test/plugin/test_out_null.rb +105 -0
- data/test/plugin/test_out_relabel.rb +28 -0
- data/test/plugin/test_out_roundrobin.rb +36 -29
- data/test/plugin/test_out_secondary_file.rb +458 -0
- data/test/plugin/test_out_stdout.rb +135 -37
- data/test/plugin/test_out_stream.rb +18 -0
- data/test/plugin/test_output.rb +984 -0
- data/test/plugin/test_output_as_buffered.rb +2021 -0
- data/test/plugin/test_output_as_buffered_backup.rb +312 -0
- data/test/plugin/test_output_as_buffered_compress.rb +165 -0
- data/test/plugin/test_output_as_buffered_overflow.rb +250 -0
- data/test/plugin/test_output_as_buffered_retries.rb +911 -0
- data/test/plugin/test_output_as_buffered_secondary.rb +874 -0
- data/test/plugin/test_output_as_standard.rb +374 -0
- data/test/plugin/test_owned_by.rb +35 -0
- data/test/plugin/test_parser.rb +359 -0
- data/test/plugin/test_parser_apache.rb +42 -0
- data/test/plugin/test_parser_apache2.rb +47 -0
- data/test/plugin/test_parser_apache_error.rb +45 -0
- data/test/plugin/test_parser_csv.rb +103 -0
- data/test/plugin/test_parser_json.rb +138 -0
- data/test/plugin/test_parser_labeled_tsv.rb +145 -0
- data/test/plugin/test_parser_multiline.rb +100 -0
- data/test/plugin/test_parser_nginx.rb +88 -0
- data/test/plugin/test_parser_none.rb +52 -0
- data/test/plugin/test_parser_regexp.rb +289 -0
- data/test/plugin/test_parser_syslog.rb +441 -0
- data/test/plugin/test_parser_tsv.rb +122 -0
- data/test/plugin/test_storage.rb +167 -0
- data/test/plugin/test_storage_local.rb +335 -0
- data/test/plugin_helper/data/cert/cert-key.pem +27 -0
- data/test/plugin_helper/data/cert/cert-with-no-newline.pem +19 -0
- data/test/plugin_helper/data/cert/cert.pem +19 -0
- data/test/plugin_helper/http_server/test_app.rb +65 -0
- data/test/plugin_helper/http_server/test_route.rb +32 -0
- data/test/plugin_helper/test_cert_option.rb +16 -0
- data/test/plugin_helper/test_child_process.rb +794 -0
- data/test/plugin_helper/test_compat_parameters.rb +353 -0
- data/test/plugin_helper/test_event_emitter.rb +51 -0
- data/test/plugin_helper/test_event_loop.rb +52 -0
- data/test/plugin_helper/test_extract.rb +194 -0
- data/test/plugin_helper/test_formatter.rb +255 -0
- data/test/plugin_helper/test_http_server_helper.rb +205 -0
- data/test/plugin_helper/test_inject.rb +519 -0
- data/test/plugin_helper/test_parser.rb +264 -0
- data/test/plugin_helper/test_record_accessor.rb +197 -0
- data/test/plugin_helper/test_retry_state.rb +442 -0
- data/test/plugin_helper/test_server.rb +1714 -0
- data/test/plugin_helper/test_storage.rb +542 -0
- data/test/plugin_helper/test_thread.rb +164 -0
- data/test/plugin_helper/test_timer.rb +132 -0
- data/test/scripts/exec_script.rb +0 -6
- data/test/scripts/fluent/plugin/formatter1/formatter_test1.rb +7 -0
- data/test/scripts/fluent/plugin/formatter2/formatter_test2.rb +7 -0
- data/test/scripts/fluent/plugin/out_test.rb +23 -15
- data/test/scripts/fluent/plugin/out_test2.rb +80 -0
- data/test/test_clock.rb +164 -0
- data/test/test_config.rb +16 -7
- data/test/test_configdsl.rb +2 -2
- data/test/test_event.rb +360 -13
- data/test/test_event_router.rb +108 -11
- data/test/test_event_time.rb +199 -0
- data/test/test_filter.rb +48 -6
- data/test/test_formatter.rb +11 -391
- data/test/test_input.rb +1 -1
- data/test/test_log.rb +591 -31
- data/test/test_mixin.rb +1 -1
- data/test/test_output.rb +121 -185
- data/test/test_plugin.rb +251 -0
- data/test/test_plugin_classes.rb +177 -10
- data/test/test_plugin_helper.rb +81 -0
- data/test/test_plugin_id.rb +101 -0
- data/test/test_process.rb +8 -42
- data/test/test_root_agent.rb +766 -21
- data/test/test_supervisor.rb +481 -0
- data/test/test_test_drivers.rb +135 -0
- data/test/test_time_formatter.rb +282 -0
- data/test/test_time_parser.rb +231 -0
- data/test/test_unique_id.rb +47 -0
- metadata +454 -60
- data/COPYING +0 -14
- data/ChangeLog +0 -666
- data/lib/fluent/buffer.rb +0 -365
- data/lib/fluent/plugin/in_status.rb +0 -76
- data/test/plugin/test_in_status.rb +0 -38
- data/test/test_buffer.rb +0 -624
- data/test/test_parser.rb +0 -1305
@@ -0,0 +1,779 @@
|
|
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/unique_id'
|
20
|
+
|
21
|
+
require 'monitor'
|
22
|
+
|
23
|
+
module Fluent
|
24
|
+
module Plugin
|
25
|
+
class Buffer < Base
|
26
|
+
include OwnedByMixin
|
27
|
+
include UniqueId::Mixin
|
28
|
+
include MonitorMixin
|
29
|
+
|
30
|
+
class BufferError < StandardError; end
|
31
|
+
class BufferOverflowError < BufferError; end
|
32
|
+
class BufferChunkOverflowError < BufferError; end # A record size is larger than chunk size limit
|
33
|
+
|
34
|
+
MINIMUM_APPEND_ATTEMPT_RECORDS = 10
|
35
|
+
|
36
|
+
DEFAULT_CHUNK_LIMIT_SIZE = 8 * 1024 * 1024 # 8MB
|
37
|
+
DEFAULT_TOTAL_LIMIT_SIZE = 512 * 1024 * 1024 # 512MB, same with v0.12 (BufferedOutput + buf_memory: 64 x 8MB)
|
38
|
+
|
39
|
+
DEFAULT_CHUNK_FULL_THRESHOLD = 0.95
|
40
|
+
|
41
|
+
configured_in :buffer
|
42
|
+
|
43
|
+
# TODO: system total buffer limit size in bytes by SystemConfig
|
44
|
+
|
45
|
+
config_param :chunk_limit_size, :size, default: DEFAULT_CHUNK_LIMIT_SIZE
|
46
|
+
config_param :total_limit_size, :size, default: DEFAULT_TOTAL_LIMIT_SIZE
|
47
|
+
|
48
|
+
# If user specify this value and (chunk_size * queue_length) is smaller than total_size,
|
49
|
+
# then total_size is automatically configured to that value
|
50
|
+
config_param :queue_limit_length, :integer, default: nil
|
51
|
+
|
52
|
+
# optional new limitations
|
53
|
+
config_param :chunk_limit_records, :integer, default: nil
|
54
|
+
|
55
|
+
# if chunk size (or records) is 95% or more after #write, then that chunk will be enqueued
|
56
|
+
config_param :chunk_full_threshold, :float, default: DEFAULT_CHUNK_FULL_THRESHOLD
|
57
|
+
|
58
|
+
desc 'The max number of queued chunks.'
|
59
|
+
config_param :queued_chunks_limit_size, :integer, default: nil
|
60
|
+
|
61
|
+
desc 'Compress buffered data.'
|
62
|
+
config_param :compress, :enum, list: [:text, :gzip], default: :text
|
63
|
+
|
64
|
+
Metadata = Struct.new(:timekey, :tag, :variables) do
|
65
|
+
def empty?
|
66
|
+
timekey.nil? && tag.nil? && variables.nil?
|
67
|
+
end
|
68
|
+
|
69
|
+
def cmp_variables(v1, v2)
|
70
|
+
if v1.nil? && v2.nil?
|
71
|
+
return 0
|
72
|
+
elsif v1.nil? # v2 is non-nil
|
73
|
+
return -1
|
74
|
+
elsif v2.nil? # v1 is non-nil
|
75
|
+
return 1
|
76
|
+
end
|
77
|
+
# both of v1 and v2 are non-nil
|
78
|
+
v1_sorted_keys = v1.keys.sort
|
79
|
+
v2_sorted_keys = v2.keys.sort
|
80
|
+
if v1_sorted_keys != v2_sorted_keys
|
81
|
+
if v1_sorted_keys.size == v2_sorted_keys.size
|
82
|
+
v1_sorted_keys <=> v2_sorted_keys
|
83
|
+
else
|
84
|
+
v1_sorted_keys.size <=> v2_sorted_keys.size
|
85
|
+
end
|
86
|
+
else
|
87
|
+
v1_sorted_keys.each do |k|
|
88
|
+
a = v1[k]
|
89
|
+
b = v2[k]
|
90
|
+
if a && b && a != b
|
91
|
+
return a <=> b
|
92
|
+
elsif a && b || (!a && !b) # same value (including both are nil)
|
93
|
+
next
|
94
|
+
elsif a # b is nil
|
95
|
+
return 1
|
96
|
+
else # a is nil (but b is non-nil)
|
97
|
+
return -1
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
0
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def <=>(o)
|
106
|
+
timekey2 = o.timekey
|
107
|
+
tag2 = o.tag
|
108
|
+
variables2 = o.variables
|
109
|
+
if (!!timekey ^ !!timekey2) || (!!tag ^ !!tag2) || (!!variables ^ !!variables2)
|
110
|
+
# One has value in a field, but another doesn't have value in same field
|
111
|
+
# This case occurs very rarely
|
112
|
+
if timekey == timekey2 # including the case of nil == nil
|
113
|
+
if tag == tag2
|
114
|
+
cmp_variables(variables, variables2)
|
115
|
+
elsif tag.nil?
|
116
|
+
-1
|
117
|
+
elsif tag2.nil?
|
118
|
+
1
|
119
|
+
else
|
120
|
+
tag <=> tag2
|
121
|
+
end
|
122
|
+
elsif timekey.nil?
|
123
|
+
-1
|
124
|
+
elsif timekey2.nil?
|
125
|
+
1
|
126
|
+
else
|
127
|
+
timekey <=> timekey2
|
128
|
+
end
|
129
|
+
else
|
130
|
+
# objects have values in same field pairs (comparison with non-nil and nil doesn't occur here)
|
131
|
+
(timekey <=> timekey2 || 0).nonzero? || # if `a <=> b` is nil, then both are nil
|
132
|
+
(tag <=> tag2 || 0).nonzero? ||
|
133
|
+
cmp_variables(variables, variables2)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# for tests
|
139
|
+
attr_accessor :stage_size, :queue_size
|
140
|
+
attr_reader :stage, :queue, :dequeued, :queued_num
|
141
|
+
|
142
|
+
def initialize
|
143
|
+
super
|
144
|
+
|
145
|
+
@chunk_limit_size = nil
|
146
|
+
@total_limit_size = nil
|
147
|
+
@queue_limit_length = nil
|
148
|
+
@chunk_limit_records = nil
|
149
|
+
|
150
|
+
@stage = {} #=> Hash (metadata -> chunk) : not flushed yet
|
151
|
+
@queue = [] #=> Array (chunks) : already flushed (not written)
|
152
|
+
@dequeued = {} #=> Hash (unique_id -> chunk): already written (not purged)
|
153
|
+
@queued_num = {} # metadata => int (number of queued chunks)
|
154
|
+
@dequeued_num = {} # metadata => int (number of dequeued chunks)
|
155
|
+
|
156
|
+
@stage_size = @queue_size = 0
|
157
|
+
@timekeys = Hash.new(0)
|
158
|
+
@metadata_list = [] # keys of @stage
|
159
|
+
end
|
160
|
+
|
161
|
+
def persistent?
|
162
|
+
false
|
163
|
+
end
|
164
|
+
|
165
|
+
def configure(conf)
|
166
|
+
super
|
167
|
+
|
168
|
+
unless @queue_limit_length.nil?
|
169
|
+
@total_limit_size = @chunk_limit_size * @queue_limit_length
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def start
|
174
|
+
super
|
175
|
+
|
176
|
+
@stage, @queue = resume
|
177
|
+
@stage.each_pair do |metadata, chunk|
|
178
|
+
@metadata_list << metadata unless @metadata_list.include?(metadata)
|
179
|
+
@stage_size += chunk.bytesize
|
180
|
+
add_timekey(metadata)
|
181
|
+
end
|
182
|
+
@queue.each do |chunk|
|
183
|
+
@metadata_list << chunk.metadata unless @metadata_list.include?(chunk.metadata)
|
184
|
+
@queued_num[chunk.metadata] ||= 0
|
185
|
+
@queued_num[chunk.metadata] += 1
|
186
|
+
@queue_size += chunk.bytesize
|
187
|
+
add_timekey(chunk.metadata)
|
188
|
+
end
|
189
|
+
log.debug "buffer started", instance: self.object_id, stage_size: @stage_size, queue_size: @queue_size
|
190
|
+
end
|
191
|
+
|
192
|
+
def close
|
193
|
+
super
|
194
|
+
synchronize do
|
195
|
+
log.debug "closing buffer", instance: self.object_id
|
196
|
+
@dequeued.each_pair do |chunk_id, chunk|
|
197
|
+
chunk.close
|
198
|
+
end
|
199
|
+
until @queue.empty?
|
200
|
+
@queue.shift.close
|
201
|
+
end
|
202
|
+
@stage.each_pair do |metadata, chunk|
|
203
|
+
chunk.close
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def terminate
|
209
|
+
super
|
210
|
+
@dequeued = @stage = @queue = @queued_num = @metadata_list = nil
|
211
|
+
@stage_size = @queue_size = 0
|
212
|
+
@timekeys.clear
|
213
|
+
end
|
214
|
+
|
215
|
+
def storable?
|
216
|
+
@total_limit_size > @stage_size + @queue_size
|
217
|
+
end
|
218
|
+
|
219
|
+
## TODO: for back pressure feature
|
220
|
+
# def used?(ratio)
|
221
|
+
# @total_limit_size * ratio > @stage_size + @queue_size
|
222
|
+
# end
|
223
|
+
|
224
|
+
def resume
|
225
|
+
# return {}, []
|
226
|
+
raise NotImplementedError, "Implement this method in child class"
|
227
|
+
end
|
228
|
+
|
229
|
+
def generate_chunk(metadata)
|
230
|
+
raise NotImplementedError, "Implement this method in child class"
|
231
|
+
end
|
232
|
+
|
233
|
+
def metadata_list
|
234
|
+
synchronize do
|
235
|
+
@metadata_list.dup
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
# it's too dangerous, and use it so carefully to remove metadata for tests
|
240
|
+
def metadata_list_clear!
|
241
|
+
synchronize do
|
242
|
+
@metadata_list.clear
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def new_metadata(timekey: nil, tag: nil, variables: nil)
|
247
|
+
Metadata.new(timekey, tag, variables)
|
248
|
+
end
|
249
|
+
|
250
|
+
def add_metadata(metadata)
|
251
|
+
log.on_trace { log.trace "adding metadata", instance: self.object_id, metadata: metadata }
|
252
|
+
|
253
|
+
synchronize do
|
254
|
+
if i = @metadata_list.index(metadata)
|
255
|
+
@metadata_list[i]
|
256
|
+
else
|
257
|
+
@metadata_list << metadata
|
258
|
+
add_timekey(metadata)
|
259
|
+
metadata
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
def metadata(timekey: nil, tag: nil, variables: nil)
|
265
|
+
meta = new_metadata(timekey: timekey, tag: tag, variables: variables)
|
266
|
+
add_metadata(meta)
|
267
|
+
end
|
268
|
+
|
269
|
+
def add_timekey(metadata)
|
270
|
+
if t = metadata.timekey
|
271
|
+
@timekeys[t] += 1
|
272
|
+
end
|
273
|
+
nil
|
274
|
+
end
|
275
|
+
private :add_timekey
|
276
|
+
|
277
|
+
def del_timekey(metadata)
|
278
|
+
if t = metadata.timekey
|
279
|
+
if @timekeys[t] <= 1
|
280
|
+
@timekeys.delete(t)
|
281
|
+
else
|
282
|
+
@timekeys[t] -= 1
|
283
|
+
end
|
284
|
+
end
|
285
|
+
nil
|
286
|
+
end
|
287
|
+
private :del_timekey
|
288
|
+
|
289
|
+
def timekeys
|
290
|
+
@timekeys.keys
|
291
|
+
end
|
292
|
+
|
293
|
+
# metadata MUST have consistent object_id for each variation
|
294
|
+
# data MUST be Array of serialized events, or EventStream
|
295
|
+
# metadata_and_data MUST be a hash of { metadata => data }
|
296
|
+
def write(metadata_and_data, format: nil, size: nil, enqueue: false)
|
297
|
+
return if metadata_and_data.size < 1
|
298
|
+
raise BufferOverflowError, "buffer space has too many data" unless storable?
|
299
|
+
|
300
|
+
log.on_trace { log.trace "writing events into buffer", instance: self.object_id, metadata_size: metadata_and_data.size }
|
301
|
+
|
302
|
+
staged_bytesize = 0
|
303
|
+
operated_chunks = []
|
304
|
+
unstaged_chunks = {} # metadata => [chunk, chunk, ...]
|
305
|
+
chunks_to_enqueue = []
|
306
|
+
|
307
|
+
begin
|
308
|
+
# sort metadata to get lock of chunks in same order with other threads
|
309
|
+
metadata_and_data.keys.sort.each do |metadata|
|
310
|
+
data = metadata_and_data[metadata]
|
311
|
+
write_once(metadata, data, format: format, size: size) do |chunk, adding_bytesize|
|
312
|
+
chunk.mon_enter # add lock to prevent to be committed/rollbacked from other threads
|
313
|
+
operated_chunks << chunk
|
314
|
+
if chunk.staged?
|
315
|
+
staged_bytesize += adding_bytesize
|
316
|
+
elsif chunk.unstaged?
|
317
|
+
unstaged_chunks[metadata] ||= []
|
318
|
+
unstaged_chunks[metadata] << chunk
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
return if operated_chunks.empty?
|
324
|
+
|
325
|
+
# Now, this thread acquires many locks of chunks... getting buffer-global lock causes dead lock.
|
326
|
+
# Any operations needs buffer-global lock (including enqueueing) should be done after releasing locks.
|
327
|
+
|
328
|
+
first_chunk = operated_chunks.shift
|
329
|
+
# Following commits for other chunks also can finish successfully if the first commit operation
|
330
|
+
# finishes without any exceptions.
|
331
|
+
# In most cases, #commit just requires very small disk spaces, so major failure reason are
|
332
|
+
# permission errors, disk failures and other permanent(fatal) errors.
|
333
|
+
begin
|
334
|
+
first_chunk.commit
|
335
|
+
if enqueue || first_chunk.unstaged? || chunk_size_full?(first_chunk)
|
336
|
+
chunks_to_enqueue << first_chunk
|
337
|
+
end
|
338
|
+
first_chunk.mon_exit
|
339
|
+
rescue
|
340
|
+
operated_chunks.unshift(first_chunk)
|
341
|
+
raise
|
342
|
+
end
|
343
|
+
|
344
|
+
errors = []
|
345
|
+
# Buffer plugin estimates there's no serious error cause: will commit for all chunks eigher way
|
346
|
+
operated_chunks.each do |chunk|
|
347
|
+
begin
|
348
|
+
chunk.commit
|
349
|
+
if enqueue || chunk.unstaged? || chunk_size_full?(chunk)
|
350
|
+
chunks_to_enqueue << chunk
|
351
|
+
end
|
352
|
+
chunk.mon_exit
|
353
|
+
rescue => e
|
354
|
+
chunk.rollback
|
355
|
+
chunk.mon_exit
|
356
|
+
errors << e
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
# All locks about chunks are released.
|
361
|
+
|
362
|
+
synchronize do
|
363
|
+
# At here, staged chunks may be enqueued by other threads.
|
364
|
+
@stage_size += staged_bytesize
|
365
|
+
|
366
|
+
chunks_to_enqueue.each do |c|
|
367
|
+
if c.staged? && (enqueue || chunk_size_full?(c))
|
368
|
+
m = c.metadata
|
369
|
+
enqueue_chunk(m)
|
370
|
+
if unstaged_chunks[m]
|
371
|
+
u = unstaged_chunks[m].pop
|
372
|
+
if u.unstaged? && !chunk_size_full?(u)
|
373
|
+
@stage[m] = u.staged!
|
374
|
+
@stage_size += u.bytesize
|
375
|
+
end
|
376
|
+
end
|
377
|
+
elsif c.unstaged?
|
378
|
+
enqueue_unstaged_chunk(c)
|
379
|
+
else
|
380
|
+
# previously staged chunk is already enqueued, closed or purged.
|
381
|
+
# no problem.
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
operated_chunks.clear if errors.empty?
|
387
|
+
|
388
|
+
if errors.size > 0
|
389
|
+
log.warn "error occurs in committing chunks: only first one raised", errors: errors.map(&:class)
|
390
|
+
raise errors.first
|
391
|
+
end
|
392
|
+
ensure
|
393
|
+
operated_chunks.each do |chunk|
|
394
|
+
chunk.rollback rescue nil # nothing possible to do for #rollback failure
|
395
|
+
if chunk.unstaged?
|
396
|
+
chunk.purge rescue nil # to prevent leakage of unstaged chunks
|
397
|
+
end
|
398
|
+
chunk.mon_exit rescue nil # this may raise ThreadError for chunks already committed
|
399
|
+
end
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
def queue_full?
|
404
|
+
synchronize { @queue.size } >= @queued_chunks_limit_size
|
405
|
+
end
|
406
|
+
|
407
|
+
def queued_records
|
408
|
+
synchronize { @queue.reduce(0){|r, chunk| r + chunk.size } }
|
409
|
+
end
|
410
|
+
|
411
|
+
def queued?(metadata=nil)
|
412
|
+
synchronize do
|
413
|
+
if metadata
|
414
|
+
n = @queued_num[metadata]
|
415
|
+
n && n.nonzero?
|
416
|
+
else
|
417
|
+
!@queue.empty?
|
418
|
+
end
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
def enqueue_chunk(metadata)
|
423
|
+
log.on_trace { log.trace "enqueueing chunk", instance: self.object_id, metadata: metadata }
|
424
|
+
|
425
|
+
chunk = synchronize do
|
426
|
+
@stage.delete(metadata)
|
427
|
+
end
|
428
|
+
return nil unless chunk
|
429
|
+
|
430
|
+
chunk.synchronize do
|
431
|
+
synchronize do
|
432
|
+
if chunk.empty?
|
433
|
+
chunk.close
|
434
|
+
else
|
435
|
+
@queue << chunk
|
436
|
+
@queued_num[metadata] = @queued_num.fetch(metadata, 0) + 1
|
437
|
+
chunk.enqueued!
|
438
|
+
end
|
439
|
+
bytesize = chunk.bytesize
|
440
|
+
@stage_size -= bytesize
|
441
|
+
@queue_size += bytesize
|
442
|
+
end
|
443
|
+
end
|
444
|
+
nil
|
445
|
+
end
|
446
|
+
|
447
|
+
def enqueue_unstaged_chunk(chunk)
|
448
|
+
log.on_trace { log.trace "enqueueing unstaged chunk", instance: self.object_id, metadata: chunk.metadata }
|
449
|
+
|
450
|
+
synchronize do
|
451
|
+
chunk.synchronize do
|
452
|
+
metadata = chunk.metadata
|
453
|
+
@queue << chunk
|
454
|
+
@queued_num[metadata] = @queued_num.fetch(metadata, 0) + 1
|
455
|
+
chunk.enqueued!
|
456
|
+
end
|
457
|
+
@queue_size += chunk.bytesize
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
# At flush_at_shutdown, all staged chunks should be enqueued for buffer flush. Set true to force_enqueue for it.
|
462
|
+
def enqueue_all(force_enqueue = false)
|
463
|
+
log.on_trace { log.trace "enqueueing all chunks in buffer", instance: self.object_id }
|
464
|
+
|
465
|
+
if block_given?
|
466
|
+
synchronize{ @stage.keys }.each do |metadata|
|
467
|
+
return if !force_enqueue && queue_full?
|
468
|
+
# NOTE: The following line might cause data race depending on Ruby implementations except CRuby
|
469
|
+
# cf. https://github.com/fluent/fluentd/pull/1721#discussion_r146170251
|
470
|
+
chunk = @stage[metadata]
|
471
|
+
next unless chunk
|
472
|
+
v = yield metadata, chunk
|
473
|
+
enqueue_chunk(metadata) if v
|
474
|
+
end
|
475
|
+
else
|
476
|
+
synchronize{ @stage.keys }.each do |metadata|
|
477
|
+
return if !force_enqueue && queue_full?
|
478
|
+
enqueue_chunk(metadata)
|
479
|
+
end
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
def dequeue_chunk
|
484
|
+
return nil if @queue.empty?
|
485
|
+
log.on_trace { log.trace "dequeueing a chunk", instance: self.object_id }
|
486
|
+
|
487
|
+
synchronize do
|
488
|
+
chunk = @queue.shift
|
489
|
+
|
490
|
+
# this buffer is dequeued by other thread just before "synchronize" in this thread
|
491
|
+
return nil unless chunk
|
492
|
+
|
493
|
+
@dequeued[chunk.unique_id] = chunk
|
494
|
+
@queued_num[chunk.metadata] -= 1 # BUG if nil, 0 or subzero
|
495
|
+
@dequeued_num[chunk.metadata] ||= 0
|
496
|
+
@dequeued_num[chunk.metadata] += 1
|
497
|
+
log.trace "chunk dequeued", instance: self.object_id, metadata: chunk.metadata
|
498
|
+
chunk
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
def takeback_chunk(chunk_id)
|
503
|
+
log.on_trace { log.trace "taking back a chunk", instance: self.object_id, chunk_id: dump_unique_id_hex(chunk_id) }
|
504
|
+
|
505
|
+
synchronize do
|
506
|
+
chunk = @dequeued.delete(chunk_id)
|
507
|
+
return false unless chunk # already purged by other thread
|
508
|
+
@queue.unshift(chunk)
|
509
|
+
log.trace "chunk taken back", instance: self.object_id, chunk_id: dump_unique_id_hex(chunk_id), metadata: chunk.metadata
|
510
|
+
@queued_num[chunk.metadata] += 1 # BUG if nil
|
511
|
+
@dequeued_num[chunk.metadata] -= 1
|
512
|
+
end
|
513
|
+
true
|
514
|
+
end
|
515
|
+
|
516
|
+
def purge_chunk(chunk_id)
|
517
|
+
synchronize do
|
518
|
+
chunk = @dequeued.delete(chunk_id)
|
519
|
+
return nil unless chunk # purged by other threads
|
520
|
+
|
521
|
+
metadata = chunk.metadata
|
522
|
+
log.on_trace { log.trace "purging a chunk", instance: self.object_id, chunk_id: dump_unique_id_hex(chunk_id), metadata: metadata }
|
523
|
+
|
524
|
+
begin
|
525
|
+
bytesize = chunk.bytesize
|
526
|
+
chunk.purge
|
527
|
+
@queue_size -= bytesize
|
528
|
+
rescue => e
|
529
|
+
log.error "failed to purge buffer chunk", chunk_id: dump_unique_id_hex(chunk_id), error_class: e.class, error: e
|
530
|
+
log.error_backtrace
|
531
|
+
end
|
532
|
+
|
533
|
+
@dequeued_num[chunk.metadata] -= 1
|
534
|
+
if metadata && !@stage[metadata] && (!@queued_num[metadata] || @queued_num[metadata] < 1) && @dequeued_num[metadata].zero?
|
535
|
+
@metadata_list.delete(metadata)
|
536
|
+
@queued_num.delete(metadata)
|
537
|
+
@dequeued_num.delete(metadata)
|
538
|
+
del_timekey(metadata)
|
539
|
+
end
|
540
|
+
log.trace "chunk purged", instance: self.object_id, chunk_id: dump_unique_id_hex(chunk_id), metadata: metadata
|
541
|
+
end
|
542
|
+
nil
|
543
|
+
end
|
544
|
+
|
545
|
+
def clear_queue!
|
546
|
+
log.on_trace { log.trace "clearing queue", instance: self.object_id }
|
547
|
+
|
548
|
+
synchronize do
|
549
|
+
until @queue.empty?
|
550
|
+
begin
|
551
|
+
q = @queue.shift
|
552
|
+
log.trace("purging a chunk in queue"){ {id: dump_unique_id_hex(chunk.unique_id), bytesize: chunk.bytesize, size: chunk.size} }
|
553
|
+
q.purge
|
554
|
+
rescue => e
|
555
|
+
log.error "unexpected error while clearing buffer queue", error_class: e.class, error: e
|
556
|
+
log.error_backtrace
|
557
|
+
end
|
558
|
+
end
|
559
|
+
@queue_size = 0
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
def chunk_size_over?(chunk)
|
564
|
+
chunk.bytesize > @chunk_limit_size || (@chunk_limit_records && chunk.size > @chunk_limit_records)
|
565
|
+
end
|
566
|
+
|
567
|
+
def chunk_size_full?(chunk)
|
568
|
+
chunk.bytesize >= @chunk_limit_size * @chunk_full_threshold || (@chunk_limit_records && chunk.size >= @chunk_limit_records * @chunk_full_threshold)
|
569
|
+
end
|
570
|
+
|
571
|
+
class ShouldRetry < StandardError; end
|
572
|
+
|
573
|
+
# write once into a chunk
|
574
|
+
# 1. append whole data into existing chunk
|
575
|
+
# 2. commit it & return unless chunk_size_over?
|
576
|
+
# 3. enqueue existing chunk & retry whole method if chunk was not empty
|
577
|
+
# 4. go to step_by_step writing
|
578
|
+
|
579
|
+
def write_once(metadata, data, format: nil, size: nil, &block)
|
580
|
+
return if data.empty?
|
581
|
+
|
582
|
+
stored = false
|
583
|
+
adding_bytesize = nil
|
584
|
+
|
585
|
+
chunk = synchronize { @stage[metadata] ||= generate_chunk(metadata).staged! }
|
586
|
+
enqueue_chunk_before_retry = false
|
587
|
+
chunk.synchronize do
|
588
|
+
# retry this method if chunk is already queued (between getting chunk and entering critical section)
|
589
|
+
raise ShouldRetry unless chunk.staged?
|
590
|
+
|
591
|
+
empty_chunk = chunk.empty?
|
592
|
+
|
593
|
+
original_bytesize = chunk.bytesize
|
594
|
+
begin
|
595
|
+
if format
|
596
|
+
serialized = format.call(data)
|
597
|
+
chunk.concat(serialized, size ? size.call : data.size)
|
598
|
+
else
|
599
|
+
chunk.append(data, compress: @compress)
|
600
|
+
end
|
601
|
+
adding_bytesize = chunk.bytesize - original_bytesize
|
602
|
+
|
603
|
+
if chunk_size_over?(chunk)
|
604
|
+
if format && empty_chunk
|
605
|
+
log.warn "chunk bytes limit exceeds for an emitted event stream: #{adding_bytesize}bytes"
|
606
|
+
end
|
607
|
+
chunk.rollback
|
608
|
+
|
609
|
+
if format && !empty_chunk
|
610
|
+
# Event streams should be appended into a chunk at once
|
611
|
+
# as far as possible, to improve performance of formatting.
|
612
|
+
# Event stream may be a MessagePackEventStream. We don't want to split it into
|
613
|
+
# 2 or more chunks (except for a case that the event stream is larger than chunk limit).
|
614
|
+
enqueue_chunk_before_retry = true
|
615
|
+
raise ShouldRetry
|
616
|
+
end
|
617
|
+
else
|
618
|
+
stored = true
|
619
|
+
end
|
620
|
+
rescue
|
621
|
+
chunk.rollback
|
622
|
+
raise
|
623
|
+
end
|
624
|
+
|
625
|
+
if stored
|
626
|
+
block.call(chunk, adding_bytesize)
|
627
|
+
end
|
628
|
+
end
|
629
|
+
|
630
|
+
unless stored
|
631
|
+
# try step-by-step appending if data can't be stored into existing a chunk in non-bulk mode
|
632
|
+
#
|
633
|
+
# 1/10 size of original event stream (splits_count == 10) seems enough small
|
634
|
+
# to try emitting events into existing chunk.
|
635
|
+
# it does not matter to split event stream into very small splits, because chunks have less
|
636
|
+
# overhead to write data many times (even about file buffer chunks).
|
637
|
+
write_step_by_step(metadata, data, format, 10, &block)
|
638
|
+
end
|
639
|
+
rescue ShouldRetry
|
640
|
+
enqueue_chunk(metadata) if enqueue_chunk_before_retry
|
641
|
+
retry
|
642
|
+
end
|
643
|
+
|
644
|
+
# EventStream can be split into many streams
|
645
|
+
# because (es1 + es2).to_msgpack_stream == es1.to_msgpack_stream + es2.to_msgpack_stream
|
646
|
+
|
647
|
+
# 1. split event streams into many (10 -> 100 -> 1000 -> ...) chunks
|
648
|
+
# 2. append splits into the staged chunks as much as possible
|
649
|
+
# 3. create unstaged chunk and append rest splits -> repeat it for all splits
|
650
|
+
|
651
|
+
def write_step_by_step(metadata, data, format, splits_count, &block)
|
652
|
+
splits = []
|
653
|
+
if splits_count > data.size
|
654
|
+
splits_count = data.size
|
655
|
+
end
|
656
|
+
slice_size = if data.size % splits_count == 0
|
657
|
+
data.size / splits_count
|
658
|
+
else
|
659
|
+
data.size / (splits_count - 1)
|
660
|
+
end
|
661
|
+
slice_origin = 0
|
662
|
+
while slice_origin < data.size
|
663
|
+
splits << data.slice(slice_origin, slice_size)
|
664
|
+
slice_origin += slice_size
|
665
|
+
end
|
666
|
+
|
667
|
+
# This method will append events into the staged chunk at first.
|
668
|
+
# Then, will generate chunks not staged (not queued) to append rest data.
|
669
|
+
staged_chunk_used = false
|
670
|
+
modified_chunks = []
|
671
|
+
get_next_chunk = ->(){
|
672
|
+
c = if staged_chunk_used
|
673
|
+
# Staging new chunk here is bad idea:
|
674
|
+
# Recovering whole state including newly staged chunks is much harder than current implementation.
|
675
|
+
generate_chunk(metadata)
|
676
|
+
else
|
677
|
+
synchronize{ @stage[metadata] ||= generate_chunk(metadata).staged! }
|
678
|
+
end
|
679
|
+
modified_chunks << c
|
680
|
+
c
|
681
|
+
}
|
682
|
+
|
683
|
+
writing_splits_index = 0
|
684
|
+
enqueue_chunk_before_retry = false
|
685
|
+
|
686
|
+
while writing_splits_index < splits.size
|
687
|
+
chunk = get_next_chunk.call
|
688
|
+
chunk.synchronize do
|
689
|
+
raise ShouldRetry unless chunk.writable?
|
690
|
+
staged_chunk_used = true if chunk.staged?
|
691
|
+
|
692
|
+
original_bytesize = chunk.bytesize
|
693
|
+
begin
|
694
|
+
while writing_splits_index < splits.size
|
695
|
+
split = splits[writing_splits_index]
|
696
|
+
if format
|
697
|
+
chunk.concat(format.call(split), split.size)
|
698
|
+
else
|
699
|
+
chunk.append(split, compress: @compress)
|
700
|
+
end
|
701
|
+
|
702
|
+
if chunk_size_over?(chunk) # split size is larger than difference between size_full? and size_over?
|
703
|
+
chunk.rollback
|
704
|
+
|
705
|
+
if split.size == 1 && original_bytesize == 0
|
706
|
+
big_record_size = format ? format.call(split).bytesize : split.first.bytesize
|
707
|
+
raise BufferChunkOverflowError, "a #{big_record_size}bytes record is larger than buffer chunk limit size"
|
708
|
+
end
|
709
|
+
|
710
|
+
if chunk_size_full?(chunk) || split.size == 1
|
711
|
+
enqueue_chunk_before_retry = true
|
712
|
+
else
|
713
|
+
splits_count *= 10
|
714
|
+
end
|
715
|
+
|
716
|
+
raise ShouldRetry
|
717
|
+
end
|
718
|
+
|
719
|
+
writing_splits_index += 1
|
720
|
+
|
721
|
+
if chunk_size_full?(chunk)
|
722
|
+
break
|
723
|
+
end
|
724
|
+
end
|
725
|
+
rescue
|
726
|
+
chunk.purge if chunk.unstaged? # unstaged chunk will leak unless purge it
|
727
|
+
raise
|
728
|
+
end
|
729
|
+
|
730
|
+
block.call(chunk, chunk.bytesize - original_bytesize)
|
731
|
+
end
|
732
|
+
end
|
733
|
+
rescue ShouldRetry
|
734
|
+
modified_chunks.each do |mc|
|
735
|
+
mc.rollback rescue nil
|
736
|
+
if mc.unstaged?
|
737
|
+
mc.purge rescue nil
|
738
|
+
end
|
739
|
+
end
|
740
|
+
enqueue_chunk(metadata) if enqueue_chunk_before_retry
|
741
|
+
retry
|
742
|
+
end
|
743
|
+
|
744
|
+
STATS_KEYS = [
|
745
|
+
'stage_length',
|
746
|
+
'stage_byte_size',
|
747
|
+
'queue_length',
|
748
|
+
'queue_byte_size',
|
749
|
+
'available_buffer_space_ratios',
|
750
|
+
'total_queued_size',
|
751
|
+
'oldest_timekey',
|
752
|
+
'newest_timekey'
|
753
|
+
]
|
754
|
+
|
755
|
+
def statistics
|
756
|
+
stage_size, queue_size = @stage_size, @queue_size
|
757
|
+
buffer_space = 1.0 - ((stage_size + queue_size * 1.0) / @total_limit_size).round
|
758
|
+
stats = {
|
759
|
+
'stage_length' => @stage.size,
|
760
|
+
'stage_byte_size' => stage_size,
|
761
|
+
'queue_length' => @queue.size,
|
762
|
+
'queue_byte_size' => queue_size,
|
763
|
+
'available_buffer_space_ratios' => buffer_space * 100,
|
764
|
+
'total_queued_size' => stage_size + queue_size,
|
765
|
+
}
|
766
|
+
|
767
|
+
if (m = timekeys.min)
|
768
|
+
stats['oldest_timekey'] = m
|
769
|
+
end
|
770
|
+
|
771
|
+
if (m = timekeys.max)
|
772
|
+
stats['newest_timekey'] = m
|
773
|
+
end
|
774
|
+
|
775
|
+
{ 'buffer' => stats }
|
776
|
+
end
|
777
|
+
end
|
778
|
+
end
|
779
|
+
end
|