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.
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