fluq 0.7.5 → 0.8.0
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/.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
|