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.

Files changed (428) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
  4. data/.github/ISSUE_TEMPLATE.md +17 -0
  5. data/.github/PULL_REQUEST_TEMPLATE.md +13 -0
  6. data/.gitignore +5 -0
  7. data/.gitlab/cicd-template.yaml +10 -0
  8. data/.gitlab-ci.yml +147 -0
  9. data/.travis.yml +56 -20
  10. data/ADOPTERS.md +5 -0
  11. data/CHANGELOG.md +1369 -0
  12. data/CONTRIBUTING.md +16 -5
  13. data/GOVERNANCE.md +55 -0
  14. data/Gemfile +5 -0
  15. data/GithubWorkflow.md +78 -0
  16. data/LICENSE +202 -0
  17. data/MAINTAINERS.md +7 -0
  18. data/README.md +23 -11
  19. data/Rakefile +48 -2
  20. data/Vagrantfile +17 -0
  21. data/appveyor.yml +37 -0
  22. data/bin/fluent-binlog-reader +7 -0
  23. data/bin/fluent-ca-generate +6 -0
  24. data/bin/fluent-plugin-config-format +5 -0
  25. data/bin/fluent-plugin-generate +5 -0
  26. data/bin/fluentd +3 -0
  27. data/code-of-conduct.md +3 -0
  28. data/example/copy_roundrobin.conf +39 -0
  29. data/example/counter.conf +18 -0
  30. data/example/in_dummy_blocks.conf +17 -0
  31. data/example/in_dummy_with_compression.conf +23 -0
  32. data/example/in_forward.conf +7 -0
  33. data/example/in_forward_client.conf +37 -0
  34. data/example/in_forward_shared_key.conf +15 -0
  35. data/example/in_forward_tls.conf +14 -0
  36. data/example/in_forward_users.conf +24 -0
  37. data/example/in_forward_workers.conf +21 -0
  38. data/example/in_http.conf +3 -1
  39. data/example/in_out_forward.conf +17 -0
  40. data/example/logevents.conf +25 -0
  41. data/example/multi_filters.conf +61 -0
  42. data/example/out_exec_filter.conf +42 -0
  43. data/example/out_forward.conf +13 -13
  44. data/example/out_forward_buf_file.conf +23 -0
  45. data/example/out_forward_client.conf +109 -0
  46. data/example/out_forward_heartbeat_none.conf +16 -0
  47. data/example/out_forward_shared_key.conf +36 -0
  48. data/example/out_forward_tls.conf +18 -0
  49. data/example/out_forward_users.conf +65 -0
  50. data/example/out_null.conf +36 -0
  51. data/example/secondary_file.conf +42 -0
  52. data/example/suppress_config_dump.conf +7 -0
  53. data/example/worker_section.conf +36 -0
  54. data/fluent.conf +29 -0
  55. data/fluentd.gemspec +21 -11
  56. data/lib/fluent/agent.rb +67 -90
  57. data/lib/fluent/clock.rb +62 -0
  58. data/lib/fluent/command/binlog_reader.rb +244 -0
  59. data/lib/fluent/command/ca_generate.rb +181 -0
  60. data/lib/fluent/command/cat.rb +42 -18
  61. data/lib/fluent/command/debug.rb +12 -10
  62. data/lib/fluent/command/fluentd.rb +153 -5
  63. data/lib/fluent/command/plugin_config_formatter.rb +292 -0
  64. data/lib/fluent/command/plugin_generator.rb +324 -0
  65. data/lib/fluent/compat/call_super_mixin.rb +67 -0
  66. data/lib/fluent/compat/detach_process_mixin.rb +33 -0
  67. data/lib/fluent/compat/exec_util.rb +129 -0
  68. data/lib/fluent/compat/file_util.rb +54 -0
  69. data/lib/fluent/compat/filter.rb +68 -0
  70. data/lib/fluent/compat/formatter.rb +111 -0
  71. data/lib/fluent/compat/formatter_utils.rb +85 -0
  72. data/lib/fluent/compat/handle_tag_and_time_mixin.rb +62 -0
  73. data/lib/fluent/compat/handle_tag_name_mixin.rb +53 -0
  74. data/lib/fluent/compat/input.rb +49 -0
  75. data/lib/fluent/compat/output.rb +718 -0
  76. data/lib/fluent/compat/output_chain.rb +60 -0
  77. data/lib/fluent/compat/parser.rb +310 -0
  78. data/lib/fluent/compat/parser_utils.rb +40 -0
  79. data/lib/fluent/compat/propagate_default.rb +62 -0
  80. data/lib/fluent/compat/record_filter_mixin.rb +34 -0
  81. data/lib/fluent/compat/set_tag_key_mixin.rb +50 -0
  82. data/lib/fluent/compat/set_time_key_mixin.rb +69 -0
  83. data/lib/fluent/compat/socket_util.rb +165 -0
  84. data/lib/fluent/compat/string_util.rb +34 -0
  85. data/lib/fluent/compat/structured_format_mixin.rb +26 -0
  86. data/lib/fluent/compat/type_converter.rb +90 -0
  87. data/lib/fluent/config/configure_proxy.rb +210 -62
  88. data/lib/fluent/config/dsl.rb +12 -5
  89. data/lib/fluent/config/element.rb +107 -9
  90. data/lib/fluent/config/literal_parser.rb +9 -3
  91. data/lib/fluent/config/parser.rb +4 -4
  92. data/lib/fluent/config/section.rb +51 -14
  93. data/lib/fluent/config/types.rb +28 -13
  94. data/lib/fluent/config/v1_parser.rb +3 -5
  95. data/lib/fluent/config.rb +23 -20
  96. data/lib/fluent/configurable.rb +79 -21
  97. data/lib/fluent/counter/base_socket.rb +46 -0
  98. data/lib/fluent/counter/client.rb +297 -0
  99. data/lib/fluent/counter/error.rb +86 -0
  100. data/lib/fluent/counter/mutex_hash.rb +163 -0
  101. data/lib/fluent/counter/server.rb +273 -0
  102. data/lib/fluent/counter/store.rb +205 -0
  103. data/lib/fluent/counter/validator.rb +145 -0
  104. data/lib/fluent/counter.rb +23 -0
  105. data/lib/fluent/daemon.rb +15 -0
  106. data/lib/fluent/engine.rb +102 -65
  107. data/lib/fluent/env.rb +7 -3
  108. data/lib/fluent/error.rb +30 -0
  109. data/lib/fluent/event.rb +197 -21
  110. data/lib/fluent/event_router.rb +93 -10
  111. data/lib/fluent/filter.rb +2 -50
  112. data/lib/fluent/formatter.rb +4 -293
  113. data/lib/fluent/input.rb +2 -32
  114. data/lib/fluent/label.rb +10 -2
  115. data/lib/fluent/load.rb +3 -3
  116. data/lib/fluent/log.rb +348 -81
  117. data/lib/fluent/match.rb +37 -36
  118. data/lib/fluent/mixin.rb +12 -176
  119. data/lib/fluent/msgpack_factory.rb +62 -0
  120. data/lib/fluent/output.rb +10 -612
  121. data/lib/fluent/output_chain.rb +23 -0
  122. data/lib/fluent/parser.rb +4 -800
  123. data/lib/fluent/plugin/bare_output.rb +63 -0
  124. data/lib/fluent/plugin/base.rb +192 -0
  125. data/lib/fluent/plugin/buf_file.rb +128 -174
  126. data/lib/fluent/plugin/buf_memory.rb +9 -92
  127. data/lib/fluent/plugin/buffer/chunk.rb +221 -0
  128. data/lib/fluent/plugin/buffer/file_chunk.rb +383 -0
  129. data/lib/fluent/plugin/buffer/memory_chunk.rb +90 -0
  130. data/lib/fluent/plugin/buffer.rb +779 -0
  131. data/lib/fluent/plugin/compressable.rb +92 -0
  132. data/lib/fluent/plugin/exec_util.rb +3 -108
  133. data/lib/fluent/plugin/file_util.rb +4 -34
  134. data/lib/fluent/plugin/file_wrapper.rb +120 -0
  135. data/lib/fluent/plugin/filter.rb +93 -0
  136. data/lib/fluent/plugin/filter_grep.rb +117 -34
  137. data/lib/fluent/plugin/filter_parser.rb +85 -62
  138. data/lib/fluent/plugin/filter_record_transformer.rb +27 -39
  139. data/lib/fluent/plugin/filter_stdout.rb +15 -12
  140. data/lib/fluent/plugin/formatter.rb +50 -0
  141. data/lib/fluent/plugin/formatter_csv.rb +52 -0
  142. data/lib/fluent/plugin/formatter_hash.rb +33 -0
  143. data/lib/fluent/plugin/formatter_json.rb +55 -0
  144. data/lib/fluent/plugin/formatter_ltsv.rb +42 -0
  145. data/lib/fluent/plugin/formatter_msgpack.rb +33 -0
  146. data/lib/fluent/plugin/formatter_out_file.rb +51 -0
  147. data/lib/fluent/plugin/formatter_single_value.rb +34 -0
  148. data/lib/fluent/plugin/formatter_stdout.rb +76 -0
  149. data/lib/fluent/plugin/formatter_tsv.rb +38 -0
  150. data/lib/fluent/plugin/in_debug_agent.rb +17 -6
  151. data/lib/fluent/plugin/in_dummy.rb +47 -20
  152. data/lib/fluent/plugin/in_exec.rb +55 -123
  153. data/lib/fluent/plugin/in_forward.rb +299 -216
  154. data/lib/fluent/plugin/in_gc_stat.rb +14 -36
  155. data/lib/fluent/plugin/in_http.rb +204 -91
  156. data/lib/fluent/plugin/in_monitor_agent.rb +186 -258
  157. data/lib/fluent/plugin/in_object_space.rb +13 -41
  158. data/lib/fluent/plugin/in_syslog.rb +112 -134
  159. data/lib/fluent/plugin/in_tail.rb +408 -745
  160. data/lib/fluent/plugin/in_tcp.rb +66 -9
  161. data/lib/fluent/plugin/in_udp.rb +60 -11
  162. data/lib/fluent/plugin/{in_stream.rb → in_unix.rb} +8 -4
  163. data/lib/fluent/plugin/input.rb +37 -0
  164. data/lib/fluent/plugin/multi_output.rb +158 -0
  165. data/lib/fluent/plugin/out_copy.rb +23 -35
  166. data/lib/fluent/plugin/out_exec.rb +67 -70
  167. data/lib/fluent/plugin/out_exec_filter.rb +204 -271
  168. data/lib/fluent/plugin/out_file.rb +267 -73
  169. data/lib/fluent/plugin/out_forward.rb +854 -325
  170. data/lib/fluent/plugin/out_null.rb +42 -9
  171. data/lib/fluent/plugin/out_relabel.rb +9 -5
  172. data/lib/fluent/plugin/out_roundrobin.rb +18 -37
  173. data/lib/fluent/plugin/out_secondary_file.rb +133 -0
  174. data/lib/fluent/plugin/out_stdout.rb +43 -10
  175. data/lib/fluent/plugin/out_stream.rb +7 -2
  176. data/lib/fluent/plugin/output.rb +1498 -0
  177. data/lib/fluent/plugin/owned_by_mixin.rb +42 -0
  178. data/lib/fluent/plugin/parser.rb +191 -0
  179. data/lib/fluent/plugin/parser_apache.rb +28 -0
  180. data/lib/fluent/plugin/parser_apache2.rb +88 -0
  181. data/lib/fluent/plugin/parser_apache_error.rb +26 -0
  182. data/lib/fluent/plugin/parser_csv.rb +39 -0
  183. data/lib/fluent/plugin/parser_json.rb +94 -0
  184. data/lib/fluent/plugin/parser_ltsv.rb +49 -0
  185. data/lib/fluent/plugin/parser_msgpack.rb +50 -0
  186. data/lib/fluent/plugin/parser_multiline.rb +106 -0
  187. data/lib/fluent/plugin/parser_nginx.rb +28 -0
  188. data/lib/fluent/plugin/parser_none.rb +36 -0
  189. data/lib/fluent/plugin/parser_regexp.rb +68 -0
  190. data/lib/fluent/plugin/parser_syslog.rb +142 -0
  191. data/lib/fluent/plugin/parser_tsv.rb +42 -0
  192. data/lib/fluent/plugin/socket_util.rb +3 -143
  193. data/lib/fluent/plugin/storage.rb +84 -0
  194. data/lib/fluent/plugin/storage_local.rb +164 -0
  195. data/lib/fluent/plugin/string_util.rb +3 -15
  196. data/lib/fluent/plugin.rb +122 -121
  197. data/lib/fluent/plugin_helper/cert_option.rb +178 -0
  198. data/lib/fluent/plugin_helper/child_process.rb +364 -0
  199. data/lib/fluent/plugin_helper/compat_parameters.rb +333 -0
  200. data/lib/fluent/plugin_helper/counter.rb +51 -0
  201. data/lib/fluent/plugin_helper/event_emitter.rb +93 -0
  202. data/lib/fluent/plugin_helper/event_loop.rb +170 -0
  203. data/lib/fluent/plugin_helper/extract.rb +104 -0
  204. data/lib/fluent/plugin_helper/formatter.rb +147 -0
  205. data/lib/fluent/plugin_helper/http_server/app.rb +79 -0
  206. data/lib/fluent/plugin_helper/http_server/compat/server.rb +81 -0
  207. data/lib/fluent/plugin_helper/http_server/compat/webrick_handler.rb +58 -0
  208. data/lib/fluent/plugin_helper/http_server/methods.rb +35 -0
  209. data/lib/fluent/plugin_helper/http_server/request.rb +42 -0
  210. data/lib/fluent/plugin_helper/http_server/router.rb +54 -0
  211. data/lib/fluent/plugin_helper/http_server/server.rb +87 -0
  212. data/lib/fluent/plugin_helper/http_server.rb +76 -0
  213. data/lib/fluent/plugin_helper/inject.rb +151 -0
  214. data/lib/fluent/plugin_helper/parser.rb +147 -0
  215. data/lib/fluent/plugin_helper/record_accessor.rb +210 -0
  216. data/lib/fluent/plugin_helper/retry_state.rb +205 -0
  217. data/lib/fluent/plugin_helper/server.rb +807 -0
  218. data/lib/fluent/plugin_helper/socket.rb +250 -0
  219. data/lib/fluent/plugin_helper/socket_option.rb +80 -0
  220. data/lib/fluent/plugin_helper/storage.rb +349 -0
  221. data/lib/fluent/plugin_helper/thread.rb +179 -0
  222. data/lib/fluent/plugin_helper/timer.rb +92 -0
  223. data/lib/fluent/plugin_helper.rb +73 -0
  224. data/lib/fluent/plugin_id.rb +80 -0
  225. data/lib/fluent/process.rb +3 -489
  226. data/lib/fluent/registry.rb +52 -10
  227. data/lib/fluent/root_agent.rb +204 -42
  228. data/lib/fluent/supervisor.rb +597 -359
  229. data/lib/fluent/system_config.rb +131 -42
  230. data/lib/fluent/test/base.rb +6 -54
  231. data/lib/fluent/test/driver/base.rb +224 -0
  232. data/lib/fluent/test/driver/base_owned.rb +70 -0
  233. data/lib/fluent/test/driver/base_owner.rb +135 -0
  234. data/lib/fluent/test/driver/event_feeder.rb +98 -0
  235. data/lib/fluent/test/driver/filter.rb +57 -0
  236. data/lib/fluent/test/driver/formatter.rb +30 -0
  237. data/lib/fluent/test/driver/input.rb +31 -0
  238. data/lib/fluent/test/driver/multi_output.rb +53 -0
  239. data/lib/fluent/test/driver/output.rb +102 -0
  240. data/lib/fluent/test/driver/parser.rb +30 -0
  241. data/lib/fluent/test/driver/test_event_router.rb +45 -0
  242. data/lib/fluent/test/filter_test.rb +0 -1
  243. data/lib/fluent/test/formatter_test.rb +4 -1
  244. data/lib/fluent/test/helpers.rb +58 -10
  245. data/lib/fluent/test/input_test.rb +27 -19
  246. data/lib/fluent/test/log.rb +79 -0
  247. data/lib/fluent/test/output_test.rb +28 -39
  248. data/lib/fluent/test/parser_test.rb +3 -1
  249. data/lib/fluent/test/startup_shutdown.rb +46 -0
  250. data/lib/fluent/test.rb +33 -1
  251. data/lib/fluent/time.rb +450 -1
  252. data/lib/fluent/timezone.rb +27 -3
  253. data/lib/fluent/{status.rb → unique_id.rb} +15 -24
  254. data/lib/fluent/version.rb +1 -1
  255. data/lib/fluent/winsvc.rb +85 -0
  256. data/templates/new_gem/Gemfile +3 -0
  257. data/templates/new_gem/README.md.erb +43 -0
  258. data/templates/new_gem/Rakefile +13 -0
  259. data/templates/new_gem/fluent-plugin.gemspec.erb +27 -0
  260. data/templates/new_gem/lib/fluent/plugin/filter.rb.erb +14 -0
  261. data/templates/new_gem/lib/fluent/plugin/formatter.rb.erb +14 -0
  262. data/templates/new_gem/lib/fluent/plugin/input.rb.erb +11 -0
  263. data/templates/new_gem/lib/fluent/plugin/output.rb.erb +11 -0
  264. data/templates/new_gem/lib/fluent/plugin/parser.rb.erb +15 -0
  265. data/templates/new_gem/test/helper.rb.erb +8 -0
  266. data/templates/new_gem/test/plugin/test_filter.rb.erb +18 -0
  267. data/templates/new_gem/test/plugin/test_formatter.rb.erb +18 -0
  268. data/templates/new_gem/test/plugin/test_input.rb.erb +18 -0
  269. data/templates/new_gem/test/plugin/test_output.rb.erb +18 -0
  270. data/templates/new_gem/test/plugin/test_parser.rb.erb +18 -0
  271. data/templates/plugin_config_formatter/param.md-compact.erb +25 -0
  272. data/templates/plugin_config_formatter/param.md.erb +34 -0
  273. data/templates/plugin_config_formatter/section.md.erb +12 -0
  274. data/test/command/test_binlog_reader.rb +346 -0
  275. data/test/command/test_ca_generate.rb +70 -0
  276. data/test/command/test_fluentd.rb +901 -0
  277. data/test/command/test_plugin_config_formatter.rb +276 -0
  278. data/test/command/test_plugin_generator.rb +92 -0
  279. data/test/compat/test_calls_super.rb +166 -0
  280. data/test/compat/test_parser.rb +92 -0
  281. data/test/config/test_config_parser.rb +126 -2
  282. data/test/config/test_configurable.rb +946 -187
  283. data/test/config/test_configure_proxy.rb +424 -74
  284. data/test/config/test_dsl.rb +11 -11
  285. data/test/config/test_element.rb +500 -0
  286. data/test/config/test_literal_parser.rb +8 -0
  287. data/test/config/test_plugin_configuration.rb +56 -0
  288. data/test/config/test_section.rb +79 -7
  289. data/test/config/test_system_config.rb +122 -35
  290. data/test/config/test_types.rb +38 -0
  291. data/test/counter/test_client.rb +559 -0
  292. data/test/counter/test_error.rb +44 -0
  293. data/test/counter/test_mutex_hash.rb +179 -0
  294. data/test/counter/test_server.rb +589 -0
  295. data/test/counter/test_store.rb +258 -0
  296. data/test/counter/test_validator.rb +137 -0
  297. data/test/helper.rb +89 -6
  298. data/test/helpers/fuzzy_assert.rb +89 -0
  299. data/test/plugin/test_bare_output.rb +118 -0
  300. data/test/plugin/test_base.rb +115 -0
  301. data/test/plugin/test_buf_file.rb +823 -460
  302. data/test/plugin/test_buf_memory.rb +32 -194
  303. data/test/plugin/test_buffer.rb +1233 -0
  304. data/test/plugin/test_buffer_chunk.rb +198 -0
  305. data/test/plugin/test_buffer_file_chunk.rb +844 -0
  306. data/test/plugin/test_buffer_memory_chunk.rb +338 -0
  307. data/test/plugin/test_compressable.rb +84 -0
  308. data/test/plugin/test_filter.rb +357 -0
  309. data/test/plugin/test_filter_grep.rb +540 -29
  310. data/test/plugin/test_filter_parser.rb +439 -452
  311. data/test/plugin/test_filter_record_transformer.rb +123 -166
  312. data/test/plugin/test_filter_stdout.rb +160 -72
  313. data/test/plugin/test_formatter_csv.rb +111 -0
  314. data/test/plugin/test_formatter_hash.rb +35 -0
  315. data/test/plugin/test_formatter_json.rb +51 -0
  316. data/test/plugin/test_formatter_ltsv.rb +62 -0
  317. data/test/plugin/test_formatter_msgpack.rb +28 -0
  318. data/test/plugin/test_formatter_out_file.rb +95 -0
  319. data/test/plugin/test_formatter_single_value.rb +38 -0
  320. data/test/plugin/test_formatter_tsv.rb +68 -0
  321. data/test/plugin/test_in_debug_agent.rb +24 -1
  322. data/test/plugin/test_in_dummy.rb +111 -18
  323. data/test/plugin/test_in_exec.rb +200 -113
  324. data/test/plugin/test_in_forward.rb +990 -387
  325. data/test/plugin/test_in_gc_stat.rb +10 -8
  326. data/test/plugin/test_in_http.rb +600 -224
  327. data/test/plugin/test_in_monitor_agent.rb +690 -0
  328. data/test/plugin/test_in_object_space.rb +24 -8
  329. data/test/plugin/test_in_syslog.rb +154 -215
  330. data/test/plugin/test_in_tail.rb +1006 -707
  331. data/test/plugin/test_in_tcp.rb +125 -48
  332. data/test/plugin/test_in_udp.rb +204 -63
  333. data/test/plugin/{test_in_stream.rb → test_in_unix.rb} +14 -13
  334. data/test/plugin/test_input.rb +126 -0
  335. data/test/plugin/test_metadata.rb +89 -0
  336. data/test/plugin/test_multi_output.rb +180 -0
  337. data/test/plugin/test_out_copy.rb +117 -112
  338. data/test/plugin/test_out_exec.rb +258 -53
  339. data/test/plugin/test_out_exec_filter.rb +538 -115
  340. data/test/plugin/test_out_file.rb +865 -178
  341. data/test/plugin/test_out_forward.rb +998 -210
  342. data/test/plugin/test_out_null.rb +105 -0
  343. data/test/plugin/test_out_relabel.rb +28 -0
  344. data/test/plugin/test_out_roundrobin.rb +36 -29
  345. data/test/plugin/test_out_secondary_file.rb +458 -0
  346. data/test/plugin/test_out_stdout.rb +135 -37
  347. data/test/plugin/test_out_stream.rb +18 -0
  348. data/test/plugin/test_output.rb +984 -0
  349. data/test/plugin/test_output_as_buffered.rb +2021 -0
  350. data/test/plugin/test_output_as_buffered_backup.rb +312 -0
  351. data/test/plugin/test_output_as_buffered_compress.rb +165 -0
  352. data/test/plugin/test_output_as_buffered_overflow.rb +250 -0
  353. data/test/plugin/test_output_as_buffered_retries.rb +911 -0
  354. data/test/plugin/test_output_as_buffered_secondary.rb +874 -0
  355. data/test/plugin/test_output_as_standard.rb +374 -0
  356. data/test/plugin/test_owned_by.rb +35 -0
  357. data/test/plugin/test_parser.rb +359 -0
  358. data/test/plugin/test_parser_apache.rb +42 -0
  359. data/test/plugin/test_parser_apache2.rb +47 -0
  360. data/test/plugin/test_parser_apache_error.rb +45 -0
  361. data/test/plugin/test_parser_csv.rb +103 -0
  362. data/test/plugin/test_parser_json.rb +138 -0
  363. data/test/plugin/test_parser_labeled_tsv.rb +145 -0
  364. data/test/plugin/test_parser_multiline.rb +100 -0
  365. data/test/plugin/test_parser_nginx.rb +88 -0
  366. data/test/plugin/test_parser_none.rb +52 -0
  367. data/test/plugin/test_parser_regexp.rb +289 -0
  368. data/test/plugin/test_parser_syslog.rb +441 -0
  369. data/test/plugin/test_parser_tsv.rb +122 -0
  370. data/test/plugin/test_storage.rb +167 -0
  371. data/test/plugin/test_storage_local.rb +335 -0
  372. data/test/plugin_helper/data/cert/cert-key.pem +27 -0
  373. data/test/plugin_helper/data/cert/cert-with-no-newline.pem +19 -0
  374. data/test/plugin_helper/data/cert/cert.pem +19 -0
  375. data/test/plugin_helper/http_server/test_app.rb +65 -0
  376. data/test/plugin_helper/http_server/test_route.rb +32 -0
  377. data/test/plugin_helper/test_cert_option.rb +16 -0
  378. data/test/plugin_helper/test_child_process.rb +794 -0
  379. data/test/plugin_helper/test_compat_parameters.rb +353 -0
  380. data/test/plugin_helper/test_event_emitter.rb +51 -0
  381. data/test/plugin_helper/test_event_loop.rb +52 -0
  382. data/test/plugin_helper/test_extract.rb +194 -0
  383. data/test/plugin_helper/test_formatter.rb +255 -0
  384. data/test/plugin_helper/test_http_server_helper.rb +205 -0
  385. data/test/plugin_helper/test_inject.rb +519 -0
  386. data/test/plugin_helper/test_parser.rb +264 -0
  387. data/test/plugin_helper/test_record_accessor.rb +197 -0
  388. data/test/plugin_helper/test_retry_state.rb +442 -0
  389. data/test/plugin_helper/test_server.rb +1714 -0
  390. data/test/plugin_helper/test_storage.rb +542 -0
  391. data/test/plugin_helper/test_thread.rb +164 -0
  392. data/test/plugin_helper/test_timer.rb +132 -0
  393. data/test/scripts/exec_script.rb +0 -6
  394. data/test/scripts/fluent/plugin/formatter1/formatter_test1.rb +7 -0
  395. data/test/scripts/fluent/plugin/formatter2/formatter_test2.rb +7 -0
  396. data/test/scripts/fluent/plugin/out_test.rb +23 -15
  397. data/test/scripts/fluent/plugin/out_test2.rb +80 -0
  398. data/test/test_clock.rb +164 -0
  399. data/test/test_config.rb +16 -7
  400. data/test/test_configdsl.rb +2 -2
  401. data/test/test_event.rb +360 -13
  402. data/test/test_event_router.rb +108 -11
  403. data/test/test_event_time.rb +199 -0
  404. data/test/test_filter.rb +48 -6
  405. data/test/test_formatter.rb +11 -391
  406. data/test/test_input.rb +1 -1
  407. data/test/test_log.rb +591 -31
  408. data/test/test_mixin.rb +1 -1
  409. data/test/test_output.rb +121 -185
  410. data/test/test_plugin.rb +251 -0
  411. data/test/test_plugin_classes.rb +177 -10
  412. data/test/test_plugin_helper.rb +81 -0
  413. data/test/test_plugin_id.rb +101 -0
  414. data/test/test_process.rb +8 -42
  415. data/test/test_root_agent.rb +766 -21
  416. data/test/test_supervisor.rb +481 -0
  417. data/test/test_test_drivers.rb +135 -0
  418. data/test/test_time_formatter.rb +282 -0
  419. data/test/test_time_parser.rb +231 -0
  420. data/test/test_unique_id.rb +47 -0
  421. metadata +454 -60
  422. data/COPYING +0 -14
  423. data/ChangeLog +0 -666
  424. data/lib/fluent/buffer.rb +0 -365
  425. data/lib/fluent/plugin/in_status.rb +0 -76
  426. data/test/plugin/test_in_status.rb +0 -38
  427. data/test/test_buffer.rb +0 -624
  428. 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