fluq 0.7.5 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +12 -1
- data/Gemfile.lock +44 -8
- data/README.md +24 -6
- data/Rakefile +8 -1
- data/benchmark/socket.rb +13 -25
- data/examples/config/multi.rb +52 -0
- data/examples/config/simple.rb +15 -0
- data/fluq.gemspec +3 -3
- data/lib/fluq.rb +22 -16
- data/lib/fluq/cli.rb +3 -12
- data/lib/fluq/dsl.rb +2 -45
- data/lib/fluq/dsl/base.rb +11 -0
- data/lib/fluq/dsl/feed.rb +24 -0
- data/lib/fluq/dsl/root.rb +35 -0
- data/lib/fluq/event.rb +9 -28
- data/lib/fluq/feed.rb +40 -5
- data/lib/fluq/format.rb +6 -0
- data/lib/fluq/format/base.rb +42 -0
- data/lib/fluq/format/json.rb +17 -0
- data/lib/fluq/format/lines.rb +27 -0
- data/lib/fluq/format/msgpack.rb +28 -0
- data/lib/fluq/format/tsv.rb +19 -0
- data/lib/fluq/handler.rb +1 -1
- data/lib/fluq/handler/base.rb +11 -38
- data/lib/fluq/handler/log.rb +12 -14
- data/lib/fluq/handler/noop.rb +2 -0
- data/lib/fluq/input/base.rb +33 -29
- data/lib/fluq/input/socket.rb +46 -16
- data/lib/fluq/mixins.rb +2 -2
- data/lib/fluq/runner.rb +41 -0
- data/lib/fluq/testing.rb +5 -11
- data/lib/fluq/version.rb +1 -1
- data/lib/fluq/worker.rb +73 -0
- data/spec/fluq/dsl/feed_spec.rb +33 -0
- data/spec/fluq/dsl/root_spec.rb +20 -0
- data/spec/fluq/event_spec.rb +17 -12
- data/spec/fluq/feed_spec.rb +24 -0
- data/spec/fluq/format/base_spec.rb +9 -0
- data/spec/fluq/format/json_spec.rb +22 -0
- data/spec/fluq/format/lines_spec.rb +20 -0
- data/spec/fluq/format/msgpack_spec.rb +22 -0
- data/spec/fluq/format/tsv_spec.rb +21 -0
- data/spec/fluq/handler/base_spec.rb +7 -52
- data/spec/fluq/handler/log_spec.rb +11 -14
- data/spec/fluq/handler/{null_spec.rb → noop_spec.rb} +1 -3
- data/spec/fluq/input/base_spec.rb +48 -15
- data/spec/fluq/input/socket_spec.rb +34 -26
- data/spec/fluq/mixins/loggable_spec.rb +2 -2
- data/spec/fluq/runner_spec.rb +18 -0
- data/spec/fluq/worker_spec.rb +87 -0
- data/spec/fluq_spec.rb +1 -2
- data/spec/scenario/config/nested/feed1.rb +6 -0
- data/spec/scenario/config/test.rb +8 -2
- data/spec/spec_helper.rb +7 -26
- metadata +62 -62
- data/benchmark/logging.rb +0 -37
- data/examples/common.rb +0 -3
- data/examples/simple.rb +0 -5
- data/lib/fluq/buffer.rb +0 -6
- data/lib/fluq/buffer/base.rb +0 -51
- data/lib/fluq/buffer/file.rb +0 -68
- data/lib/fluq/feed/base.rb +0 -37
- data/lib/fluq/feed/json.rb +0 -28
- data/lib/fluq/feed/msgpack.rb +0 -27
- data/lib/fluq/feed/tsv.rb +0 -30
- data/lib/fluq/handler/null.rb +0 -4
- data/lib/fluq/input/socket/connection.rb +0 -41
- data/lib/fluq/mixins/logger.rb +0 -26
- data/lib/fluq/reactor.rb +0 -79
- data/spec/fluq/buffer/base_spec.rb +0 -21
- data/spec/fluq/buffer/file_spec.rb +0 -47
- data/spec/fluq/dsl_spec.rb +0 -43
- data/spec/fluq/feed/base_spec.rb +0 -15
- data/spec/fluq/feed/json_spec.rb +0 -27
- data/spec/fluq/feed/msgpack_spec.rb +0 -27
- data/spec/fluq/feed/tsv_spec.rb +0 -27
- data/spec/fluq/input/socket/connection_spec.rb +0 -35
- data/spec/fluq/mixins/logger_spec.rb +0 -25
- data/spec/fluq/reactor_spec.rb +0 -69
- data/spec/scenario/config/nested/common.rb +0 -3
data/examples/common.rb
DELETED
data/examples/simple.rb
DELETED
data/lib/fluq/buffer.rb
DELETED
data/lib/fluq/buffer/base.rb
DELETED
@@ -1,51 +0,0 @@
|
|
1
|
-
class FluQ::Buffer::Base
|
2
|
-
MAX_SIZE = 256 * 1024 * 1024 # 256M
|
3
|
-
|
4
|
-
# @attr_reader [Hash] config
|
5
|
-
attr_reader :config
|
6
|
-
|
7
|
-
# @param [Hash] options various configuration options
|
8
|
-
def initialize(options = {})
|
9
|
-
super()
|
10
|
-
@config = defaults.merge(options)
|
11
|
-
end
|
12
|
-
|
13
|
-
# @return [String] name identifier
|
14
|
-
def name
|
15
|
-
@name ||= self.class.name.split("::").last.downcase
|
16
|
-
end
|
17
|
-
|
18
|
-
# @abstract
|
19
|
-
# @yield over io object
|
20
|
-
# @yieldparam [IO] io
|
21
|
-
def drain
|
22
|
-
yield StringIO.new
|
23
|
-
end
|
24
|
-
|
25
|
-
# @abstract
|
26
|
-
# @return [Integer] the size
|
27
|
-
def size
|
28
|
-
0
|
29
|
-
end
|
30
|
-
|
31
|
-
# @return [Boolean] true if size exceeds limit
|
32
|
-
def full?
|
33
|
-
size >= config[:max_size]
|
34
|
-
end
|
35
|
-
|
36
|
-
# @abstract data writer
|
37
|
-
# @param [String] data binary string
|
38
|
-
def write(data)
|
39
|
-
end
|
40
|
-
|
41
|
-
# @abstract callback, close buffer
|
42
|
-
def close
|
43
|
-
end
|
44
|
-
|
45
|
-
protected
|
46
|
-
|
47
|
-
def defaults
|
48
|
-
{ max_size: MAX_SIZE }
|
49
|
-
end
|
50
|
-
|
51
|
-
end
|
data/lib/fluq/buffer/file.rb
DELETED
@@ -1,68 +0,0 @@
|
|
1
|
-
class FluQ::Buffer::File < FluQ::Buffer::Base
|
2
|
-
|
3
|
-
# @attr_reader [File] file instance
|
4
|
-
attr_reader :file
|
5
|
-
|
6
|
-
# @see FluQ::Buffer::Base#initialize
|
7
|
-
def initialize(*)
|
8
|
-
super
|
9
|
-
@file = new_file
|
10
|
-
@size = 0
|
11
|
-
end
|
12
|
-
|
13
|
-
# @see FluQ::Buffer::Base#name
|
14
|
-
def name
|
15
|
-
@name ||= [super, File.basename(file.path)].join("-")
|
16
|
-
end
|
17
|
-
|
18
|
-
# @see FluQ::Buffer::Base#write
|
19
|
-
def write(data)
|
20
|
-
file.write(data)
|
21
|
-
end
|
22
|
-
|
23
|
-
# @see FluQ::Buffer::Base#size
|
24
|
-
def size
|
25
|
-
file.size
|
26
|
-
end
|
27
|
-
|
28
|
-
# @see FluQ::Buffer::Base#close
|
29
|
-
def close
|
30
|
-
file.close unless file.closed?
|
31
|
-
File.unlink(file.path) if File.exists?(file.path)
|
32
|
-
end
|
33
|
-
|
34
|
-
# @see FluQ::Buffer::Base#drain
|
35
|
-
def drain
|
36
|
-
file.close unless file.closed?
|
37
|
-
io = File.open(file.path, 'rb', encoding: Encoding::BINARY)
|
38
|
-
yield(io)
|
39
|
-
ensure
|
40
|
-
io.close if io
|
41
|
-
end
|
42
|
-
|
43
|
-
protected
|
44
|
-
|
45
|
-
def defaults
|
46
|
-
super.merge(path: "tmp/buffers")
|
47
|
-
end
|
48
|
-
|
49
|
-
def new_file
|
50
|
-
path = nil
|
51
|
-
incr = 0
|
52
|
-
path = root.join(generate_name(incr+=1)) until path && !path.exist?
|
53
|
-
file = path.open("wb", encoding: Encoding::BINARY)
|
54
|
-
file.sync = true
|
55
|
-
file
|
56
|
-
end
|
57
|
-
|
58
|
-
def root
|
59
|
-
@root ||= FluQ.root.join(config[:path]).tap do |full_path|
|
60
|
-
FileUtils.mkdir_p full_path.to_s
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def generate_name(index)
|
65
|
-
"fb-#{(Time.now.utc.to_f * 1000).round}.#{index}"
|
66
|
-
end
|
67
|
-
|
68
|
-
end
|
data/lib/fluq/feed/base.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
class FluQ::Feed::Base
|
2
|
-
include Enumerable
|
3
|
-
include FluQ::Mixins::Loggable
|
4
|
-
extend FluQ::Mixins::Loggable
|
5
|
-
|
6
|
-
# @abstract enumerator
|
7
|
-
# @param [String] raw event string
|
8
|
-
# @return [FluQ::Event] event
|
9
|
-
def self.to_event(raw)
|
10
|
-
end
|
11
|
-
|
12
|
-
# @attr_reader [FluQ::Buffer::Base] buffer
|
13
|
-
attr_reader :buffer
|
14
|
-
|
15
|
-
# @param [FluQ::Buffer::Base] buffer
|
16
|
-
def initialize(buffer)
|
17
|
-
@buffer = buffer
|
18
|
-
end
|
19
|
-
|
20
|
-
# @yield ober a feed of events
|
21
|
-
# @yieldparam [FluQ::Event] event
|
22
|
-
def each
|
23
|
-
each_raw do |raw|
|
24
|
-
event = self.class.to_event(raw)
|
25
|
-
yield event if event
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
protected
|
30
|
-
|
31
|
-
# @abstract enumerator
|
32
|
-
# @yield ober a feed of raw events
|
33
|
-
# @yieldparam [String] raw event
|
34
|
-
def each_raw
|
35
|
-
end
|
36
|
-
|
37
|
-
end
|
data/lib/fluq/feed/json.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
class FluQ::Feed::Json < FluQ::Feed::Base
|
2
|
-
|
3
|
-
# @see FluQ::Feed::Base.to_event
|
4
|
-
def self.to_event(raw)
|
5
|
-
case hash = Oj.load(raw)
|
6
|
-
when Hash
|
7
|
-
FluQ::Event.new hash.delete("="), hash.delete("@"), hash
|
8
|
-
else
|
9
|
-
logger.warn "buffer contained invalid event #{hash.inspect}"
|
10
|
-
nil
|
11
|
-
end
|
12
|
-
rescue Oj::ParseError
|
13
|
-
logger.warn "buffer contained invalid line #{raw.inspect}"
|
14
|
-
nil
|
15
|
-
end
|
16
|
-
|
17
|
-
protected
|
18
|
-
|
19
|
-
# @see [FluQ::Feed::Base] each_raw
|
20
|
-
def each_raw
|
21
|
-
buffer.drain do |io|
|
22
|
-
while line = io.gets
|
23
|
-
yield line
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
end
|
data/lib/fluq/feed/msgpack.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
class FluQ::Feed::Msgpack < FluQ::Feed::Base
|
2
|
-
|
3
|
-
# @see FluQ::Feed::Base.to_event
|
4
|
-
def self.to_event(raw)
|
5
|
-
raw = MessagePack.unpack(raw) if raw.is_a?(String)
|
6
|
-
|
7
|
-
case raw
|
8
|
-
when Hash
|
9
|
-
FluQ::Event.new raw.delete("="), raw.delete("@"), raw
|
10
|
-
else
|
11
|
-
logger.warn "buffer contained invalid event #{raw.inspect}"
|
12
|
-
nil
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
protected
|
17
|
-
|
18
|
-
# @see [FluQ::Feed::Base] each
|
19
|
-
def each_raw(&block)
|
20
|
-
buffer.drain do |io|
|
21
|
-
pac = MessagePack::Unpacker.new(io)
|
22
|
-
pac.each(&block)
|
23
|
-
end
|
24
|
-
rescue EOFError
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
data/lib/fluq/feed/tsv.rb
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
class FluQ::Feed::Tsv < FluQ::Feed::Base
|
2
|
-
|
3
|
-
# @see FluQ::Feed::Base.to_event
|
4
|
-
def self.to_event(raw)
|
5
|
-
tag, timestamp, json = raw.split("\t")
|
6
|
-
|
7
|
-
case hash = Oj.load(json)
|
8
|
-
when Hash
|
9
|
-
FluQ::Event.new tag, timestamp, hash
|
10
|
-
else
|
11
|
-
logger.warn "buffer contained invalid event #{hash.inspect}"
|
12
|
-
nil
|
13
|
-
end
|
14
|
-
rescue Oj::ParseError, ArgumentError
|
15
|
-
logger.warn "buffer contained invalid line #{raw.inspect}"
|
16
|
-
nil
|
17
|
-
end
|
18
|
-
|
19
|
-
protected
|
20
|
-
|
21
|
-
# @see [FluQ::Feed::Base] each_raw
|
22
|
-
def each_raw
|
23
|
-
buffer.drain do |io|
|
24
|
-
while line = io.gets
|
25
|
-
yield line
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|
data/lib/fluq/handler/null.rb
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
class FluQ::Input::Socket::Connection < EventMachine::Connection
|
2
|
-
include FluQ::Mixins::Loggable
|
3
|
-
|
4
|
-
# Constructor
|
5
|
-
# @param [FluQ::Input::Socket] parent the input
|
6
|
-
def initialize(parent)
|
7
|
-
super()
|
8
|
-
@parent = parent
|
9
|
-
end
|
10
|
-
|
11
|
-
# Callback
|
12
|
-
def post_init
|
13
|
-
self.comm_inactivity_timeout = 60
|
14
|
-
end
|
15
|
-
|
16
|
-
# Callback
|
17
|
-
def receive_data(data)
|
18
|
-
buffer.write(data)
|
19
|
-
flush! if buffer.full?
|
20
|
-
rescue => ex
|
21
|
-
logger.crash "#{self.class.name} failure: #{ex.message} (#{ex.class.name})", ex
|
22
|
-
end
|
23
|
-
|
24
|
-
# Callback
|
25
|
-
def unbind
|
26
|
-
flush!
|
27
|
-
end
|
28
|
-
|
29
|
-
protected
|
30
|
-
|
31
|
-
def buffer
|
32
|
-
@buffer ||= @parent.new_buffer
|
33
|
-
end
|
34
|
-
|
35
|
-
def flush!
|
36
|
-
current = buffer
|
37
|
-
@buffer = nil
|
38
|
-
@parent.flush!(current)
|
39
|
-
end
|
40
|
-
|
41
|
-
end
|
data/lib/fluq/mixins/logger.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
module FluQ::Mixins::Logger
|
2
|
-
|
3
|
-
def exception_handlers
|
4
|
-
@exception_handlers ||= []
|
5
|
-
end
|
6
|
-
|
7
|
-
def exception_handler(&block)
|
8
|
-
exception_handlers << block
|
9
|
-
end
|
10
|
-
|
11
|
-
def crash(string, exception)
|
12
|
-
if exception.respond_to?(:backtrace) && exception.backtrace
|
13
|
-
trace = exception.backtrace.map {|line| " #{line}" }.join("\n")
|
14
|
-
end
|
15
|
-
error [string, trace].compact.join("\n")
|
16
|
-
|
17
|
-
exception_handlers.each do |handler|
|
18
|
-
begin
|
19
|
-
handler.call(exception)
|
20
|
-
rescue => ex
|
21
|
-
error "EXCEPTION HANDLER CRASHED: #{ex.message}"
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
end
|
data/lib/fluq/reactor.rb
DELETED
@@ -1,79 +0,0 @@
|
|
1
|
-
class FluQ::Reactor
|
2
|
-
include FluQ::Mixins::Loggable
|
3
|
-
|
4
|
-
# attr_reader [Array] handlers
|
5
|
-
attr_reader :handlers
|
6
|
-
|
7
|
-
# attr_reader [Array] inputs
|
8
|
-
attr_reader :inputs
|
9
|
-
|
10
|
-
# Runs the reactor within EventMachine
|
11
|
-
def self.run
|
12
|
-
EM.run do
|
13
|
-
EM.threadpool_size = 100
|
14
|
-
yield new
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
# Constructor
|
19
|
-
def initialize
|
20
|
-
super
|
21
|
-
@handlers = []
|
22
|
-
@inputs = []
|
23
|
-
end
|
24
|
-
|
25
|
-
# Listens to an input
|
26
|
-
# @param [Class<FluQ::Input::Base>] klass input class
|
27
|
-
# @param [multiple] args initialization arguments
|
28
|
-
def listen(klass, *args)
|
29
|
-
input = klass.new(self, *args).tap(&:run)
|
30
|
-
inputs.push(input)
|
31
|
-
logger.info "Listening to #{input.name}"
|
32
|
-
input
|
33
|
-
end
|
34
|
-
|
35
|
-
# Registers a handler
|
36
|
-
# @param [Class<FluQ::Handler::Base>] klass handler class
|
37
|
-
# @param [multiple] args initialization arguments
|
38
|
-
def register(klass, *args)
|
39
|
-
handler = klass.new(self, *args)
|
40
|
-
if handlers.any? {|h| h.name == handler.name }
|
41
|
-
raise ArgumentError, "Handler '#{handler.name}' is already registered. Please provide a unique :name option"
|
42
|
-
end
|
43
|
-
handlers.push(handler)
|
44
|
-
logger.info "Registered #{handler.name}"
|
45
|
-
handler
|
46
|
-
end
|
47
|
-
|
48
|
-
# @param [Array<Event>] events to process
|
49
|
-
def process(events)
|
50
|
-
on_events events
|
51
|
-
true
|
52
|
-
end
|
53
|
-
|
54
|
-
# @return [String] introspection
|
55
|
-
def inspect
|
56
|
-
"#<#{self.class.name} inputs: #{inputs.size}, handlers: #{handlers.size}>"
|
57
|
-
end
|
58
|
-
|
59
|
-
protected
|
60
|
-
|
61
|
-
def on_events(events)
|
62
|
-
handlers.map do |handler|
|
63
|
-
Thread.new { handle(handler, Time.now, events) }
|
64
|
-
end.each(&:join)
|
65
|
-
end
|
66
|
-
|
67
|
-
def handle(handler, start, events)
|
68
|
-
matching = handler.select(events)
|
69
|
-
::Timeout.timeout handler.config[:timeout] do
|
70
|
-
handler.on_events(matching)
|
71
|
-
end unless matching.empty?
|
72
|
-
logger.info { "#{handler.name} processed #{matching.size}/#{events.size} events in #{((Time.now - start) * 1000).round}ms" }
|
73
|
-
rescue Timeout::Error => tx
|
74
|
-
logger.crash "#{handler.class.name} #{handler.name} timeout out after #{handler.config[:timeout]}s", tx
|
75
|
-
rescue => ex
|
76
|
-
logger.crash "#{handler.class.name} #{handler.name} failed: #{ex.class.name} #{ex.message}", ex
|
77
|
-
end
|
78
|
-
|
79
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe FluQ::Buffer::Base do
|
4
|
-
|
5
|
-
its(:config) { should == {max_size: 268435456} }
|
6
|
-
its(:size) { should be(0) }
|
7
|
-
its(:name) { should == "base" }
|
8
|
-
it { should respond_to(:write) }
|
9
|
-
it { should respond_to(:close) }
|
10
|
-
it { should_not be_full }
|
11
|
-
|
12
|
-
it 'should drain' do
|
13
|
-
subject.drain {|io| io.should be_instance_of(StringIO) }
|
14
|
-
end
|
15
|
-
|
16
|
-
describe 'when size exeeds limit' do
|
17
|
-
before { subject.stub size: 268435457 }
|
18
|
-
it { should be_full }
|
19
|
-
end
|
20
|
-
|
21
|
-
end
|