fluq 0.7.5 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +3 -0
  4. data/Gemfile +12 -1
  5. data/Gemfile.lock +44 -8
  6. data/README.md +24 -6
  7. data/Rakefile +8 -1
  8. data/benchmark/socket.rb +13 -25
  9. data/examples/config/multi.rb +52 -0
  10. data/examples/config/simple.rb +15 -0
  11. data/fluq.gemspec +3 -3
  12. data/lib/fluq.rb +22 -16
  13. data/lib/fluq/cli.rb +3 -12
  14. data/lib/fluq/dsl.rb +2 -45
  15. data/lib/fluq/dsl/base.rb +11 -0
  16. data/lib/fluq/dsl/feed.rb +24 -0
  17. data/lib/fluq/dsl/root.rb +35 -0
  18. data/lib/fluq/event.rb +9 -28
  19. data/lib/fluq/feed.rb +40 -5
  20. data/lib/fluq/format.rb +6 -0
  21. data/lib/fluq/format/base.rb +42 -0
  22. data/lib/fluq/format/json.rb +17 -0
  23. data/lib/fluq/format/lines.rb +27 -0
  24. data/lib/fluq/format/msgpack.rb +28 -0
  25. data/lib/fluq/format/tsv.rb +19 -0
  26. data/lib/fluq/handler.rb +1 -1
  27. data/lib/fluq/handler/base.rb +11 -38
  28. data/lib/fluq/handler/log.rb +12 -14
  29. data/lib/fluq/handler/noop.rb +2 -0
  30. data/lib/fluq/input/base.rb +33 -29
  31. data/lib/fluq/input/socket.rb +46 -16
  32. data/lib/fluq/mixins.rb +2 -2
  33. data/lib/fluq/runner.rb +41 -0
  34. data/lib/fluq/testing.rb +5 -11
  35. data/lib/fluq/version.rb +1 -1
  36. data/lib/fluq/worker.rb +73 -0
  37. data/spec/fluq/dsl/feed_spec.rb +33 -0
  38. data/spec/fluq/dsl/root_spec.rb +20 -0
  39. data/spec/fluq/event_spec.rb +17 -12
  40. data/spec/fluq/feed_spec.rb +24 -0
  41. data/spec/fluq/format/base_spec.rb +9 -0
  42. data/spec/fluq/format/json_spec.rb +22 -0
  43. data/spec/fluq/format/lines_spec.rb +20 -0
  44. data/spec/fluq/format/msgpack_spec.rb +22 -0
  45. data/spec/fluq/format/tsv_spec.rb +21 -0
  46. data/spec/fluq/handler/base_spec.rb +7 -52
  47. data/spec/fluq/handler/log_spec.rb +11 -14
  48. data/spec/fluq/handler/{null_spec.rb → noop_spec.rb} +1 -3
  49. data/spec/fluq/input/base_spec.rb +48 -15
  50. data/spec/fluq/input/socket_spec.rb +34 -26
  51. data/spec/fluq/mixins/loggable_spec.rb +2 -2
  52. data/spec/fluq/runner_spec.rb +18 -0
  53. data/spec/fluq/worker_spec.rb +87 -0
  54. data/spec/fluq_spec.rb +1 -2
  55. data/spec/scenario/config/nested/feed1.rb +6 -0
  56. data/spec/scenario/config/test.rb +8 -2
  57. data/spec/spec_helper.rb +7 -26
  58. metadata +62 -62
  59. data/benchmark/logging.rb +0 -37
  60. data/examples/common.rb +0 -3
  61. data/examples/simple.rb +0 -5
  62. data/lib/fluq/buffer.rb +0 -6
  63. data/lib/fluq/buffer/base.rb +0 -51
  64. data/lib/fluq/buffer/file.rb +0 -68
  65. data/lib/fluq/feed/base.rb +0 -37
  66. data/lib/fluq/feed/json.rb +0 -28
  67. data/lib/fluq/feed/msgpack.rb +0 -27
  68. data/lib/fluq/feed/tsv.rb +0 -30
  69. data/lib/fluq/handler/null.rb +0 -4
  70. data/lib/fluq/input/socket/connection.rb +0 -41
  71. data/lib/fluq/mixins/logger.rb +0 -26
  72. data/lib/fluq/reactor.rb +0 -79
  73. data/spec/fluq/buffer/base_spec.rb +0 -21
  74. data/spec/fluq/buffer/file_spec.rb +0 -47
  75. data/spec/fluq/dsl_spec.rb +0 -43
  76. data/spec/fluq/feed/base_spec.rb +0 -15
  77. data/spec/fluq/feed/json_spec.rb +0 -27
  78. data/spec/fluq/feed/msgpack_spec.rb +0 -27
  79. data/spec/fluq/feed/tsv_spec.rb +0 -27
  80. data/spec/fluq/input/socket/connection_spec.rb +0 -35
  81. data/spec/fluq/mixins/logger_spec.rb +0 -25
  82. data/spec/fluq/reactor_spec.rb +0 -69
  83. data/spec/scenario/config/nested/common.rb +0 -3
data/examples/common.rb DELETED
@@ -1,3 +0,0 @@
1
- input :socket do
2
- bind "tcp://127.0.0.1:6790"
3
- end
data/examples/simple.rb DELETED
@@ -1,5 +0,0 @@
1
- input :socket do
2
- bind "tcp://127.0.0.1:6789"
3
- end
4
-
5
- handler :log
data/lib/fluq/buffer.rb DELETED
@@ -1,6 +0,0 @@
1
- module FluQ::Buffer
2
- end
3
-
4
- %w'base file'.each do |name|
5
- require "fluq/buffer/#{name}"
6
- end
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -1,4 +0,0 @@
1
- class FluQ::Handler::Null < FluQ::Handler::Base
2
- def on_events(events)
3
- end
4
- end
@@ -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
@@ -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