fluentd 1.2.0.pre1 → 1.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +28 -0
- data/MAINTAINERS.md +7 -6
- data/example/counter.conf +18 -0
- data/example/secondary_file.conf +3 -2
- data/lib/fluent/counter.rb +23 -0
- data/lib/fluent/counter/base_socket.rb +46 -0
- data/lib/fluent/counter/client.rb +288 -0
- data/lib/fluent/counter/error.rb +65 -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/env.rb +1 -0
- data/lib/fluent/log.rb +7 -0
- data/lib/fluent/plugin/filter_grep.rb +20 -24
- data/lib/fluent/plugin/output.rb +50 -1
- data/lib/fluent/plugin_helper.rb +1 -0
- data/lib/fluent/plugin_helper/counter.rb +51 -0
- data/lib/fluent/plugin_helper/retry_state.rb +15 -7
- data/lib/fluent/plugin_helper/server.rb +3 -0
- data/lib/fluent/supervisor.rb +30 -5
- data/lib/fluent/system_config.rb +26 -2
- data/lib/fluent/version.rb +1 -1
- data/test/counter/test_client.rb +549 -0
- data/test/counter/test_error.rb +44 -0
- data/test/counter/test_mutex_hash.rb +179 -0
- data/test/counter/test_server.rb +583 -0
- data/test/counter/test_store.rb +252 -0
- data/test/counter/test_validator.rb +137 -0
- data/test/plugin/test_output_as_buffered_backup.rb +271 -0
- data/test/plugin_helper/test_retry_state.rb +20 -0
- data/test/test_supervisor.rb +20 -0
- metadata +29 -5
@@ -0,0 +1,44 @@
|
|
1
|
+
require_relative '../helper'
|
2
|
+
require 'fluent/counter/error'
|
3
|
+
|
4
|
+
class CounterErrorTest < ::Test::Unit::TestCase
|
5
|
+
setup do
|
6
|
+
@message = 'error message'
|
7
|
+
end
|
8
|
+
|
9
|
+
test 'invalid_params' do
|
10
|
+
error = Fluent::Counter::InvalidParams.new(@message)
|
11
|
+
expected = { 'code' => 'invalid_params', 'message' => @message }
|
12
|
+
assert_equal expected, error.to_hash
|
13
|
+
end
|
14
|
+
|
15
|
+
test 'unknown_key' do
|
16
|
+
error = Fluent::Counter::UnknownKey.new(@message)
|
17
|
+
expected = { 'code' => 'unknown_key', 'message' => @message }
|
18
|
+
assert_equal expected, error.to_hash
|
19
|
+
end
|
20
|
+
|
21
|
+
test 'parse_error' do
|
22
|
+
error = Fluent::Counter::ParseError.new(@message)
|
23
|
+
expected = { 'code' => 'parse_error', 'message' => @message }
|
24
|
+
assert_equal expected, error.to_hash
|
25
|
+
end
|
26
|
+
|
27
|
+
test 'method_not_found' do
|
28
|
+
error = Fluent::Counter::MethodNotFound.new(@message)
|
29
|
+
expected = { 'code' => 'method_not_found', 'message' => @message }
|
30
|
+
assert_equal expected, error.to_hash
|
31
|
+
end
|
32
|
+
|
33
|
+
test 'invalid_request' do
|
34
|
+
error = Fluent::Counter::InvalidRequest.new(@message)
|
35
|
+
expected = { 'code' => 'invalid_request', 'message' => @message }
|
36
|
+
assert_equal expected, error.to_hash
|
37
|
+
end
|
38
|
+
|
39
|
+
test 'internal_server_error' do
|
40
|
+
error = Fluent::Counter::InternalServerError.new(@message)
|
41
|
+
expected = { 'code' => 'internal_server_error', 'message' => @message }
|
42
|
+
assert_equal expected, error.to_hash
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
require_relative '../helper'
|
2
|
+
require 'fluent/counter/mutex_hash'
|
3
|
+
require 'fluent/counter/store'
|
4
|
+
require 'flexmock/test_unit'
|
5
|
+
require 'timecop'
|
6
|
+
|
7
|
+
class MutexHashTest < ::Test::Unit::TestCase
|
8
|
+
setup do
|
9
|
+
@store = {}
|
10
|
+
@value = 'sample value'
|
11
|
+
@counter_store_mutex = Fluent::Counter::MutexHash.new(@store)
|
12
|
+
end
|
13
|
+
|
14
|
+
sub_test_case 'synchronize' do
|
15
|
+
test "create new mutex values if keys don't exist" do
|
16
|
+
keys = ['key', 'key1']
|
17
|
+
|
18
|
+
@counter_store_mutex.synchronize(*keys) do |store, k|
|
19
|
+
store[k] = @value
|
20
|
+
end
|
21
|
+
|
22
|
+
mhash = @counter_store_mutex.instance_variable_get(:@mutex_hash)
|
23
|
+
keys.each do |key|
|
24
|
+
assert_true mhash[key].is_a?(Mutex)
|
25
|
+
assert_equal @value, @store[key]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
test 'nothing to do when an empty array passed' do
|
30
|
+
@counter_store_mutex.synchronize(*[]) do |store, k|
|
31
|
+
store[k] = @value
|
32
|
+
end
|
33
|
+
|
34
|
+
mhash = @counter_store_mutex.instance_variable_get(:@mutex_hash)
|
35
|
+
assert_true mhash.empty?
|
36
|
+
assert_true @store.empty?
|
37
|
+
end
|
38
|
+
|
39
|
+
test 'use a one mutex value when the same key specified' do
|
40
|
+
key = 'key'
|
41
|
+
@counter_store_mutex.synchronize(key) do |store, k|
|
42
|
+
store[k] = @value
|
43
|
+
end
|
44
|
+
|
45
|
+
mhash = @counter_store_mutex.instance_variable_get(:@mutex_hash)
|
46
|
+
m = mhash[key]
|
47
|
+
assert_true m.is_a?(Mutex)
|
48
|
+
assert_equal @value, @store[key]
|
49
|
+
|
50
|
+
# access the same key once again
|
51
|
+
value2 = 'test value2'
|
52
|
+
@counter_store_mutex.synchronize(key) do |store, k|
|
53
|
+
store[k] = value2
|
54
|
+
end
|
55
|
+
|
56
|
+
mhash = @counter_store_mutex.instance_variable_get(:@mutex_hash)
|
57
|
+
m2 = mhash[key]
|
58
|
+
assert_true m2.is_a?(Mutex)
|
59
|
+
assert_equal value2, @store[key]
|
60
|
+
|
61
|
+
assert_equal m, m2
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
sub_test_case 'synchronize_key' do
|
66
|
+
test "create new mutex values if keys don't exist" do
|
67
|
+
keys = ['key', 'key1']
|
68
|
+
|
69
|
+
@counter_store_mutex.synchronize_keys(*keys) do |store, k|
|
70
|
+
store[k] = @value
|
71
|
+
end
|
72
|
+
|
73
|
+
mhash = @counter_store_mutex.instance_variable_get(:@mutex_hash)
|
74
|
+
keys.each do |key|
|
75
|
+
assert_true mhash[key].is_a?(Mutex)
|
76
|
+
assert_equal @value, @store[key]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
test 'nothing to do when an empty array passed' do
|
81
|
+
@counter_store_mutex.synchronize_keys(*[]) do |store, k|
|
82
|
+
store[k] = @value
|
83
|
+
end
|
84
|
+
|
85
|
+
mhash = @counter_store_mutex.instance_variable_get(:@mutex_hash)
|
86
|
+
assert_true mhash.empty?
|
87
|
+
assert_true @store.empty?
|
88
|
+
end
|
89
|
+
|
90
|
+
test 'use a one mutex value when the same key specified' do
|
91
|
+
key = 'key'
|
92
|
+
@counter_store_mutex.synchronize_keys(key) do |store, k|
|
93
|
+
store[k] = @value
|
94
|
+
end
|
95
|
+
|
96
|
+
mhash = @counter_store_mutex.instance_variable_get(:@mutex_hash)
|
97
|
+
m = mhash[key]
|
98
|
+
assert_true m.is_a?(Mutex)
|
99
|
+
assert_equal @value, @store[key]
|
100
|
+
|
101
|
+
# access the same key once again
|
102
|
+
value2 = 'test value2'
|
103
|
+
@counter_store_mutex.synchronize_keys(key) do |store, k|
|
104
|
+
store[k] = value2
|
105
|
+
end
|
106
|
+
|
107
|
+
mhash = @counter_store_mutex.instance_variable_get(:@mutex_hash)
|
108
|
+
m2 = mhash[key]
|
109
|
+
assert_true m2.is_a?(Mutex)
|
110
|
+
assert_equal value2, @store[key]
|
111
|
+
|
112
|
+
assert_equal m, m2
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
class CleanupThreadTest < ::Test::Unit::TestCase
|
118
|
+
StoreValue = Struct.new(:last_modified_at)
|
119
|
+
|
120
|
+
setup do
|
121
|
+
# timecop isn't compatible with EventTime
|
122
|
+
t = Time.parse('2016-09-22 16:59:59 +0900')
|
123
|
+
Timecop.freeze(t)
|
124
|
+
|
125
|
+
@store = Fluent::Counter::Store.new
|
126
|
+
@mhash = Fluent::Counter::MutexHash.new(@store)
|
127
|
+
|
128
|
+
# stub sleep method to avoid waiting CLEANUP_INTERVAL
|
129
|
+
ct = @mhash.instance_variable_get(:@cleanup_thread)
|
130
|
+
flexstub(ct).should_receive(:sleep)
|
131
|
+
end
|
132
|
+
|
133
|
+
teardown do
|
134
|
+
@mhash.stop
|
135
|
+
Timecop.return
|
136
|
+
end
|
137
|
+
|
138
|
+
test 'clean up unused mutex' do
|
139
|
+
name = 'key1'
|
140
|
+
init_obj = { 'name' => name, 'reset_interval' => 2 }
|
141
|
+
|
142
|
+
@mhash.synchronize(init_obj['name']) do
|
143
|
+
@store.init(name, init_obj)
|
144
|
+
end
|
145
|
+
|
146
|
+
ct = @mhash.instance_variable_get(:@mutex_hash)
|
147
|
+
assert ct[name]
|
148
|
+
|
149
|
+
Timecop.travel(15 * 60 + 1) # 15 min
|
150
|
+
|
151
|
+
@mhash.start # start cleanup
|
152
|
+
sleep 1
|
153
|
+
|
154
|
+
ct = @mhash.instance_variable_get(:@mutex_hash)
|
155
|
+
assert_empty ct
|
156
|
+
|
157
|
+
@mhash.stop
|
158
|
+
end
|
159
|
+
|
160
|
+
test "don't remove when `last_modified_at` is greater than (Time.now - CLEANUP_INTERVAL)" do
|
161
|
+
name = 'key1'
|
162
|
+
init_obj = { 'name' => name, 'reset_interval' => 2 }
|
163
|
+
|
164
|
+
@mhash.synchronize(init_obj['name']) do
|
165
|
+
@store.init(name, init_obj)
|
166
|
+
end
|
167
|
+
|
168
|
+
ct = @mhash.instance_variable_get(:@mutex_hash)
|
169
|
+
assert ct[name]
|
170
|
+
|
171
|
+
@mhash.start # start cleanup
|
172
|
+
sleep 1
|
173
|
+
|
174
|
+
ct = @mhash.instance_variable_get(:@mutex_hash)
|
175
|
+
assert ct[name]
|
176
|
+
|
177
|
+
@mhash.stop
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,583 @@
|
|
1
|
+
require_relative '../helper'
|
2
|
+
require 'fluent/counter/server'
|
3
|
+
require 'fluent/counter/store'
|
4
|
+
require 'fluent/time'
|
5
|
+
require 'flexmock/test_unit'
|
6
|
+
require 'timecop'
|
7
|
+
|
8
|
+
class CounterServerTest < ::Test::Unit::TestCase
|
9
|
+
setup do
|
10
|
+
# timecop isn't compatible with EventTime
|
11
|
+
t = Time.parse('2016-09-22 16:59:59 +0900')
|
12
|
+
Timecop.freeze(t)
|
13
|
+
@now = Fluent::EventTime.now
|
14
|
+
|
15
|
+
@scope = "server\tworker\tplugin"
|
16
|
+
@server_name = 'server1'
|
17
|
+
@server = Fluent::Counter::Server.new(@server_name, opt: { log: $log })
|
18
|
+
@server.instance_eval { @server.close }
|
19
|
+
end
|
20
|
+
|
21
|
+
teardown do
|
22
|
+
Timecop.return
|
23
|
+
end
|
24
|
+
|
25
|
+
def extract_value_from_counter(counter, scope, name)
|
26
|
+
store = counter.instance_variable_get(:@store).instance_variable_get(:@storage).instance_variable_get(:@store)
|
27
|
+
key = Fluent::Counter::Store.gen_key(scope, name)
|
28
|
+
store[key]
|
29
|
+
end
|
30
|
+
|
31
|
+
test 'raise an error when server name is invalid' do
|
32
|
+
assert_raise do
|
33
|
+
Fluent::Counter::Server.new("\tinvalid_name")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
sub_test_case 'on_message' do
|
38
|
+
data(
|
39
|
+
establish: 'establish',
|
40
|
+
init: 'init',
|
41
|
+
delete: 'delete',
|
42
|
+
inc: 'inc',
|
43
|
+
get: 'get',
|
44
|
+
reset: 'reset',
|
45
|
+
)
|
46
|
+
test 'call valid methods' do |method|
|
47
|
+
stub(@server).send do |_m, params, scope, options|
|
48
|
+
{ 'data' => [params, scope, options] }
|
49
|
+
end
|
50
|
+
|
51
|
+
request = { 'id' => 0, 'method' => method }
|
52
|
+
expected = { 'id' => 0, 'data' => [nil, nil, nil] }
|
53
|
+
assert_equal expected, @server.on_message(request)
|
54
|
+
end
|
55
|
+
|
56
|
+
data(
|
57
|
+
missing_id: [
|
58
|
+
{ 'method' => 'init' },
|
59
|
+
{ 'code' => 'invalid_request', 'message' => 'Request should include `id`' }
|
60
|
+
],
|
61
|
+
missing_method: [
|
62
|
+
{ 'id' => 0 },
|
63
|
+
{ 'code' => 'invalid_request', 'message' => 'Request should include `method`' }
|
64
|
+
],
|
65
|
+
invalid_method: [
|
66
|
+
{ 'id' => 0, 'method' => 'invalid_method' },
|
67
|
+
{ 'code' => 'method_not_found', 'message' => 'Unknown method name passed: invalid_method' }
|
68
|
+
]
|
69
|
+
)
|
70
|
+
test 'invalid request' do |(request, error)|
|
71
|
+
expected = {
|
72
|
+
'id' => request['id'],
|
73
|
+
'data' => [],
|
74
|
+
'errors' => [error],
|
75
|
+
}
|
76
|
+
|
77
|
+
assert_equal expected, @server.on_message(request)
|
78
|
+
end
|
79
|
+
|
80
|
+
test 'return an `internal_server_error` error object when an error raises in safe_run' do
|
81
|
+
stub(@server).send do |_m, _params, _scope, _options|
|
82
|
+
raise 'Error in safe_run'
|
83
|
+
end
|
84
|
+
|
85
|
+
request = { 'id' => 0, 'method' => 'init' }
|
86
|
+
expected = {
|
87
|
+
'id' => request['id'],
|
88
|
+
'data' => [],
|
89
|
+
'errors' => [
|
90
|
+
{ 'code' => 'internal_server_error', 'message' => 'Error in safe_run' }
|
91
|
+
]
|
92
|
+
}
|
93
|
+
assert_equal expected, @server.on_message(request)
|
94
|
+
end
|
95
|
+
|
96
|
+
test 'output an error log when passed data is not Hash' do
|
97
|
+
data = 'this is not a hash'
|
98
|
+
mock($log).error("Received data is not Hash: #{data}")
|
99
|
+
@server.on_message(data)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
sub_test_case 'establish' do
|
104
|
+
test 'establish a scope in a counter' do
|
105
|
+
result = @server.send('establish', ['key'], nil, nil)
|
106
|
+
expected = { 'data' => ["#{@server_name}\tkey"] }
|
107
|
+
assert_equal expected, result
|
108
|
+
end
|
109
|
+
|
110
|
+
data(
|
111
|
+
empty: [[], 'One or more `params` are required'],
|
112
|
+
empty_key: [[''], '`scope` is the invalid format'],
|
113
|
+
invalid_key: [['_key'], '`scope` is the invalid format'],
|
114
|
+
)
|
115
|
+
test 'raise an error: invalid_params' do |(params, msg)|
|
116
|
+
result = @server.send('establish', params, nil, nil)
|
117
|
+
expected = {
|
118
|
+
'data' => [],
|
119
|
+
'errors' => [{ 'code' => 'invalid_params', 'message' => msg }]
|
120
|
+
}
|
121
|
+
assert_equal expected, result
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
sub_test_case 'init' do
|
126
|
+
setup do
|
127
|
+
@name = 'key1'
|
128
|
+
@key = Fluent::Counter::Store.gen_key(@scope, @name)
|
129
|
+
end
|
130
|
+
|
131
|
+
test 'create new value in a counter' do
|
132
|
+
assert_nil extract_value_from_counter(@server, @scope, @name)
|
133
|
+
result = @server.send('init', [{ 'name' => @name, 'reset_interval' => 1 }], @scope, {})
|
134
|
+
assert_nil result['errors']
|
135
|
+
|
136
|
+
counter = result['data'].first
|
137
|
+
assert_equal 'numeric', counter['type']
|
138
|
+
assert_equal @name, counter['name']
|
139
|
+
assert_equal 0, counter['current']
|
140
|
+
assert_equal 0, counter['total']
|
141
|
+
assert_equal @now, counter['last_reset_at']
|
142
|
+
assert extract_value_from_counter(@server, @scope, @name)
|
143
|
+
end
|
144
|
+
|
145
|
+
data(
|
146
|
+
numeric: 'numeric',
|
147
|
+
integer: 'integer',
|
148
|
+
float: 'float'
|
149
|
+
)
|
150
|
+
test 'set the type of a counter value' do |type|
|
151
|
+
result = @server.send('init', [{ 'name' => @name, 'reset_interval' => 1, 'type' => type }], @scope, {})
|
152
|
+
counter = result['data'].first
|
153
|
+
assert_equal type, counter['type']
|
154
|
+
|
155
|
+
v = extract_value_from_counter(@server, @scope, @name)
|
156
|
+
assert_equal type, v['type']
|
157
|
+
end
|
158
|
+
|
159
|
+
data(
|
160
|
+
empty: [[], 'One or more `params` are required'],
|
161
|
+
missing_name: [
|
162
|
+
[{ 'rest_interval' => 20 }],
|
163
|
+
'`name` is required'
|
164
|
+
],
|
165
|
+
invalid_name: [
|
166
|
+
[{ 'name' => '_test', 'reset_interval' => 20 }],
|
167
|
+
'`name` is the invalid format'
|
168
|
+
],
|
169
|
+
missing_interval: [
|
170
|
+
[{ 'name' => 'test' }],
|
171
|
+
'`reset_interval` is required'
|
172
|
+
],
|
173
|
+
minus_interval: [
|
174
|
+
[{ 'name' => 'test', 'reset_interval' => -1 }],
|
175
|
+
'`reset_interval` should be a positive number'
|
176
|
+
],
|
177
|
+
invalid_type: [
|
178
|
+
[{ 'name' => 'test', 'reset_interval' => 1, 'type' => 'invalid_type' }],
|
179
|
+
'`type` should be integer, float, or numeric'
|
180
|
+
]
|
181
|
+
)
|
182
|
+
test 'return an error object: invalid_params' do |(params, msg)|
|
183
|
+
result = @server.send('init', params, @scope, {})
|
184
|
+
assert_empty result['data']
|
185
|
+
error = result['errors'].first
|
186
|
+
assert_equal 'invalid_params', error['code']
|
187
|
+
assert_equal msg, error['message']
|
188
|
+
end
|
189
|
+
|
190
|
+
test 'return error objects when passed key already exists' do
|
191
|
+
@server.send('init', [{ 'name' => @name, 'reset_interval' => 1 }], @scope, {})
|
192
|
+
|
193
|
+
# call `init` to same key twice
|
194
|
+
result = @server.send('init', [{ 'name' => @name, 'reset_interval' => 1 }], @scope, {})
|
195
|
+
assert_empty result['data']
|
196
|
+
error = result['errors'].first
|
197
|
+
|
198
|
+
expected = { 'code' => 'invalid_params', 'message' => "#{@key} already exists in counter" }
|
199
|
+
assert_equal expected, error
|
200
|
+
end
|
201
|
+
|
202
|
+
test 'return existing value when passed key already exists and ignore option is true' do
|
203
|
+
v1 = @server.send('init', [{ 'name' => @name, 'reset_interval' => 1 }], @scope, {})
|
204
|
+
|
205
|
+
# call `init` to same key twice
|
206
|
+
v2 = @server.send('init', [{ 'name' => @name, 'reset_interval' => 1 }], @scope, { 'ignore' => true })
|
207
|
+
assert_nil v2['errors']
|
208
|
+
assert_equal v1['data'], v2['data']
|
209
|
+
end
|
210
|
+
|
211
|
+
test 'call `synchronize_keys` when random option is true' do
|
212
|
+
mhash = @server.instance_variable_get(:@mutex_hash)
|
213
|
+
mock(mhash).synchronize(@key).once
|
214
|
+
@server.send('init', [{ 'name' => @name, 'reset_interval' => 1 }], @scope, {})
|
215
|
+
|
216
|
+
mhash = @server.instance_variable_get(:@mutex_hash)
|
217
|
+
mock(mhash).synchronize_keys(@key).once
|
218
|
+
@server.send('init', [{ 'name' => @name, 'reset_interval' => 1 }], @scope, { 'random' => true })
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
sub_test_case 'delete' do
|
223
|
+
setup do
|
224
|
+
@name = 'key1'
|
225
|
+
@key = Fluent::Counter::Store.gen_key(@scope, @name)
|
226
|
+
@server.send('init', [{ 'name' => @name, 'reset_interval' => 20 }], @scope, {})
|
227
|
+
end
|
228
|
+
|
229
|
+
test 'delete a value from a counter' do
|
230
|
+
assert extract_value_from_counter(@server, @scope, @name)
|
231
|
+
|
232
|
+
result = @server.send('delete', [@name], @scope, {})
|
233
|
+
assert_nil result['errors']
|
234
|
+
|
235
|
+
counter = result['data'].first
|
236
|
+
assert_equal 0, counter['current']
|
237
|
+
assert_equal 0, counter['total']
|
238
|
+
assert_equal 'numeric', counter['type']
|
239
|
+
assert_equal @name, counter['name']
|
240
|
+
assert_equal @now, counter['last_reset_at']
|
241
|
+
|
242
|
+
assert_nil extract_value_from_counter(@server, @scope, @name)
|
243
|
+
end
|
244
|
+
|
245
|
+
data(
|
246
|
+
empty: [[], 'One or more `params` are required'],
|
247
|
+
empty_key: [[''], '`key` is the invalid format'],
|
248
|
+
invalid_key: [['_key'], '`key` is the invalid format'],
|
249
|
+
)
|
250
|
+
test 'return an error object: invalid_params' do |(params, msg)|
|
251
|
+
result = @server.send('delete', params, @scope, {})
|
252
|
+
|
253
|
+
assert_empty result['data']
|
254
|
+
error = result['errors'].first
|
255
|
+
assert_equal 'invalid_params', error['code']
|
256
|
+
assert_equal msg, error['message']
|
257
|
+
end
|
258
|
+
|
259
|
+
test 'return an error object: unknown_key' do
|
260
|
+
unknown_key = 'unknown_key'
|
261
|
+
result = @server.send('delete', [unknown_key], @scope, {})
|
262
|
+
|
263
|
+
assert_empty result['data']
|
264
|
+
error = result['errors'].first
|
265
|
+
assert_equal unknown_key, error['code']
|
266
|
+
assert_equal "`#{@scope}\t#{unknown_key}` doesn't exist in counter", error['message']
|
267
|
+
end
|
268
|
+
|
269
|
+
test 'call `synchronize_keys` when random option is true' do
|
270
|
+
mhash = @server.instance_variable_get(:@mutex_hash)
|
271
|
+
mock(mhash).synchronize(@key).once
|
272
|
+
@server.send('delete', [@name], @scope, {})
|
273
|
+
|
274
|
+
mhash = @server.instance_variable_get(:@mutex_hash)
|
275
|
+
mock(mhash).synchronize_keys(@key).once
|
276
|
+
@server.send('delete', [@name], @scope, { 'random' => true })
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
sub_test_case 'inc' do
|
281
|
+
setup do
|
282
|
+
@name1 = 'key1'
|
283
|
+
@name2 = 'key2'
|
284
|
+
@key1 = Fluent::Counter::Store.gen_key(@scope, @name1)
|
285
|
+
inc_objects = [
|
286
|
+
{ 'name' => @name1, 'reset_interval' => 20 },
|
287
|
+
{ 'name' => @name2, 'type' => 'integer', 'reset_interval' => 20 }
|
288
|
+
]
|
289
|
+
@server.send('init', inc_objects, @scope, {})
|
290
|
+
end
|
291
|
+
|
292
|
+
test 'increment or decrement a value in counter' do
|
293
|
+
result = @server.send('inc', [{ 'name' => @name1, 'value' => 10 }], @scope, {})
|
294
|
+
assert_nil result['errors']
|
295
|
+
|
296
|
+
counter = result['data'].first
|
297
|
+
assert_equal 10, counter['current']
|
298
|
+
assert_equal 10, counter['total']
|
299
|
+
assert_equal 'numeric', counter['type']
|
300
|
+
assert_equal @name1, counter['name']
|
301
|
+
assert_equal @now, counter['last_reset_at']
|
302
|
+
|
303
|
+
c = extract_value_from_counter(@server, @scope, @name1)
|
304
|
+
assert_equal 10, c['current']
|
305
|
+
assert_equal 10, c['total']
|
306
|
+
assert_equal @now, Fluent::EventTime.new(*c['last_reset_at'])
|
307
|
+
assert_equal @now, Fluent::EventTime.new(*c['last_modified_at'])
|
308
|
+
end
|
309
|
+
|
310
|
+
test 'create new value and increment/decrement its value when `force` option is true' do
|
311
|
+
new_name = 'new_key'
|
312
|
+
assert_nil extract_value_from_counter(@server, @scope, new_name)
|
313
|
+
|
314
|
+
v1 = @server.send('inc', [{ 'name' => new_name, 'value' => 10 }], @scope, {})
|
315
|
+
assert_empty v1['data']
|
316
|
+
error = v1['errors'].first
|
317
|
+
assert_equal 'unknown_key', error['code']
|
318
|
+
|
319
|
+
assert_nil extract_value_from_counter(@server, @scope, new_name)
|
320
|
+
|
321
|
+
v2 = @server.send(
|
322
|
+
'inc',
|
323
|
+
[{ 'name' => new_name, 'value' => 10, 'reset_interval' => 20 }],
|
324
|
+
@scope,
|
325
|
+
{ 'force' => true }
|
326
|
+
)
|
327
|
+
|
328
|
+
assert_nil v2['errors']
|
329
|
+
|
330
|
+
counter = v2['data'].first
|
331
|
+
assert_equal 10, counter['current']
|
332
|
+
assert_equal 10, counter['total']
|
333
|
+
assert_equal 'numeric', counter['type']
|
334
|
+
assert_equal new_name, counter['name']
|
335
|
+
assert_equal @now, counter['last_reset_at']
|
336
|
+
|
337
|
+
assert extract_value_from_counter(@server, @scope, new_name)
|
338
|
+
end
|
339
|
+
|
340
|
+
data(
|
341
|
+
empty: [[], 'One or more `params` are required', {}],
|
342
|
+
missing_name: [
|
343
|
+
[{ 'value' => 10 }],
|
344
|
+
'`name` is required', {}
|
345
|
+
],
|
346
|
+
missing_value: [
|
347
|
+
[{ 'name' => 'key1' }],
|
348
|
+
'`value` is required', {}
|
349
|
+
],
|
350
|
+
invalid_type: [
|
351
|
+
[{ 'name' => 'key2', 'value' => 10.0 }],
|
352
|
+
'`type` is integer. You should pass integer value as a `value`', {}
|
353
|
+
],
|
354
|
+
missing_reset_interval: [
|
355
|
+
[{ 'name' => 'key1', 'value' => 1 }],
|
356
|
+
'`reset_interval` is required',
|
357
|
+
{ 'force' => true }
|
358
|
+
]
|
359
|
+
)
|
360
|
+
test 'return an error object: invalid_params' do |(params, msg, opt)|
|
361
|
+
result = @server.send('inc', params, @scope, opt)
|
362
|
+
assert_empty result['data']
|
363
|
+
|
364
|
+
error = result['errors'].first
|
365
|
+
assert_equal 'invalid_params', error['code']
|
366
|
+
assert_equal msg, error['message']
|
367
|
+
end
|
368
|
+
|
369
|
+
test 'call `synchronize_keys` when random option is true' do
|
370
|
+
mhash = @server.instance_variable_get(:@mutex_hash)
|
371
|
+
mock(mhash).synchronize(@key1).once
|
372
|
+
params = [{ 'name' => @name1, 'value' => 1 }]
|
373
|
+
@server.send('inc', params, @scope, {})
|
374
|
+
|
375
|
+
mhash = @server.instance_variable_get(:@mutex_hash)
|
376
|
+
mock(mhash).synchronize_keys(@key1).once
|
377
|
+
@server.send('inc', params, @scope, { 'random' => true })
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
sub_test_case 'reset' do
|
382
|
+
setup do
|
383
|
+
@name = 'key'
|
384
|
+
@travel_sec = 10
|
385
|
+
@server.send('init', [{ 'name' => @name, 'reset_interval' => 10 }], @scope, {})
|
386
|
+
@server.send('inc', [{ 'name' => @name, 'value' => 10 }], @scope, {})
|
387
|
+
end
|
388
|
+
|
389
|
+
test 'reset a value in the counter' do
|
390
|
+
Timecop.travel(@travel_sec)
|
391
|
+
|
392
|
+
result = @server.send('reset', [@name], @scope, {})
|
393
|
+
assert_nil result['errors']
|
394
|
+
|
395
|
+
data = result['data'].first
|
396
|
+
assert_true data['success']
|
397
|
+
assert_equal @travel_sec, data['elapsed_time']
|
398
|
+
|
399
|
+
counter = data['counter_data']
|
400
|
+
assert_equal 10, counter['current']
|
401
|
+
assert_equal 10, counter['total']
|
402
|
+
assert_equal 'numeric', counter['type']
|
403
|
+
assert_equal @name, counter['name']
|
404
|
+
assert_equal @now, counter['last_reset_at']
|
405
|
+
|
406
|
+
v = extract_value_from_counter(@server, @scope, @name)
|
407
|
+
assert_equal 0, v['current']
|
408
|
+
assert_equal 10, v['total']
|
409
|
+
assert_equal (@now + @travel_sec), Fluent::EventTime.new(*v['last_reset_at'])
|
410
|
+
assert_equal (@now + @travel_sec), Fluent::EventTime.new(*v['last_modified_at'])
|
411
|
+
end
|
412
|
+
|
413
|
+
test 'reset a value after `reset_interval` passed' do
|
414
|
+
first_travel_sec = 5
|
415
|
+
Timecop.travel(first_travel_sec) # jump time less than reset_interval
|
416
|
+
result = @server.send('reset', [@name], @scope, {})
|
417
|
+
v = result['data'].first
|
418
|
+
|
419
|
+
assert_equal false, v['success']
|
420
|
+
assert_equal first_travel_sec, v['elapsed_time']
|
421
|
+
|
422
|
+
store = extract_value_from_counter(@server, @scope, @name)
|
423
|
+
assert_equal 10, store['current']
|
424
|
+
assert_equal @now, Fluent::EventTime.new(*store['last_reset_at'])
|
425
|
+
|
426
|
+
# time is passed greater than reset_interval
|
427
|
+
Timecop.travel(@travel_sec)
|
428
|
+
result = @server.send('reset', [@name], @scope, {})
|
429
|
+
v = result['data'].first
|
430
|
+
|
431
|
+
assert_true v['success']
|
432
|
+
assert_equal @travel_sec + first_travel_sec, v['elapsed_time']
|
433
|
+
|
434
|
+
v1 = extract_value_from_counter(@server, @scope, @name)
|
435
|
+
assert_equal 0, v1['current']
|
436
|
+
assert_equal (@now + @travel_sec + first_travel_sec), Fluent::EventTime.new(*v1['last_reset_at'])
|
437
|
+
assert_equal (@now + @travel_sec + first_travel_sec), Fluent::EventTime.new(*v1['last_modified_at'])
|
438
|
+
end
|
439
|
+
|
440
|
+
data(
|
441
|
+
empty: [[], 'One or more `params` are required'],
|
442
|
+
empty_key: [[''], '`key` is the invalid format'],
|
443
|
+
invalid_key: [['_key'], '`key` is the invalid format'],
|
444
|
+
)
|
445
|
+
test 'return an error object: invalid_params' do |(params, msg)|
|
446
|
+
result = @server.send('reset', params, @scope, {})
|
447
|
+
assert_empty result['data']
|
448
|
+
assert_equal 'invalid_params', result['errors'].first['code']
|
449
|
+
assert_equal msg, result['errors'].first['message']
|
450
|
+
end
|
451
|
+
|
452
|
+
test 'return an error object: unknown_key' do
|
453
|
+
unknown_key = 'unknown_key'
|
454
|
+
result = @server.send('reset', [unknown_key], @scope, {})
|
455
|
+
|
456
|
+
assert_empty result['data']
|
457
|
+
error = result['errors'].first
|
458
|
+
assert_equal unknown_key, error['code']
|
459
|
+
assert_equal "`#{@scope}\t#{unknown_key}` doesn't exist in counter", error['message']
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
sub_test_case 'get' do
|
464
|
+
setup do
|
465
|
+
@name1 = 'key1'
|
466
|
+
@name2 = 'key2'
|
467
|
+
init_objects = [
|
468
|
+
{ 'name' => @name1, 'reset_interval' => 0 },
|
469
|
+
{ 'name' => @name2, 'reset_interval' => 0 },
|
470
|
+
]
|
471
|
+
@server.send('init', init_objects, @scope, {})
|
472
|
+
end
|
473
|
+
|
474
|
+
test 'get a counter value' do
|
475
|
+
key = @name1
|
476
|
+
result = @server.send('get', [key], @scope, {})
|
477
|
+
assert_nil result['errors']
|
478
|
+
|
479
|
+
counter = result['data'].first
|
480
|
+
assert_equal 0, counter['current']
|
481
|
+
assert_equal 0, counter['total']
|
482
|
+
assert_equal 'numeric', counter['type']
|
483
|
+
assert_equal key, counter['name']
|
484
|
+
end
|
485
|
+
|
486
|
+
test 'get counter values' do
|
487
|
+
result = @server.send('get', [@name1, @name2], @scope, {})
|
488
|
+
assert_nil result['errors']
|
489
|
+
|
490
|
+
counter1 = result['data'][0]
|
491
|
+
assert_equal 0, counter1['current']
|
492
|
+
assert_equal 0, counter1['total']
|
493
|
+
assert_equal 'numeric', counter1['type']
|
494
|
+
assert_equal @name1, counter1['name']
|
495
|
+
|
496
|
+
counter2 = result['data'][1]
|
497
|
+
assert_equal 0, counter2['current']
|
498
|
+
assert_equal 0, counter2['total']
|
499
|
+
assert_equal 'numeric', counter2['type']
|
500
|
+
assert_equal @name2, counter2['name']
|
501
|
+
end
|
502
|
+
|
503
|
+
data(
|
504
|
+
empty: [[], 'One or more `params` are required'],
|
505
|
+
empty_key: [[''], '`key` is the invalid format'],
|
506
|
+
invalid_key: [['_key'], '`key` is the invalid format'],
|
507
|
+
)
|
508
|
+
test 'return an error object: invalid_params' do |(params, msg)|
|
509
|
+
result = @server.send('get', params, @scope, {})
|
510
|
+
assert_empty result['data']
|
511
|
+
assert_equal 'invalid_params', result['errors'].first['code']
|
512
|
+
assert_equal msg, result['errors'].first['message']
|
513
|
+
end
|
514
|
+
|
515
|
+
test 'return an error object: unknown_key' do
|
516
|
+
unknown_key = 'unknown_key'
|
517
|
+
result = @server.send('get', [unknown_key], @scope, {})
|
518
|
+
|
519
|
+
assert_empty result['data']
|
520
|
+
error = result['errors'].first
|
521
|
+
assert_equal unknown_key, error['code']
|
522
|
+
assert_equal "`#{@scope}\t#{unknown_key}` doesn't exist in counter", error['message']
|
523
|
+
end
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
class CounterCounterResponseTest < ::Test::Unit::TestCase
|
528
|
+
setup do
|
529
|
+
@response = Fluent::Counter::Server::Response.new
|
530
|
+
@errors = [
|
531
|
+
StandardError.new('standard error'),
|
532
|
+
Fluent::Counter::InternalServerError.new('internal server error')
|
533
|
+
]
|
534
|
+
@now = Fluent::EventTime.now
|
535
|
+
value = {
|
536
|
+
'name' => 'name',
|
537
|
+
'total' => 100,
|
538
|
+
'current' => 11,
|
539
|
+
'type' => 'numeric',
|
540
|
+
'reset_interval' => 10,
|
541
|
+
'last_reset_at' => @now,
|
542
|
+
}
|
543
|
+
@values = [value, 'test']
|
544
|
+
end
|
545
|
+
|
546
|
+
test 'push_error' do
|
547
|
+
@errors.each do |e|
|
548
|
+
@response.push_error(e)
|
549
|
+
end
|
550
|
+
|
551
|
+
v = @response.instance_variable_get(:@errors)
|
552
|
+
assert_equal @errors, v
|
553
|
+
end
|
554
|
+
|
555
|
+
test 'push_data' do
|
556
|
+
@values.each do |v|
|
557
|
+
@response.push_data v
|
558
|
+
end
|
559
|
+
|
560
|
+
data = @response.instance_variable_get(:@data)
|
561
|
+
assert_equal @values, data
|
562
|
+
end
|
563
|
+
|
564
|
+
test 'to_hash' do
|
565
|
+
@errors.each do |e|
|
566
|
+
@response.push_error(e)
|
567
|
+
end
|
568
|
+
|
569
|
+
@values.each do |v|
|
570
|
+
@response.push_data v
|
571
|
+
end
|
572
|
+
|
573
|
+
expected_errors = [
|
574
|
+
{ 'code' => 'internal_server_error', 'message' => 'standard error' },
|
575
|
+
{ 'code' => 'internal_server_error', 'message' => 'internal server error' }
|
576
|
+
]
|
577
|
+
expected_data = @values
|
578
|
+
|
579
|
+
hash = @response.to_hash
|
580
|
+
assert_equal expected_errors, hash['errors']
|
581
|
+
assert_equal expected_data, hash['data']
|
582
|
+
end
|
583
|
+
end
|