fluentd 0.12.43 → 0.14.0

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 (253) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE.md +6 -0
  3. data/.gitignore +2 -0
  4. data/.travis.yml +33 -21
  5. data/CONTRIBUTING.md +1 -0
  6. data/ChangeLog +1239 -0
  7. data/README.md +0 -25
  8. data/Rakefile +2 -1
  9. data/Vagrantfile +17 -0
  10. data/appveyor.yml +35 -0
  11. data/example/filter_stdout.conf +5 -5
  12. data/example/in_forward.conf +2 -2
  13. data/example/in_http.conf +2 -2
  14. data/example/in_out_forward.conf +17 -0
  15. data/example/in_syslog.conf +2 -2
  16. data/example/in_tail.conf +2 -2
  17. data/example/in_tcp.conf +2 -2
  18. data/example/in_udp.conf +2 -2
  19. data/example/out_copy.conf +4 -4
  20. data/example/out_file.conf +2 -2
  21. data/example/out_forward.conf +2 -2
  22. data/example/out_forward_buf_file.conf +23 -0
  23. data/example/v0_12_filter.conf +8 -8
  24. data/fluent.conf +29 -0
  25. data/fluentd.gemspec +18 -11
  26. data/lib/fluent/agent.rb +60 -58
  27. data/lib/fluent/command/cat.rb +1 -1
  28. data/lib/fluent/command/debug.rb +7 -5
  29. data/lib/fluent/command/fluentd.rb +97 -2
  30. data/lib/fluent/compat/call_super_mixin.rb +67 -0
  31. data/lib/fluent/compat/filter.rb +50 -0
  32. data/lib/fluent/compat/formatter.rb +109 -0
  33. data/lib/fluent/compat/input.rb +50 -0
  34. data/lib/fluent/compat/output.rb +617 -0
  35. data/lib/fluent/compat/output_chain.rb +60 -0
  36. data/lib/fluent/compat/parser.rb +163 -0
  37. data/lib/fluent/compat/propagate_default.rb +62 -0
  38. data/lib/fluent/config.rb +23 -20
  39. data/lib/fluent/config/configure_proxy.rb +119 -70
  40. data/lib/fluent/config/dsl.rb +5 -18
  41. data/lib/fluent/config/element.rb +72 -8
  42. data/lib/fluent/config/error.rb +0 -3
  43. data/lib/fluent/config/literal_parser.rb +0 -2
  44. data/lib/fluent/config/parser.rb +4 -4
  45. data/lib/fluent/config/section.rb +39 -28
  46. data/lib/fluent/config/types.rb +2 -13
  47. data/lib/fluent/config/v1_parser.rb +1 -3
  48. data/lib/fluent/configurable.rb +48 -16
  49. data/lib/fluent/daemon.rb +15 -0
  50. data/lib/fluent/engine.rb +26 -52
  51. data/lib/fluent/env.rb +6 -4
  52. data/lib/fluent/event.rb +58 -11
  53. data/lib/fluent/event_router.rb +5 -5
  54. data/lib/fluent/filter.rb +2 -50
  55. data/lib/fluent/formatter.rb +4 -293
  56. data/lib/fluent/input.rb +2 -32
  57. data/lib/fluent/label.rb +2 -2
  58. data/lib/fluent/load.rb +3 -2
  59. data/lib/fluent/log.rb +107 -38
  60. data/lib/fluent/match.rb +0 -36
  61. data/lib/fluent/mixin.rb +117 -7
  62. data/lib/fluent/msgpack_factory.rb +62 -0
  63. data/lib/fluent/output.rb +7 -612
  64. data/lib/fluent/output_chain.rb +23 -0
  65. data/lib/fluent/parser.rb +4 -800
  66. data/lib/fluent/plugin.rb +100 -121
  67. data/lib/fluent/plugin/bare_output.rb +63 -0
  68. data/lib/fluent/plugin/base.rb +121 -0
  69. data/lib/fluent/plugin/buf_file.rb +101 -182
  70. data/lib/fluent/plugin/buf_memory.rb +9 -92
  71. data/lib/fluent/plugin/buffer.rb +473 -0
  72. data/lib/fluent/plugin/buffer/chunk.rb +135 -0
  73. data/lib/fluent/plugin/buffer/file_chunk.rb +339 -0
  74. data/lib/fluent/plugin/buffer/memory_chunk.rb +100 -0
  75. data/lib/fluent/plugin/exec_util.rb +80 -75
  76. data/lib/fluent/plugin/file_util.rb +33 -28
  77. data/lib/fluent/plugin/file_wrapper.rb +120 -0
  78. data/lib/fluent/plugin/filter.rb +51 -0
  79. data/lib/fluent/plugin/filter_grep.rb +13 -40
  80. data/lib/fluent/plugin/filter_record_transformer.rb +22 -18
  81. data/lib/fluent/plugin/formatter.rb +93 -0
  82. data/lib/fluent/plugin/formatter_csv.rb +48 -0
  83. data/lib/fluent/plugin/formatter_hash.rb +32 -0
  84. data/lib/fluent/plugin/formatter_json.rb +47 -0
  85. data/lib/fluent/plugin/formatter_ltsv.rb +42 -0
  86. data/lib/fluent/plugin/formatter_msgpack.rb +32 -0
  87. data/lib/fluent/plugin/formatter_out_file.rb +45 -0
  88. data/lib/fluent/plugin/formatter_single_value.rb +34 -0
  89. data/lib/fluent/plugin/formatter_stdout.rb +39 -0
  90. data/lib/fluent/plugin/in_debug_agent.rb +4 -0
  91. data/lib/fluent/plugin/in_dummy.rb +22 -18
  92. data/lib/fluent/plugin/in_exec.rb +18 -8
  93. data/lib/fluent/plugin/in_forward.rb +36 -79
  94. data/lib/fluent/plugin/in_gc_stat.rb +4 -0
  95. data/lib/fluent/plugin/in_http.rb +21 -18
  96. data/lib/fluent/plugin/in_monitor_agent.rb +15 -48
  97. data/lib/fluent/plugin/in_object_space.rb +6 -1
  98. data/lib/fluent/plugin/in_stream.rb +7 -3
  99. data/lib/fluent/plugin/in_syslog.rb +46 -95
  100. data/lib/fluent/plugin/in_tail.rb +58 -640
  101. data/lib/fluent/plugin/in_tcp.rb +8 -1
  102. data/lib/fluent/plugin/in_udp.rb +8 -18
  103. data/lib/fluent/plugin/input.rb +33 -0
  104. data/lib/fluent/plugin/multi_output.rb +95 -0
  105. data/lib/fluent/plugin/out_buffered_null.rb +59 -0
  106. data/lib/fluent/plugin/out_copy.rb +11 -7
  107. data/lib/fluent/plugin/out_exec.rb +15 -11
  108. data/lib/fluent/plugin/out_exec_filter.rb +18 -10
  109. data/lib/fluent/plugin/out_file.rb +34 -5
  110. data/lib/fluent/plugin/out_forward.rb +25 -19
  111. data/lib/fluent/plugin/out_null.rb +0 -14
  112. data/lib/fluent/plugin/out_roundrobin.rb +11 -7
  113. data/lib/fluent/plugin/out_stdout.rb +5 -7
  114. data/lib/fluent/plugin/out_stream.rb +3 -1
  115. data/lib/fluent/plugin/output.rb +979 -0
  116. data/lib/fluent/plugin/owned_by_mixin.rb +42 -0
  117. data/lib/fluent/plugin/parser.rb +244 -0
  118. data/lib/fluent/plugin/parser_apache.rb +24 -0
  119. data/lib/fluent/plugin/parser_apache2.rb +84 -0
  120. data/lib/fluent/plugin/parser_apache_error.rb +21 -0
  121. data/lib/fluent/plugin/parser_csv.rb +31 -0
  122. data/lib/fluent/plugin/parser_json.rb +79 -0
  123. data/lib/fluent/plugin/parser_ltsv.rb +50 -0
  124. data/lib/fluent/plugin/parser_multiline.rb +102 -0
  125. data/lib/fluent/plugin/parser_nginx.rb +24 -0
  126. data/lib/fluent/plugin/parser_none.rb +36 -0
  127. data/lib/fluent/plugin/parser_syslog.rb +82 -0
  128. data/lib/fluent/plugin/parser_tsv.rb +37 -0
  129. data/lib/fluent/plugin/socket_util.rb +119 -117
  130. data/lib/fluent/plugin/storage.rb +84 -0
  131. data/lib/fluent/plugin/storage_local.rb +116 -0
  132. data/lib/fluent/plugin/string_util.rb +16 -13
  133. data/lib/fluent/plugin_helper.rb +39 -0
  134. data/lib/fluent/plugin_helper/child_process.rb +298 -0
  135. data/lib/fluent/plugin_helper/compat_parameters.rb +99 -0
  136. data/lib/fluent/plugin_helper/event_emitter.rb +80 -0
  137. data/lib/fluent/plugin_helper/event_loop.rb +118 -0
  138. data/lib/fluent/plugin_helper/retry_state.rb +177 -0
  139. data/lib/fluent/plugin_helper/storage.rb +308 -0
  140. data/lib/fluent/plugin_helper/thread.rb +147 -0
  141. data/lib/fluent/plugin_helper/timer.rb +85 -0
  142. data/lib/fluent/plugin_id.rb +63 -0
  143. data/lib/fluent/process.rb +21 -30
  144. data/lib/fluent/registry.rb +21 -9
  145. data/lib/fluent/root_agent.rb +115 -40
  146. data/lib/fluent/supervisor.rb +330 -320
  147. data/lib/fluent/system_config.rb +42 -18
  148. data/lib/fluent/test.rb +6 -1
  149. data/lib/fluent/test/base.rb +23 -3
  150. data/lib/fluent/test/driver/base.rb +247 -0
  151. data/lib/fluent/test/driver/event_feeder.rb +98 -0
  152. data/lib/fluent/test/driver/filter.rb +35 -0
  153. data/lib/fluent/test/driver/input.rb +31 -0
  154. data/lib/fluent/test/driver/output.rb +78 -0
  155. data/lib/fluent/test/driver/test_event_router.rb +45 -0
  156. data/lib/fluent/test/filter_test.rb +0 -1
  157. data/lib/fluent/test/formatter_test.rb +2 -1
  158. data/lib/fluent/test/input_test.rb +23 -17
  159. data/lib/fluent/test/output_test.rb +28 -39
  160. data/lib/fluent/test/parser_test.rb +1 -1
  161. data/lib/fluent/time.rb +104 -1
  162. data/lib/fluent/{status.rb → unique_id.rb} +15 -24
  163. data/lib/fluent/version.rb +1 -1
  164. data/lib/fluent/winsvc.rb +72 -0
  165. data/test/compat/test_calls_super.rb +164 -0
  166. data/test/config/test_config_parser.rb +83 -0
  167. data/test/config/test_configurable.rb +547 -274
  168. data/test/config/test_configure_proxy.rb +146 -29
  169. data/test/config/test_dsl.rb +3 -181
  170. data/test/config/test_element.rb +274 -0
  171. data/test/config/test_literal_parser.rb +1 -1
  172. data/test/config/test_section.rb +79 -7
  173. data/test/config/test_system_config.rb +21 -0
  174. data/test/config/test_types.rb +3 -26
  175. data/test/helper.rb +78 -8
  176. data/test/plugin/test_bare_output.rb +118 -0
  177. data/test/plugin/test_base.rb +75 -0
  178. data/test/plugin/test_buf_file.rb +420 -521
  179. data/test/plugin/test_buf_memory.rb +32 -194
  180. data/test/plugin/test_buffer.rb +981 -0
  181. data/test/plugin/test_buffer_chunk.rb +110 -0
  182. data/test/plugin/test_buffer_file_chunk.rb +770 -0
  183. data/test/plugin/test_buffer_memory_chunk.rb +265 -0
  184. data/test/plugin/test_filter.rb +255 -0
  185. data/test/plugin/test_filter_grep.rb +2 -73
  186. data/test/plugin/test_filter_record_transformer.rb +24 -68
  187. data/test/plugin/test_filter_stdout.rb +6 -6
  188. data/test/plugin/test_in_debug_agent.rb +2 -0
  189. data/test/plugin/test_in_dummy.rb +11 -17
  190. data/test/plugin/test_in_exec.rb +6 -25
  191. data/test/plugin/test_in_forward.rb +112 -151
  192. data/test/plugin/test_in_gc_stat.rb +2 -0
  193. data/test/plugin/test_in_http.rb +106 -157
  194. data/test/plugin/test_in_object_space.rb +21 -5
  195. data/test/plugin/test_in_stream.rb +14 -13
  196. data/test/plugin/test_in_syslog.rb +30 -275
  197. data/test/plugin/test_in_tail.rb +95 -282
  198. data/test/plugin/test_in_tcp.rb +14 -0
  199. data/test/plugin/test_in_udp.rb +21 -67
  200. data/test/plugin/test_input.rb +122 -0
  201. data/test/plugin/test_multi_output.rb +180 -0
  202. data/test/plugin/test_out_buffered_null.rb +79 -0
  203. data/test/plugin/test_out_copy.rb +15 -2
  204. data/test/plugin/test_out_exec.rb +75 -25
  205. data/test/plugin/test_out_exec_filter.rb +74 -8
  206. data/test/plugin/test_out_file.rb +61 -7
  207. data/test/plugin/test_out_forward.rb +92 -15
  208. data/test/plugin/test_out_roundrobin.rb +1 -0
  209. data/test/plugin/test_out_stdout.rb +22 -13
  210. data/test/plugin/test_out_stream.rb +18 -0
  211. data/test/plugin/test_output.rb +515 -0
  212. data/test/plugin/test_output_as_buffered.rb +1540 -0
  213. data/test/plugin/test_output_as_buffered_overflow.rb +247 -0
  214. data/test/plugin/test_output_as_buffered_retries.rb +808 -0
  215. data/test/plugin/test_output_as_buffered_secondary.rb +776 -0
  216. data/test/plugin/test_output_as_standard.rb +362 -0
  217. data/test/plugin/test_owned_by.rb +35 -0
  218. data/test/plugin/test_storage.rb +167 -0
  219. data/test/plugin/test_storage_local.rb +8 -0
  220. data/test/plugin_helper/test_child_process.rb +599 -0
  221. data/test/plugin_helper/test_compat_parameters.rb +175 -0
  222. data/test/plugin_helper/test_event_emitter.rb +51 -0
  223. data/test/plugin_helper/test_event_loop.rb +52 -0
  224. data/test/plugin_helper/test_retry_state.rb +399 -0
  225. data/test/plugin_helper/test_storage.rb +411 -0
  226. data/test/plugin_helper/test_thread.rb +164 -0
  227. data/test/plugin_helper/test_timer.rb +100 -0
  228. data/test/scripts/exec_script.rb +0 -6
  229. data/test/scripts/fluent/plugin/out_test.rb +3 -0
  230. data/test/test_config.rb +13 -4
  231. data/test/test_event.rb +24 -13
  232. data/test/test_event_router.rb +8 -7
  233. data/test/test_event_time.rb +187 -0
  234. data/test/test_formatter.rb +13 -51
  235. data/test/test_input.rb +1 -1
  236. data/test/test_log.rb +239 -16
  237. data/test/test_mixin.rb +1 -1
  238. data/test/test_output.rb +53 -66
  239. data/test/test_parser.rb +105 -323
  240. data/test/test_plugin_helper.rb +81 -0
  241. data/test/test_root_agent.rb +4 -52
  242. data/test/test_supervisor.rb +272 -0
  243. data/test/test_unique_id.rb +47 -0
  244. metadata +181 -55
  245. data/CHANGELOG.md +0 -710
  246. data/lib/fluent/buffer.rb +0 -365
  247. data/lib/fluent/plugin/filter_parser.rb +0 -107
  248. data/lib/fluent/plugin/in_status.rb +0 -76
  249. data/lib/fluent/test/helpers.rb +0 -86
  250. data/test/plugin/data/log/foo/bar2 +0 -0
  251. data/test/plugin/test_filter_parser.rb +0 -744
  252. data/test/plugin/test_in_status.rb +0 -38
  253. data/test/test_buffer.rb +0 -624
