logstash-input-redis 2.0.2 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -1
- data/CONTRIBUTORS +1 -0
- data/README.md +8 -1
- data/lib/logstash/inputs/redis.rb +29 -33
- data/logstash-input-redis.gemspec +1 -1
- data/spec/inputs/redis_spec.rb +68 -9
- metadata +17 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d9928285771d37dd118f099c40c59243cb24aedf
|
4
|
+
data.tar.gz: 9fa7aeb915e98e533b06aaddc91f830b2595256c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dee7ad5a84df8293ffdb343a478caad99e1d951e2b5b344a866e25d5f19bb23128c5b63573dca91be6435142382589264f2bc9c97270454e5f7cdf853d91f1f7
|
7
|
+
data.tar.gz: 88deceeca56390a52fac036bba9191b15362ab6a3e972a85be4cf3ce7ea12c29d11f1a3ddc64fa98542cd05a4f4e4a9b0930ff8827253558aa6f9c670bd882cd
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
+
## 2.0.3
|
2
|
+
- Changed default batch size to 125. Improve batch handling code. Add travis ci build with redis integration.
|
3
|
+
|
1
4
|
## 2.0.0
|
2
|
-
- Plugins were updated to follow the new shutdown semantic, this mainly allows Logstash to instruct input plugins to terminate gracefully,
|
5
|
+
- Plugins were updated to follow the new shutdown semantic, this mainly allows Logstash to instruct input plugins to terminate gracefully,
|
3
6
|
instead of using Thread.raise on the plugins' threads. Ref: https://github.com/elastic/logstash/pull/3895
|
4
7
|
- Dependency on logstash-core update to 2.0
|
5
8
|
|
data/CONTRIBUTORS
CHANGED
@@ -14,6 +14,7 @@ Contributors:
|
|
14
14
|
* Pier-Hugues Pellerin (ph)
|
15
15
|
* Richard Pijnenburg (electrical)
|
16
16
|
* piavlo
|
17
|
+
* Guy Boertje (guyboertje)
|
17
18
|
|
18
19
|
Note: If you've sent us patches, bug reports, or otherwise contributed to
|
19
20
|
Logstash, and you aren't on the list above and want to be, please let us know
|
data/README.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# Logstash Plugin
|
2
2
|
|
3
|
+
Travis Build
|
4
|
+
[](https://travis-ci.org/logstash-plugins/logstash-input-redis)
|
5
|
+
|
6
|
+
Jenkins Build
|
7
|
+
[](http://build-eu-00.elastic.co/view/LS%20Plugins/view/LS%20Inputs/job/logstash-plugin-input-redis-unit/)
|
9
|
+
|
3
10
|
This is a plugin for [Logstash](https://github.com/elastic/logstash).
|
4
11
|
|
5
12
|
It is fully free and fully open source. The license is Apache 2.0, meaning you are pretty much free to use it however you want in whatever way.
|
@@ -83,4 +90,4 @@ Programming is not a required skill. Whatever you've seen about open source and
|
|
83
90
|
|
84
91
|
It is more important to the community that you are able to contribute.
|
85
92
|
|
86
|
-
For more information about contributing, see the [CONTRIBUTING](https://github.com/elastic/logstash/blob/master/CONTRIBUTING.md) file.
|
93
|
+
For more information about contributing, see the [CONTRIBUTING](https://github.com/elastic/logstash/blob/master/CONTRIBUTING.md) file.
|
@@ -2,6 +2,7 @@
|
|
2
2
|
require "logstash/namespace"
|
3
3
|
require "logstash/inputs/base"
|
4
4
|
require "logstash/inputs/threadable"
|
5
|
+
require 'redis'
|
5
6
|
|
6
7
|
# This input will read events from a Redis instance; it supports both Redis channels and lists.
|
7
8
|
# The list command (BLPOP) used by Logstash is supported in Redis v1.3.1+, and
|
@@ -16,7 +17,7 @@ require "logstash/inputs/threadable"
|
|
16
17
|
# newer. Anything older does not support the operations used by batching.
|
17
18
|
#
|
18
19
|
module LogStash module Inputs class Redis < LogStash::Inputs::Threadable
|
19
|
-
|
20
|
+
BATCH_EMPTY_SLEEP = 0.25
|
20
21
|
|
21
22
|
config_name "redis"
|
22
23
|
|
@@ -56,7 +57,7 @@ module LogStash module Inputs class Redis < LogStash::Inputs::Threadable
|
|
56
57
|
config :data_type, :validate => [ "list", "channel", "pattern_channel" ], :required => false
|
57
58
|
|
58
59
|
# The number of events to return from Redis using EVAL.
|
59
|
-
config :batch_count, :validate => :number, :default =>
|
60
|
+
config :batch_count, :validate => :number, :default => 125
|
60
61
|
|
61
62
|
public
|
62
63
|
# public API
|
@@ -77,7 +78,6 @@ module LogStash module Inputs class Redis < LogStash::Inputs::Threadable
|
|
77
78
|
end
|
78
79
|
|
79
80
|
def register
|
80
|
-
require 'redis'
|
81
81
|
@redis_url = "redis://#{@password}@#{@host}:#{@port}/#{@db}"
|
82
82
|
|
83
83
|
# TODO remove after setting key and data_type to true
|
@@ -112,6 +112,8 @@ module LogStash module Inputs class Redis < LogStash::Inputs::Threadable
|
|
112
112
|
@stop_method = method(:subscribe_stop)
|
113
113
|
end
|
114
114
|
|
115
|
+
@list_method = batched? ? method(:list_batch_listener) : method(:list_single_listener)
|
116
|
+
|
115
117
|
# TODO(sissel, boertje): set @identity directly when @name config option is removed.
|
116
118
|
@identity = @name != 'default' ? @name : "#{@redis_url} #{@data_type}:#{@key}"
|
117
119
|
@logger.info("Registering Redis", :identity => @identity)
|
@@ -165,21 +167,11 @@ module LogStash module Inputs class Redis < LogStash::Inputs::Threadable
|
|
165
167
|
# private
|
166
168
|
def load_batch_script(redis)
|
167
169
|
#A Redis Lua EVAL script to fetch a count of keys
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
if length < i then i = length end
|
174
|
-
while (i > 0) do
|
175
|
-
local item = redis.call("lpop", KEYS[1])
|
176
|
-
if (not item) then
|
177
|
-
break
|
178
|
-
end
|
179
|
-
table.insert(res, item)
|
180
|
-
i = i-1
|
181
|
-
end
|
182
|
-
return res
|
170
|
+
redis_script = <<EOF
|
171
|
+
local batchsize = tonumber(ARGV[1])
|
172
|
+
local result = redis.call('lrange', KEYS[1], 0, batchsize)
|
173
|
+
redis.call('ltrim', KEYS[1], batchsize + 1, -1)
|
174
|
+
return result
|
183
175
|
EOF
|
184
176
|
@redis_script_sha = redis.script(:load, redis_script)
|
185
177
|
end
|
@@ -209,7 +201,7 @@ EOF
|
|
209
201
|
while !stop?
|
210
202
|
begin
|
211
203
|
@redis ||= connect
|
212
|
-
|
204
|
+
@list_method.call(@redis, output_queue)
|
213
205
|
rescue ::Redis::BaseError => e
|
214
206
|
@logger.warn("Redis connection problem", :exception => e)
|
215
207
|
# Reset the redis variable to trigger reconnect
|
@@ -221,24 +213,17 @@ EOF
|
|
221
213
|
end
|
222
214
|
end
|
223
215
|
|
224
|
-
|
225
|
-
def list_listener(redis, output_queue)
|
226
|
-
|
227
|
-
item = redis.blpop(@key, 0, :timeout => 1)
|
228
|
-
return unless item # from timeout or other conditions
|
229
|
-
|
230
|
-
# blpop returns the 'key' read from as well as the item result
|
231
|
-
# we only care about the result (2nd item in the list).
|
232
|
-
queue_event(item.last, output_queue)
|
233
|
-
|
234
|
-
# If @batch_count is 1, there's no need to continue.
|
235
|
-
return if !batched?
|
236
|
-
|
216
|
+
def list_batch_listener(redis, output_queue)
|
237
217
|
begin
|
238
|
-
redis.evalsha(@redis_script_sha, [@key], [@batch_count-1])
|
218
|
+
results = redis.evalsha(@redis_script_sha, [@key], [@batch_count-1])
|
219
|
+
results.each do |item|
|
239
220
|
queue_event(item, output_queue)
|
240
221
|
end
|
241
222
|
|
223
|
+
if results.size.zero?
|
224
|
+
sleep BATCH_EMPTY_SLEEP
|
225
|
+
end
|
226
|
+
|
242
227
|
# Below is a commented-out implementation of 'batch fetch'
|
243
228
|
# using pipelined LPOP calls. This in practice has been observed to
|
244
229
|
# perform exactly the same in terms of event throughput as
|
@@ -253,6 +238,8 @@ EOF
|
|
253
238
|
#queue_event(item, output_queue) if item
|
254
239
|
#end
|
255
240
|
# --- End commented out implementation of 'batch fetch'
|
241
|
+
# further to the above, the LUA script now uses lrange and trim
|
242
|
+
# which should further improve the efficiency of the script
|
256
243
|
rescue ::Redis::CommandError => e
|
257
244
|
if e.to_s =~ /NOSCRIPT/ then
|
258
245
|
@logger.warn("Redis may have been restarted, reloading Redis batch EVAL script", :exception => e);
|
@@ -264,6 +251,15 @@ EOF
|
|
264
251
|
end
|
265
252
|
end
|
266
253
|
|
254
|
+
def list_single_listener(redis, output_queue)
|
255
|
+
item = redis.blpop(@key, 0, :timeout => 1)
|
256
|
+
return unless item # from timeout or other conditions
|
257
|
+
|
258
|
+
# blpop returns the 'key' read from as well as the item result
|
259
|
+
# we only care about the result (2nd item in the list).
|
260
|
+
queue_event(item.last, output_queue)
|
261
|
+
end
|
262
|
+
|
267
263
|
# private
|
268
264
|
def subscribe_stop
|
269
265
|
return if @redis.nil? || !@redis.connected?
|
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
|
3
3
|
s.name = 'logstash-input-redis'
|
4
|
-
s.version = '2.0.
|
4
|
+
s.version = '2.0.3'
|
5
5
|
s.licenses = ['Apache License (2.0)']
|
6
6
|
s.summary = "This input will read events from a Redis instance"
|
7
7
|
s.description = "This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program"
|
data/spec/inputs/redis_spec.rb
CHANGED
@@ -2,6 +2,7 @@ require "logstash/devutils/rspec/spec_helper"
|
|
2
2
|
require "redis"
|
3
3
|
require "stud/try"
|
4
4
|
require 'logstash/inputs/redis'
|
5
|
+
require 'securerandom'
|
5
6
|
|
6
7
|
def populate(key, event_count)
|
7
8
|
require "logstash/event"
|
@@ -19,17 +20,15 @@ def process(conf, event_count)
|
|
19
20
|
event_count.times.map{queue.pop}
|
20
21
|
end
|
21
22
|
|
22
|
-
events.
|
23
|
-
|
24
|
-
end
|
25
|
-
end # process
|
23
|
+
expect(events.map{|evt| evt["sequence"]}).to eq((0..event_count.pred).to_a)
|
24
|
+
end
|
26
25
|
|
27
26
|
# integration tests ---------------------
|
28
27
|
|
29
28
|
describe "inputs/redis", :redis => true do
|
30
29
|
|
31
30
|
it "should read events from a list" do
|
32
|
-
key =
|
31
|
+
key = SecureRandom.hex
|
33
32
|
event_count = 1000 + rand(50)
|
34
33
|
# event_count = 100
|
35
34
|
conf = <<-CONFIG
|
@@ -38,6 +37,7 @@ describe "inputs/redis", :redis => true do
|
|
38
37
|
type => "blah"
|
39
38
|
key => "#{key}"
|
40
39
|
data_type => "list"
|
40
|
+
batch_count => 1
|
41
41
|
}
|
42
42
|
}
|
43
43
|
CONFIG
|
@@ -46,8 +46,8 @@ describe "inputs/redis", :redis => true do
|
|
46
46
|
process(conf, event_count)
|
47
47
|
end
|
48
48
|
|
49
|
-
it "should read events from a list using batch_count" do
|
50
|
-
key =
|
49
|
+
it "should read events from a list using batch_count (default 125)" do
|
50
|
+
key = SecureRandom.hex
|
51
51
|
event_count = 1000 + rand(50)
|
52
52
|
conf = <<-CONFIG
|
53
53
|
input {
|
@@ -55,7 +55,6 @@ describe "inputs/redis", :redis => true do
|
|
55
55
|
type => "blah"
|
56
56
|
key => "#{key}"
|
57
57
|
data_type => "list"
|
58
|
-
batch_count => #{rand(20)+1}
|
59
58
|
}
|
60
59
|
}
|
61
60
|
CONFIG
|
@@ -73,7 +72,8 @@ describe LogStash::Inputs::Redis do
|
|
73
72
|
let(:connection) { double('redis_connection') }
|
74
73
|
let(:connected) { [true] }
|
75
74
|
let(:data_type) { 'list' }
|
76
|
-
let(:
|
75
|
+
let(:batch_count) { 1 }
|
76
|
+
let(:cfg) { {'key' => 'foo', 'data_type' => data_type, 'batch_count' => batch_count} }
|
77
77
|
let(:quit_calls) { [:quit] }
|
78
78
|
let(:accumulator) { [] }
|
79
79
|
|
@@ -123,6 +123,65 @@ describe LogStash::Inputs::Redis do
|
|
123
123
|
expect(accumulator.size).to be > 0
|
124
124
|
end
|
125
125
|
|
126
|
+
context "when the batch size is greater than 1" do
|
127
|
+
let(:batch_count) { 10 }
|
128
|
+
let(:rates) { [] }
|
129
|
+
|
130
|
+
before do
|
131
|
+
allow(redis).to receive(:connected?).and_return(connected.last)
|
132
|
+
allow(redis).to receive(:script)
|
133
|
+
allow(redis).to receive(:quit)
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'calling the run method, adds events to the queue' do
|
137
|
+
expect(redis).to receive(:evalsha).at_least(:once).and_return(['a', 'b'])
|
138
|
+
|
139
|
+
tt = Thread.new do
|
140
|
+
sleep 0.01
|
141
|
+
subject.do_stop
|
142
|
+
end
|
143
|
+
|
144
|
+
subject.run(accumulator)
|
145
|
+
|
146
|
+
tt.join
|
147
|
+
expect(accumulator.size).to be > 0
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
context "when there is no data" do
|
152
|
+
let(:batch_count) { 10 }
|
153
|
+
let(:rates) { [] }
|
154
|
+
|
155
|
+
it 'will throttle the loop' do
|
156
|
+
allow(redis).to receive(:evalsha) do
|
157
|
+
rates.unshift Time.now.to_f
|
158
|
+
[]
|
159
|
+
end
|
160
|
+
allow(redis).to receive(:connected?).and_return(connected.last)
|
161
|
+
allow(redis).to receive(:script)
|
162
|
+
allow(redis).to receive(:quit)
|
163
|
+
|
164
|
+
tt = Thread.new do
|
165
|
+
sleep 1
|
166
|
+
subject.do_stop
|
167
|
+
end
|
168
|
+
|
169
|
+
subject.run(accumulator)
|
170
|
+
|
171
|
+
tt.join
|
172
|
+
|
173
|
+
inters = []
|
174
|
+
rates.each_cons(2) do |x, y|
|
175
|
+
inters << x - y
|
176
|
+
end
|
177
|
+
|
178
|
+
expect(accumulator.size).to eq(0)
|
179
|
+
inters.each do |delta|
|
180
|
+
expect(delta).to be_within(0.01).of(LogStash::Inputs::Redis::BATCH_EMPTY_SLEEP)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
126
185
|
it 'multiple close calls, calls to redis once' do
|
127
186
|
subject.use_redis(redis)
|
128
187
|
allow(redis).to receive(:blpop).and_return(['foo', 'l1'])
|
metadata
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logstash-input-redis
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Elastic
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-03-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
|
14
|
+
name: logstash-core
|
15
|
+
version_requirements: !ruby/object:Gem::Requirement
|
15
16
|
requirements:
|
16
17
|
- - '>='
|
17
18
|
- !ruby/object:Gem::Version
|
@@ -19,10 +20,7 @@ dependencies:
|
|
19
20
|
- - <
|
20
21
|
- !ruby/object:Gem::Version
|
21
22
|
version: 3.0.0
|
22
|
-
|
23
|
-
prerelease: false
|
24
|
-
type: :runtime
|
25
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirement: !ruby/object:Gem::Requirement
|
26
24
|
requirements:
|
27
25
|
- - '>='
|
28
26
|
- !ruby/object:Gem::Version
|
@@ -30,48 +28,50 @@ dependencies:
|
|
30
28
|
- - <
|
31
29
|
- !ruby/object:Gem::Version
|
32
30
|
version: 3.0.0
|
31
|
+
prerelease: false
|
32
|
+
type: :runtime
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
|
+
name: logstash-codec-json
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - '>='
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
34
40
|
requirement: !ruby/object:Gem::Requirement
|
35
41
|
requirements:
|
36
42
|
- - '>='
|
37
43
|
- !ruby/object:Gem::Version
|
38
44
|
version: '0'
|
39
|
-
name: logstash-codec-json
|
40
45
|
prerelease: false
|
41
46
|
type: :runtime
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: redis
|
42
49
|
version_requirements: !ruby/object:Gem::Requirement
|
43
50
|
requirements:
|
44
51
|
- - '>='
|
45
52
|
- !ruby/object:Gem::Version
|
46
53
|
version: '0'
|
47
|
-
- !ruby/object:Gem::Dependency
|
48
54
|
requirement: !ruby/object:Gem::Requirement
|
49
55
|
requirements:
|
50
56
|
- - '>='
|
51
57
|
- !ruby/object:Gem::Version
|
52
58
|
version: '0'
|
53
|
-
name: redis
|
54
59
|
prerelease: false
|
55
60
|
type: :runtime
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: logstash-devutils
|
56
63
|
version_requirements: !ruby/object:Gem::Requirement
|
57
64
|
requirements:
|
58
65
|
- - '>='
|
59
66
|
- !ruby/object:Gem::Version
|
60
67
|
version: '0'
|
61
|
-
- !ruby/object:Gem::Dependency
|
62
68
|
requirement: !ruby/object:Gem::Requirement
|
63
69
|
requirements:
|
64
70
|
- - '>='
|
65
71
|
- !ruby/object:Gem::Version
|
66
72
|
version: '0'
|
67
|
-
name: logstash-devutils
|
68
73
|
prerelease: false
|
69
74
|
type: :development
|
70
|
-
version_requirements: !ruby/object:Gem::Requirement
|
71
|
-
requirements:
|
72
|
-
- - '>='
|
73
|
-
- !ruby/object:Gem::Version
|
74
|
-
version: '0'
|
75
75
|
description: This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program
|
76
76
|
email: info@elastic.co
|
77
77
|
executables: []
|