@@ -0,0 +1,776 @@
1
+ require_relative '../helper'
2
+ require 'fluent/plugin/output'
3
+ require 'fluent/plugin/buffer'
4
+ require 'fluent/event'
5
+
6
+ require 'json'
7
+ require 'time'
8
+ require 'timeout'
9
+ require 'timecop'
10
+
11
+ module FluentPluginOutputAsBufferedSecondaryTest
12
+ class DummyBareOutput < Fluent::Plugin::Output
13
+ def register(name, &block)
14
+ instance_variable_set("@#{name}", block)
15
+ end
16
+ end
17
+ class DummySyncOutput < DummyBareOutput
18
+ def initialize
19
+ super
20
+ @process = nil
21
+ end
22
+ def process(tag, es)
23
+ @process ? @process.call(tag, es) : nil
24
+ end
25
+ end
26
+ class DummyFullFeatureOutput < DummyBareOutput
27
+ def initialize
28
+ super
29
+ @prefer_buffered_processing = nil
30
+ @prefer_delayed_commit = nil
31
+ @process = nil
32
+ @format = nil
33
+ @write = nil
34
+ @try_write = nil
35
+ end
36
+ def prefer_buffered_processing
37
+ @prefer_buffered_processing ? @prefer_buffered_processing.call : false
38
+ end
39
+ def prefer_delayed_commit
40
+ @prefer_delayed_commit ? @prefer_delayed_commit.call : false
41
+ end
42
+ def process(tag, es)
43
+ @process ? @process.call(tag, es) : nil
44
+ end
45
+ def format(tag, time, record)
46
+ @format ? @format.call(tag, time, record) : [tag, time, record].to_json
47
+ end
48
+ def write(chunk)
49
+ @write ? @write.call(chunk) : nil
50
+ end
51
+ def try_write(chunk)
52
+ @try_write ? @try_write.call(chunk) : nil
53
+ end
54
+ end
55
+ class DummyFullFeatureOutput2 < DummyFullFeatureOutput
56
+ def prefer_buffered_processing; true; end
57
+ def prefer_delayed_commit; super; end
58
+ def format(tag, time, record); super; end
59
+ def write(chunk); super; end
60
+ def try_write(chunk); super; end
61
+ end
62
+ end
63
+
64
+ class BufferedOutputSecondaryTest < Test::Unit::TestCase
65
+ def create_output(type=:full)
66
+ case type
67
+ when :bare then FluentPluginOutputAsBufferedSecondaryTest::DummyBareOutput.new
68
+ when :sync then FluentPluginOutputAsBufferedSecondaryTest::DummySyncOutput.new
69
+ when :full then FluentPluginOutputAsBufferedSecondaryTest::DummyFullFeatureOutput.new
70
+ else
71
+ raise ArgumentError, "unknown type: #{type}"
72
+ end
73
+ end
74
+ def create_metadata(timekey: nil, tag: nil, variables: nil)
75
+ Fluent::Plugin::Buffer::Metadata.new(timekey, tag, variables)
76
+ end
77
+ def waiting(seconds)
78
+ begin
79
+ Timeout.timeout(seconds) do
80
+ yield
81
+ end
82
+ rescue Timeout::Error
83
+ STDERR.print(*@i.log.out.logs)
84
+ raise
85
+ end
86
+ end
87
+ def dummy_event_stream
88
+ Fluent::ArrayEventStream.new([
89
+ [ event_time('2016-04-13 18:33:00'), {"name" => "moris", "age" => 36, "message" => "data1"} ],
90
+ [ event_time('2016-04-13 18:33:13'), {"name" => "moris", "age" => 36, "message" => "data2"} ],
91
+ [ event_time('2016-04-13 18:33:32'), {"name" => "moris", "age" => 36, "message" => "data3"} ],
92
+ ])
93
+ end
94
+
95
+ setup do
96
+ @i = create_output
97
+ end
98
+
99
+ teardown do
100
+ if @i
101
+ @i.stop unless @i.stopped?
102
+ @i.before_shutdown unless @i.before_shutdown?
103
+ @i.shutdown unless @i.shutdown?
104
+ @i.after_shutdown unless @i.after_shutdown?
105
+ @i.close unless @i.closed?
106
+ @i.terminate unless @i.terminated?
107
+ end
108
+ Timecop.return
109
+ end
110
+
111
+ sub_test_case 'secondary plugin feature for buffered output with periodical retry' do
112
+ setup do
113
+ Fluent::Plugin.register_output('output_secondary_test', FluentPluginOutputAsBufferedSecondaryTest::DummyFullFeatureOutput)
114
+ Fluent::Plugin.register_output('output_secondary_test2', FluentPluginOutputAsBufferedSecondaryTest::DummyFullFeatureOutput2)
115
+ end
116
+
117
+ test 'raises configuration error if primary does not support buffering' do
118
+ i = create_output(:sync)
119
+ assert_raise Fluent::ConfigError do
120
+ i.configure(config_element('ROOT','',{},[config_element('secondary','',{'@type'=>'output_secondary_test'})]))
121
+ end
122
+ end
123
+
124
+ test 'raises configuration error if <buffer>/<secondary> section is specified in <secondary> section' do
125
+ priconf = config_element('buffer','tag',{'flush_interval' => 1, 'retry_type' => :periodic, 'retry_wait' => 3, 'retry_timeout' => 30, 'retry_randomize' => false})
126
+ secconf1 = config_element('secondary','',{'@type' => 'output_secondary_test'},[config_element('buffer', 'time')])
127
+ secconf2 = config_element('secondary','',{'@type' => 'output_secondary_test'},[config_element('secondary', '')])
128
+ i = create_output()
129
+ assert_raise Fluent::ConfigError do
130
+ i.configure(config_element('ROOT','',{},[priconf,secconf1]))
131
+ end
132
+ assert_raise Fluent::ConfigError do
133
+ i.configure(config_element('ROOT','',{},[priconf,secconf2]))
134
+ end
135
+ end
136
+
137
+ test 'warns if secondary plugin is different type from primary one' do
138
+ priconf = config_element('buffer','tag',{'flush_interval' => 1, 'retry_type' => :periodic, 'retry_wait' => 3, 'retry_timeout' => 30, 'retry_randomize' => false})
139
+ secconf = config_element('secondary','',{'@type' => 'output_secondary_test2'})
140
+ i = create_output()
141
+ i.configure(config_element('ROOT','',{},[priconf,secconf]))
142
+ logs = i.log.out.logs
143
+ assert{ logs.any?{|l| l.include?("secondary type should be same with primary one") } }
144
+ end
145
+
146
+ test 'secondary plugin lifecycle is kicked by primary' do
147
+ priconf = config_element('buffer','tag',{'flush_interval' => 1, 'retry_type' => :periodic, 'retry_wait' => 3, 'retry_timeout' => 30, 'retry_randomize' => false})
148
+ secconf = config_element('secondary','',{'@type' => 'output_secondary_test2'})
149
+ i = create_output()
150
+ i.configure(config_element('ROOT','',{},[priconf,secconf]))
151
+ logs = i.log.out.logs
152
+ assert{ logs.any?{|l| l.include?("secondary type should be same with primary one") } }
153
+
154
+ assert i.secondary.configured?
155
+
156
+ assert !i.secondary.started?
157
+ i.start
158
+ assert i.secondary.started?
159
+
160
+ assert !i.secondary.stopped?
161
+ i.stop
162
+ assert i.secondary.stopped?
163
+
164
+ assert !i.secondary.before_shutdown?
165
+ i.before_shutdown
166
+ assert i.secondary.before_shutdown?
167
+
168
+ assert !i.secondary.shutdown?
169
+ i.shutdown
170
+ assert i.secondary.shutdown?
171
+
172
+ assert !i.secondary.after_shutdown?
173
+ i.after_shutdown
174
+ assert i.secondary.after_shutdown?
175
+
176
+ assert !i.secondary.closed?
177
+ i.close
178
+ assert i.secondary.closed?
179
+
180
+ assert !i.secondary.terminated?
181
+ i.terminate
182
+ assert i.secondary.terminated?
183
+ end
184
+
185
+ test 'primary plugin will emit event streams to secondary after retries for time of retry_timeout * retry_secondary_threshold' do
186
+ written = []
187
+ priconf = config_element('buffer','tag',{'flush_interval' => 1, 'retry_type' => :periodic, 'retry_wait' => 3, 'retry_timeout' => 60, 'retry_randomize' => false})
188
+ secconf = config_element('secondary','',{'@type' => 'output_secondary_test2'})
189
+ @i.configure(config_element('ROOT','',{},[priconf,secconf]))
190
+ @i.register(:prefer_buffered_processing){ true }
191
+ @i.register(:prefer_delayed_commit){ false }
192
+ @i.register(:format){|tag,time,record| [tag,time.to_i,record].to_json + "\n" }
193
+ @i.register(:write){|chunk| raise "yay, your #write must fail" }
194
+ @i.secondary.register(:prefer_delayed_commit){ false }
195
+ @i.secondary.register(:write){|chunk| chunk.read.split("\n").each{|line| written << JSON.parse(line) } }
196
+ @i.start
197
+
198
+ now = Time.parse('2016-04-13 18:33:30 -0700')
199
+ Timecop.freeze( now )
200
+
201
+ @i.emit_events("test.tag.1", dummy_event_stream())
202
+
203
+ now = Time.parse('2016-04-13 18:33:31 -0700')
204
+ Timecop.freeze( now )
205
+
206
+ @i.emit_events("test.tag.2", dummy_event_stream())
207
+
208
+ assert_equal 0, @i.write_count
209
+ assert_equal 0, @i.num_errors
210
+
211
+ @i.enqueue_thread_wait
212
+ @i.flush_thread_wakeup
213
+ waiting(4){ Thread.pass until @i.write_count > 0 && @i.num_errors > 0 }
214
+
215
+ assert{ @i.buffer.queue.size > 0 }
216
+ assert{ @i.buffer.queue.first.metadata.tag == 'test.tag.1' }
217
+
218
+ assert{ @i.write_count > 0 }
219
+ assert{ @i.num_errors > 0 }
220
+
221
+ prev_write_count = @i.write_count
222
+ prev_num_errors = @i.num_errors
223
+
224
+ first_failure = @i.retry.start
225
+
226
+ # retry_timeout == 60(sec), retry_secondary_threshold == 0.8
227
+
228
+ now = first_failure + 60 * 0.8 + 1
229
+
230
+ Timecop.freeze( now )
231
+ @i.enqueue_thread_wait
232
+ @i.flush_thread_wakeup
233
+ waiting(4){ Thread.pass until @i.write_count > prev_write_count }
234
+
235
+ assert{ @i.write_count > prev_write_count }
236
+ assert{ @i.num_errors == prev_num_errors }
237
+
238
+ assert_nil @i.retry
239
+
240
+ assert_equal [ 'test.tag.1', event_time('2016-04-13 18:33:00').to_i, {"name" => "moris", "age" => 36, "message" => "data1"} ], written[0]
241
+ assert_equal [ 'test.tag.1', event_time('2016-04-13 18:33:13').to_i, {"name" => "moris", "age" => 36, "message" => "data2"} ], written[1]
242
+ assert_equal [ 'test.tag.1', event_time('2016-04-13 18:33:32').to_i, {"name" => "moris", "age" => 36, "message" => "data3"} ], written[2]
243
+
244
+ assert{ @i.log.out.logs.any?{|l| l.include?("[warn]: retry succeeded by secondary.") } }
245
+ end
246
+
247
+ test 'secondary can do non-delayed commit even if primary do delayed commit' do
248
+ written = []
249
+ priconf = config_element('buffer','tag',{'flush_interval' => 1, 'retry_type' => :periodic, 'retry_wait' => 3, 'retry_timeout' => 60, 'retry_randomize' => false})
250
+ secconf = config_element('secondary','',{'@type' => 'output_secondary_test2'})
251
+ @i.configure(config_element('ROOT','',{},[priconf,secconf]))
252
+ @i.register(:prefer_buffered_processing){ true }
253
+ @i.register(:prefer_delayed_commit){ true }
254
+ @i.register(:format){|tag,time,record| [tag,time.to_i,record].to_json + "\n" }
255
+ @i.register(:try_write){|chunk| raise "yay, your #write must fail" }
256
+ @i.secondary.register(:prefer_delayed_commit){ false }
257
+ @i.secondary.register(:write){|chunk| chunk.read.split("\n").each{|line| written << JSON.parse(line) } }
258
+ @i.start
259
+
260
+ now = Time.parse('2016-04-13 18:33:30 -0700')
261
+ Timecop.freeze( now )
262
+
263
+ @i.emit_events("test.tag.1", dummy_event_stream())
264
+
265
+ now = Time.parse('2016-04-13 18:33:31 -0700')
266
+ Timecop.freeze( now )
267
+
268
+ @i.emit_events("test.tag.2", dummy_event_stream())
269
+
270
+ assert_equal 0, @i.write_count
271
+ assert_equal 0, @i.num_errors
272
+
273
+ @i.enqueue_thread_wait
274
+ @i.flush_thread_wakeup
275
+ waiting(4){ Thread.pass until @i.write_count > 0 && @i.num_errors > 0 }
276
+
277
+ assert{ @i.buffer.queue.size > 0 }
278
+ assert{ @i.buffer.queue.first.metadata.tag == 'test.tag.1' }
279
+
280
+ assert{ @i.write_count > 0 }
281
+ assert{ @i.num_errors > 0 }
282
+
283
+ prev_write_count = @i.write_count
284
+ prev_num_errors = @i.num_errors
285
+
286
+ first_failure = @i.retry.start
287
+
288
+ # retry_timeout == 60(sec), retry_secondary_threshold == 0.8
289
+
290
+ now = first_failure + 60 * 0.8 + 1
291
+
292
+ Timecop.freeze( now )
293
+ @i.enqueue_thread_wait
294
+ @i.flush_thread_wakeup
295
+ waiting(4){ Thread.pass until @i.write_count > prev_write_count }
296
+
297
+ assert{ @i.write_count > prev_write_count }
298
+ assert{ @i.num_errors == prev_num_errors }
299
+
300
+ assert_nil @i.retry
301
+
302
+ assert_equal [ 'test.tag.1', event_time('2016-04-13 18:33:00').to_i, {"name" => "moris", "age" => 36, "message" => "data1"} ], written[0]
303
+ assert_equal [ 'test.tag.1', event_time('2016-04-13 18:33:13').to_i, {"name" => "moris", "age" => 36, "message" => "data2"} ], written[1]
304
+ assert_equal [ 'test.tag.1', event_time('2016-04-13 18:33:32').to_i, {"name" => "moris", "age" => 36, "message" => "data3"} ], written[2]
305
+
306
+ assert{ @i.log.out.logs.any?{|l| l.include?("[warn]: retry succeeded by secondary.") } }
307
+ end
308
+
309
+ test 'secondary plugin can do delayed commit if primary do it' do
310
+ written = []
311
+ chunks = []
312
+ priconf = config_element('buffer','tag',{'flush_interval' => 1, 'retry_type' => :periodic, 'retry_wait' => 3, 'retry_timeout' => 60, 'retry_randomize' => false})
313
+ secconf = config_element('secondary','',{'@type' => 'output_secondary_test2'})
314
+ @i.configure(config_element('ROOT','',{},[priconf,secconf]))
315
+ @i.register(:prefer_buffered_processing){ true }
316
+ @i.register(:prefer_delayed_commit){ true }
317
+ @i.register(:format){|tag,time,record| [tag,time.to_i,record].to_json + "\n" }
318
+ @i.register(:try_write){|chunk| raise "yay, your #write must fail" }
319
+ @i.secondary.register(:prefer_delayed_commit){ true }
320
+ @i.secondary.register(:try_write){|chunk| chunks << chunk; chunk.read.split("\n").each{|line| written << JSON.parse(line) } }
321
+ @i.start
322
+
323
+ now = Time.parse('2016-04-13 18:33:30 -0700')
324
+ Timecop.freeze( now )
325
+
326
+ @i.emit_events("test.tag.1", dummy_event_stream())
327
+
328
+ now = Time.parse('2016-04-13 18:33:31 -0700')
329
+ Timecop.freeze( now )
330
+
331
+ @i.emit_events("test.tag.2", dummy_event_stream())
332
+
333
+ assert_equal 0, @i.write_count
334
+ assert_equal 0, @i.num_errors
335
+
336
+ @i.enqueue_thread_wait
337
+ @i.flush_thread_wakeup
338
+ waiting(4){ Thread.pass until @i.write_count > 0 && @i.num_errors > 0 }
339
+
340
+ assert{ @i.buffer.queue.size > 0 }
341
+ assert{ @i.buffer.queue.first.metadata.tag == 'test.tag.1' }
342
+
343
+ assert{ @i.write_count > 0 }
344
+ assert{ @i.num_errors > 0 }
345
+
346
+ prev_write_count = @i.write_count
347
+ prev_num_errors = @i.num_errors
348
+
349
+ first_failure = @i.retry.start
350
+
351
+ # retry_timeout == 60(sec), retry_secondary_threshold == 0.8
352
+
353
+ now = first_failure + 60 * 0.8 + 1
354
+
355
+ Timecop.freeze( now )
356
+ @i.enqueue_thread_wait
357
+ @i.flush_thread_wakeup
358
+ waiting(4){ Thread.pass until @i.write_count > prev_write_count }
359
+
360
+ assert{ @i.write_count > prev_write_count }
361
+ assert{ @i.num_errors == prev_num_errors }
362
+
363
+ assert @i.retry
364
+
365
+ assert_equal [ 'test.tag.1', event_time('2016-04-13 18:33:00').to_i, {"name" => "moris", "age" => 36, "message" => "data1"} ], written[0]
366
+ assert_equal [ 'test.tag.1', event_time('2016-04-13 18:33:13').to_i, {"name" => "moris", "age" => 36, "message" => "data2"} ], written[1]
367
+ assert_equal [ 'test.tag.1', event_time('2016-04-13 18:33:32').to_i, {"name" => "moris", "age" => 36, "message" => "data3"} ], written[2]
368
+
369
+ assert{ @i.buffer.dequeued.size > 0 }
370
+ assert{ chunks.size > 0 }
371
+ assert{ !chunks.first.empty? }
372
+
373
+ @i.secondary.commit_write(chunks[0].unique_id)
374
+
375
+ assert{ @i.buffer.dequeued[chunks[0].unique_id].nil? }
376
+ assert{ chunks.first.empty? }
377
+
378
+ assert_nil @i.retry
379
+
380
+ assert{ @i.log.out.logs.any?{|l| l.include?("[warn]: retry succeeded by secondary.") } }
381
+ end
382
+
383
+ test 'secondary plugin can do delayed commit even if primary does not do it' do
384
+ written = []
385
+ chunks = []
386
+ priconf = config_element('buffer','tag',{'flush_interval' => 1, 'retry_type' => :periodic, 'retry_wait' => 3, 'retry_timeout' => 60, 'retry_randomize' => false})
387
+ secconf = config_element('secondary','',{'@type' => 'output_secondary_test2'})
388
+ @i.configure(config_element('ROOT','',{},[priconf,secconf]))
389
+ @i.register(:prefer_buffered_processing){ true }
390
+ @i.register(:prefer_delayed_commit){ false }
391
+ @i.register(:format){|tag,time,record| [tag,time.to_i,record].to_json + "\n" }
392
+ @i.register(:write){|chunk| raise "yay, your #write must fail" }
393
+ @i.secondary.register(:prefer_delayed_commit){ true }
394
+ @i.secondary.register(:try_write){|chunk| chunks << chunk; chunk.read.split("\n").each{|line| written << JSON.parse(line) } }
395
+ @i.start
396
+
397
+ now = Time.parse('2016-04-13 18:33:30 -0700')
398
+ Timecop.freeze( now )
399
+
400
+ @i.emit_events("test.tag.1", dummy_event_stream())
401
+
402
+ now = Time.parse('2016-04-13 18:33:31 -0700')
403
+ Timecop.freeze( now )
404
+
405
+ @i.emit_events("test.tag.2", dummy_event_stream())
406
+
407
+ assert_equal 0, @i.write_count
408
+ assert_equal 0, @i.num_errors
409
+
410
+ @i.enqueue_thread_wait
411
+ @i.flush_thread_wakeup
412
+ waiting(4){ Thread.pass until @i.write_count > 0 && @i.num_errors > 0 }
413
+
414
+ assert{ @i.buffer.queue.size > 0 }
415
+ assert{ @i.buffer.queue.first.metadata.tag == 'test.tag.1' }
416
+
417
+ assert{ @i.write_count > 0 }
418
+ assert{ @i.num_errors > 0 }
419
+
420
+ prev_write_count = @i.write_count
421
+ prev_num_errors = @i.num_errors
422
+
423
+ first_failure = @i.retry.start
424
+
425
+ # retry_timeout == 60(sec), retry_secondary_threshold == 0.8
426
+
427
+ now = first_failure + 60 * 0.8 + 1
428
+
429
+ Timecop.freeze( now )
430
+ @i.enqueue_thread_wait
431
+ @i.flush_thread_wakeup
432
+ waiting(4){ Thread.pass until @i.write_count > prev_write_count }
433
+
434
+ assert{ @i.write_count > prev_write_count }
435
+ assert{ @i.num_errors == prev_num_errors }
436
+
437
+ assert @i.retry
438
+
439
+ assert_equal [ 'test.tag.1', event_time('2016-04-13 18:33:00').to_i, {"name" => "moris", "age" => 36, "message" => "data1"} ], written[0]
440
+ assert_equal [ 'test.tag.1', event_time('2016-04-13 18:33:13').to_i, {"name" => "moris", "age" => 36, "message" => "data2"} ], written[1]
441
+ assert_equal [ 'test.tag.1', event_time('2016-04-13 18:33:32').to_i, {"name" => "moris", "age" => 36, "message" => "data3"} ], written[2]
442
+
443
+ assert{ @i.buffer.dequeued.size > 0 }
444
+ assert{ chunks.size > 0 }
445
+ assert{ !chunks.first.empty? }
446
+
447
+ @i.secondary.commit_write(chunks[0].unique_id)
448
+
449
+ assert{ @i.buffer.dequeued[chunks[0].unique_id].nil? }
450
+ assert{ chunks.first.empty? }
451
+
452
+ assert_nil @i.retry
453
+
454
+ assert{ @i.log.out.logs.any?{|l| l.include?("[warn]: retry succeeded by secondary.") } }
455
+ end
456
+
457
+ test 'secondary plugin can do delayed commit even if primary does not do it, and non-committed chunks will be rollbacked by primary' do
458
+ written = []
459
+ chunks = []
460
+ priconf = config_element('buffer','tag',{'flush_interval' => 1, 'retry_type' => :periodic, 'retry_wait' => 3, 'retry_timeout' => 60, 'delayed_commit_timeout' => 2, 'retry_randomize' => false})
461
+ secconf = config_element('secondary','',{'@type' => 'output_secondary_test2'})
462
+ @i.configure(config_element('ROOT','',{},[priconf,secconf]))
463
+ @i.register(:prefer_buffered_processing){ true }
464
+ @i.register(:prefer_delayed_commit){ false }
465
+ @i.register(:format){|tag,time,record| [tag,time.to_i,record].to_json + "\n" }
466
+ @i.register(:write){|chunk| raise "yay, your #write must fail" }
467
+ @i.secondary.register(:prefer_delayed_commit){ true }
468
+ @i.secondary.register(:try_write){|chunk| chunks << chunk; chunk.read.split("\n").each{|line| written << JSON.parse(line) } }
469
+ @i.secondary.register(:write){|chunk| raise "don't use this" }
470
+ @i.start
471
+
472
+ now = Time.parse('2016-04-13 18:33:30 -0700')
473
+ Timecop.freeze( now )
474
+
475
+ @i.emit_events("test.tag.1", dummy_event_stream())
476
+ @i.emit_events("test.tag.2", dummy_event_stream())
477
+
478
+ now = Time.parse('2016-04-13 18:33:31 -0700')
479
+ Timecop.freeze( now )
480
+
481
+ assert_equal 0, @i.write_count
482
+ assert_equal 0, @i.num_errors
483
+
484
+ @i.enqueue_thread_wait
485
+ @i.flush_thread_wakeup
486
+ waiting(4){ Thread.pass until @i.write_count > 0 && @i.num_errors > 0 }
487
+
488
+ assert{ @i.buffer.queue.size == 2 }
489
+ assert{ @i.buffer.queue.first.metadata.tag == 'test.tag.1' }
490
+
491
+ assert{ @i.write_count > 0 }
492
+ assert{ @i.num_errors > 0 }
493
+
494
+ prev_write_count = @i.write_count
495
+ prev_num_errors = @i.num_errors
496
+
497
+ first_failure = @i.retry.start
498
+
499
+ # retry_timeout == 60(sec), retry_secondary_threshold == 0.8
500
+
501
+ now = first_failure + 60 * 0.8 + 1
502
+ Timecop.freeze( now )
503
+ @i.enqueue_thread_wait
504
+ @i.flush_thread_wakeup
505
+ now = first_failure + 60 * 0.8 + 2
506
+ Timecop.freeze( now )
507
+ @i.enqueue_thread_wait
508
+ @i.flush_thread_wakeup
509
+
510
+ waiting(4){ Thread.pass until chunks.size == 2 }
511
+
512
+ assert{ @i.write_count > prev_write_count }
513
+ assert{ @i.num_errors == prev_num_errors }
514
+
515
+ assert @i.retry
516
+
517
+ assert_equal [ 'test.tag.1', event_time('2016-04-13 18:33:00').to_i, {"name" => "moris", "age" => 36, "message" => "data1"} ], written[0]
518
+ assert_equal [ 'test.tag.1', event_time('2016-04-13 18:33:13').to_i, {"name" => "moris", "age" => 36, "message" => "data2"} ], written[1]
519
+ assert_equal [ 'test.tag.1', event_time('2016-04-13 18:33:32').to_i, {"name" => "moris", "age" => 36, "message" => "data3"} ], written[2]
520
+ assert_equal [ 'test.tag.2', event_time('2016-04-13 18:33:00').to_i, {"name" => "moris", "age" => 36, "message" => "data1"} ], written[3]
521
+ assert_equal [ 'test.tag.2', event_time('2016-04-13 18:33:13').to_i, {"name" => "moris", "age" => 36, "message" => "data2"} ], written[4]
522
+ assert_equal [ 'test.tag.2', event_time('2016-04-13 18:33:32').to_i, {"name" => "moris", "age" => 36, "message" => "data3"} ], written[5]
523
+
524
+ assert{ @i.buffer.dequeued.size == 2 }
525
+ assert{ chunks.size == 2 }
526
+ assert{ !chunks[0].empty? }
527
+ assert{ !chunks[1].empty? }
528
+
529
+ 30.times do |i| # large enough
530
+ now = first_failure + 60 * 0.8 + 2 + i
531
+ Timecop.freeze( now )
532
+ @i.flush_thread_wakeup
533
+
534
+ break if @i.buffer.dequeued.size == 0
535
+ end
536
+
537
+ assert @i.retry
538
+ logs = @i.log.out.logs
539
+ assert{ logs.select{|l| l.include?("[warn]: failed to flush the buffer chunk, timeout to commit.") }.size == 2 }
540
+ end
541
+
542
+ test 'retry_wait for secondary is same with one for primary' do
543
+ priconf = config_element('buffer','tag',{'flush_interval' => 1, 'retry_type' => :periodic, 'retry_wait' => 3, 'retry_timeout' => 60, 'retry_randomize' => false})
544
+ secconf = config_element('secondary','',{'@type' => 'output_secondary_test2'})
545
+ @i.configure(config_element('ROOT','',{},[priconf,secconf]))
546
+ @i.register(:prefer_buffered_processing){ true }
547
+ @i.register(:prefer_delayed_commit){ false }
548
+ @i.register(:format){|tag,time,record| [tag,time.to_i,record].to_json + "\n" }
549
+ @i.register(:write){|chunk| raise "yay, your #write must fail" }
550
+ @i.secondary.register(:prefer_delayed_commit){ false }
551
+ @i.secondary.register(:write){|chunk| raise "your secondary is also useless." }
552
+ @i.start
553
+
554
+ now = Time.parse('2016-04-13 18:33:30 -0700')
555
+ Timecop.freeze( now )
556
+
557
+ @i.emit_events("test.tag.1", dummy_event_stream())
558
+
559
+ now = Time.parse('2016-04-13 18:33:31 -0700')
560
+ Timecop.freeze( now )
561
+
562
+ @i.emit_events("test.tag.2", dummy_event_stream())
563
+
564
+ assert_equal 0, @i.write_count
565
+ assert_equal 0, @i.num_errors
566
+
567
+ @i.enqueue_thread_wait
568
+ @i.flush_thread_wakeup
569
+ waiting(4){ Thread.pass until @i.write_count > 0 && @i.num_errors > 0 }
570
+
571
+ assert{ @i.buffer.queue.size > 0 }
572
+ assert{ @i.buffer.queue.first.metadata.tag == 'test.tag.1' }
573
+
574
+ assert{ @i.write_count > 0 }
575
+ assert{ @i.num_errors > 0 }
576
+
577
+ prev_write_count = @i.write_count
578
+ prev_num_errors = @i.num_errors
579
+
580
+ first_failure = @i.retry.start
581
+
582
+ # retry_timeout == 60(sec), retry_secondary_threshold == 0.8
583
+
584
+ now = first_failure + 60 * 0.8 + 1
585
+
586
+ Timecop.freeze( now )
587
+ @i.enqueue_thread_wait
588
+ @i.flush_thread_wakeup
589
+ waiting(4){ Thread.pass until @i.write_count > prev_write_count }
590
+
591
+ assert{ @i.write_count > prev_write_count }
592
+ assert{ @i.num_errors > prev_num_errors }
593
+
594
+ assert @i.retry
595
+
596
+ assert_equal 3, (@i.next_flush_time - Time.now)
597
+
598
+ logs = @i.log.out.logs
599
+ assert{ logs.any?{|l| l.include?("[warn]: failed to flush the buffer with secondary output.") } }
600
+ end
601
+ end
602
+
603
+ sub_test_case 'secondary plugin feature for buffered output with exponential backoff' do
604
+ setup do
605
+ Fluent::Plugin.register_output('output_secondary_test', FluentPluginOutputAsBufferedSecondaryTest::DummyFullFeatureOutput)
606
+ Fluent::Plugin.register_output('output_secondary_test2', FluentPluginOutputAsBufferedSecondaryTest::DummyFullFeatureOutput2)
607
+ end
608
+
609
+ test 'primary plugin will emit event streams to secondary after retries for time of retry_timeout * retry_secondary_threshold' do
610
+ written = []
611
+ priconf = config_element('buffer','tag',{'flush_interval' => 1, 'retry_type' => :exponential_backoff, 'retry_wait' => 1, 'retry_timeout' => 60, 'retry_randomize' => false})
612
+ secconf = config_element('secondary','',{'@type' => 'output_secondary_test2'})
613
+ @i.configure(config_element('ROOT','',{},[priconf,secconf]))
614
+ @i.register(:prefer_buffered_processing){ true }
615
+ @i.register(:prefer_delayed_commit){ false }
616
+ @i.register(:format){|tag,time,record| [tag,time.to_i,record].to_json + "\n" }
617
+ @i.register(:write){|chunk| raise "yay, your #write must fail" }
618
+ @i.secondary.register(:prefer_delayed_commit){ false }
619
+ @i.secondary.register(:write){|chunk| chunk.read.split("\n").each{|line| written << JSON.parse(line) } }
620
+ @i.start
621
+
622
+ now = Time.parse('2016-04-13 18:33:30 -0700')
623
+ Timecop.freeze( now )
624
+
625
+ @i.emit_events("test.tag.1", dummy_event_stream())
626
+
627
+ now = Time.parse('2016-04-13 18:33:31 -0700')
628
+ Timecop.freeze( now )
629
+
630
+ @i.emit_events("test.tag.2", dummy_event_stream())
631
+
632
+ assert_equal 0, @i.write_count
633
+ assert_equal 0, @i.num_errors
634
+
635
+ @i.enqueue_thread_wait
636
+ @i.flush_thread_wakeup
637
+ waiting(4){ Thread.pass until @i.write_count > 0 && @i.num_errors > 0 }
638
+
639
+ assert{ @i.buffer.queue.size > 0 }
640
+ assert{ @i.buffer.queue.first.metadata.tag == 'test.tag.1' }
641
+
642
+ assert{ @i.write_count > 0 }
643
+ assert{ @i.num_errors > 0 }
644
+
645
+ prev_write_count = @i.write_count
646
+ prev_num_errors = @i.num_errors
647
+
648
+ first_failure = @i.retry.start
649
+
650
+ 20.times do |i| # large enough
651
+ now = @i.next_flush_time
652
+ Timecop.freeze( now )
653
+ @i.enqueue_thread_wait
654
+ @i.flush_thread_wakeup
655
+ waiting(4){ Thread.pass until @i.write_count > prev_write_count }
656
+
657
+ assert{ @i.write_count > prev_write_count }
658
+
659
+ break if @i.buffer.queue.size == 0
660
+
661
+ prev_write_count = @i.write_count
662
+ prev_num_errors = @i.num_errors
663
+ end
664
+
665
+ # retry_timeout == 60(sec), retry_secondary_threshold == 0.8
666
+
667
+ assert{ now >= first_failure + 60 * 0.8 }
668
+
669
+ assert_nil @i.retry
670
+
671
+ assert_equal [ 'test.tag.1', event_time('2016-04-13 18:33:00').to_i, {"name" => "moris", "age" => 36, "message" => "data1"} ], written[0]
672
+ assert_equal [ 'test.tag.1', event_time('2016-04-13 18:33:13').to_i, {"name" => "moris", "age" => 36, "message" => "data2"} ], written[1]
673
+ assert_equal [ 'test.tag.1', event_time('2016-04-13 18:33:32').to_i, {"name" => "moris", "age" => 36, "message" => "data3"} ], written[2]
674
+
675
+ assert{ @i.log.out.logs.any?{|l| l.include?("[warn]: retry succeeded by secondary.") } }
676
+ end
677
+
678
+ test 'exponential backoff interval will be initialized when switched to secondary' do
679
+ priconf = config_element('buffer','tag',{'flush_interval' => 1, 'retry_type' => :exponential_backoff, 'retry_wait' => 1, 'retry_timeout' => 60, 'retry_randomize' => false})
680
+ secconf = config_element('secondary','',{'@type' => 'output_secondary_test2'})
681
+ @i.configure(config_element('ROOT','',{},[priconf,secconf]))
682
+ @i.register(:prefer_buffered_processing){ true }
683
+ @i.register(:prefer_delayed_commit){ false }
684
+ @i.register(:format){|tag,time,record| [tag,time.to_i,record].to_json + "\n" }
685
+ @i.register(:write){|chunk| raise "yay, your #write must fail" }
686
+ @i.secondary.register(:prefer_delayed_commit){ false }
687
+ @i.secondary.register(:write){|chunk| raise "your secondary is also useless." }
688
+ @i.start
689
+
690
+ now = Time.parse('2016-04-13 18:33:30 -0700')
691
+ Timecop.freeze( now )
692
+
693
+ @i.emit_events("test.tag.1", dummy_event_stream())
694
+
695
+ now = Time.parse('2016-04-13 18:33:31 -0700')
696
+ Timecop.freeze( now )
697
+
698
+ @i.emit_events("test.tag.2", dummy_event_stream())
699
+
700
+ assert_equal 0, @i.write_count
701
+ assert_equal 0, @i.num_errors
702
+
703
+ @i.enqueue_thread_wait
704
+ @i.flush_thread_wakeup
705
+ waiting(4){ Thread.pass until @i.write_count > 0 && @i.num_errors > 0 }
706
+
707
+ assert{ @i.buffer.queue.size > 0 }
708
+ assert{ @i.buffer.queue.first.metadata.tag == 'test.tag.1' }
709
+
710
+ assert{ @i.write_count > 0 }
711
+ assert{ @i.num_errors > 0 }
712
+
713
+ prev_write_count = @i.write_count
714
+ prev_num_errors = @i.num_errors
715
+
716
+ first_failure = @i.retry.start
717
+
718
+ 20.times do |i| # large enough
719
+ now = @i.next_flush_time
720
+ # p({i: i, now: now, diff: (now - Time.now)})
721
+ # {:i=>0, :now=>2016-04-13 18:33:32 -0700, :diff=>1.0}
722
+ # {:i=>1, :now=>2016-04-13 18:33:34 -0700, :diff=>2.0}
723
+ # {:i=>2, :now=>2016-04-13 18:33:38 -0700, :diff=>4.0}
724
+ # {:i=>3, :now=>2016-04-13 18:33:46 -0700, :diff=>8.0}
725
+ # {:i=>4, :now=>2016-04-13 18:34:02 -0700, :diff=>16.0}
726
+ # {:i=>5, :now=>2016-04-13 18:34:19 -0700, :diff=>17.0}
727
+ Timecop.freeze( now )
728
+ @i.enqueue_thread_wait
729
+ @i.flush_thread_wakeup
730
+ waiting(4){ Thread.pass until @i.write_count > prev_write_count }
731
+
732
+ assert{ @i.write_count > prev_write_count }
733
+ assert{ @i.num_errors > prev_num_errors }
734
+
735
+ prev_write_count = @i.write_count
736
+ prev_num_errors = @i.num_errors
737
+
738
+ break if @i.retry.secondary?
739
+
740
+ assert{ @i.buffer.queue.first.metadata.tag == 'test.tag.1' }
741
+ end
742
+
743
+ # retry_timeout == 60(sec), retry_secondary_threshold == 0.8
744
+
745
+ assert{ now >= first_failure + 60 * 0.8 }
746
+ assert @i.retry
747
+ logs = @i.log.out.logs
748
+ assert{ logs.any?{|l| l.include?("[warn]: failed to flush the buffer with secondary output.") } }
749
+
750
+ assert{ (@i.next_flush_time - Time.now) <= 2 } # <= retry_wait (1s) * base (2) ** 1
751
+
752
+ 20.times do |i| # large enough again
753
+ now = @i.next_flush_time
754
+ # p({i: i, now: now, diff: (now - Time.now)})
755
+ # {:i=>0, :now=>2016-04-13 18:34:20 -0700, :diff=>1.0}
756
+ # {:i=>1, :now=>2016-04-13 18:34:24 -0700, :diff=>4.0}
757
+ # {:i=>2, :now=>2016-04-13 18:34:31 -0700, :diff=>7.0}
758
+
759
+ Timecop.freeze( now )
760
+ @i.enqueue_thread_wait
761
+ @i.flush_thread_wakeup
762
+ waiting(4){ Thread.pass until @i.write_count > prev_write_count }
763
+
764
+ assert{ @i.write_count > prev_write_count }
765
+ assert{ @i.num_errors > prev_num_errors }
766
+
767
+ break if @i.buffer.queue.size == 0
768
+ end
769
+
770
+ logs = @i.log.out.logs
771
+ assert{ logs.any?{|l| l.include?("[error]: failed to flush the buffer, and hit limit for retries. dropping all chunks in the buffer queue.") } }
772
+
773
+ assert{ now >= first_failure + 60 }
774
+ end
775
+ end
776
+ end