ffwd 0.1.7 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -13,19 +13,35 @@
13
13
  # License for the specific language governing permissions and limitations under
14
14
  # the License.
15
15
 
16
+ require_relative '../../utils'
17
+
16
18
  module FFWD::TCP
17
19
  class Connection
18
20
  INITIAL_TIMEOUT = 2
19
21
 
22
+ # default flush period, if non-zero will cause the connection to be buffered.
23
+ DEFAULT_FLUSH_PERIOD = 10
24
+ # default amount of bytes that the outbound connection will allow in its
25
+ # application-level buffer.
26
+ DEFAULT_TCP_OUTBOUND_LIMIT = 2 ** 20
27
+
20
28
  attr_reader :log, :peer, :reporter_meta
21
29
 
22
- def initialize log, host, port, handler, args, outbound_limit
30
+ def self.prepare opts
31
+ opts[:flush_period] ||= DEFAULT_FLUSH_PERIOD
32
+ opts[:tcp_outbound_limit] ||= DEFAULT_TCP_OUTBOUND_LIMIT
33
+ opts[:ignored] = (opts[:ignored] || []).map{|v| Utils.check_ignored v}
34
+ opts
35
+ end
36
+
37
+ def initialize log, host, port, handler, config
23
38
  @log = log
24
39
  @host = host
25
40
  @port = port
26
41
  @handler = handler
27
- @args = args
28
- @outbound_limit = outbound_limit
42
+ @config = config
43
+
44
+ @tcp_outbound_limit = config[:tcp_outbound_limit]
29
45
 
30
46
  @peer = "#{host}:#{port}"
31
47
  @closing = false
@@ -39,8 +55,9 @@ module FFWD::TCP
39
55
 
40
56
  # Start attempting to connect.
41
57
  def connect
42
- log.info "Connecting to tcp://#{@host}:#{@port}"
43
- @c = EM.connect @host, @port, @handler, self, *@args
58
+ @c = EM.connect @host, @port, @handler, self, @config
59
+ log.info "Connect to tcp://#{@host}:#{@port}"
60
+ log.info " config: #{@config.inspect}"
44
61
  end
45
62
 
46
63
  # Explicitly disconnect and discard any reconnect attempts..
@@ -101,7 +118,7 @@ module FFWD::TCP
101
118
 
102
119
  # Check if a connection is writable or not.
103
120
  def writable?
104
- not @c.nil? and @open and @c.get_outbound_data_size < @outbound_limit
121
+ not @c.nil? and @open and @c.get_outbound_data_size < @tcp_outbound_limit
105
122
  end
106
123
  end
107
124
  end
@@ -21,6 +21,14 @@ module FFWD::TCP
21
21
  class FlushingConnect
22
22
  include FFWD::Reporter
23
23
 
24
+ # percent of maximum events/metrics which will cause a flush.
25
+ DEFAULT_FORCED_FLUSH_FACTOR = 0.8
26
+ # defaults for buffered connections.
27
+ # maximum amount of events to buffer up.
28
+ DEFAULT_EVENT_LIMIT = 1000
29
+ # maximum amount of metrics to buffer up.
30
+ DEFAULT_METRIC_LIMIT = 10000
31
+
24
32
  setup_reporter :keys => [
25
33
  :dropped_events, :dropped_metrics,
26
34
  :sent_events, :sent_metrics,
@@ -34,49 +42,55 @@ module FFWD::TCP
34
42
  @c.reporter_meta
35
43
  end
36
44
 
37
- def initialize(
38
- core, log, connection,
39
- flush_period, event_limit, metric_limit, flush_limit
40
- )
45
+ def self.prepare opts
46
+ opts[:forced_flush_factor] ||= DEFAULT_FORCED_FLUSH_FACTOR
47
+ opts[:event_limit] ||= DEFAULT_EVENT_LIMIT
48
+ opts[:metric_limit] ||= DEFAULT_METRIC_LIMIT
49
+ opts
50
+ end
51
+
52
+ def initialize(core, log, connection, config)
41
53
  @log = log
42
54
  @c = connection
43
55
 
44
- @flush_period = flush_period
45
- @event_limit = event_limit
46
- @event_flush_limit = flush_limit * event_limit
47
- @metric_limit = metric_limit
48
- @metric_flush_limit = flush_limit * metric_limit
56
+ flush_period = config[:flush_period]
57
+ ignored = config[:ignored]
58
+ forced_flush_factor = config[:forced_flush_factor]
59
+ event_limit = config[:event_limit]
60
+ metric_limit = config[:metric_limit]
49
61
 
50
62
  @event_buffer = []
51
63
  @metric_buffer = []
52
64
  @timer = nil
53
-
54
- subs = []
65
+ @subs = []
55
66
 
56
67
  core.starting do
57
- log.info "Flushing every #{@flush_period}s"
58
- @timer = EM::PeriodicTimer.new(@flush_period){flush!}
68
+ @c.connect
59
69
 
60
- event_consumer = setup_consumer(
61
- @event_buffer, @event_limit, @event_flush_limit, :dropped_events)
62
- metric_consumer = setup_consumer(
63
- @metric_buffer, @metric_limit, @metric_flush_limit, :dropped_metrics)
70
+ @timer = EM::PeriodicTimer.new(flush_period){flush!}
64
71
 
65
- subs << core.output.event_subscribe(&event_consumer)
66
- subs << core.output.metric_subscribe(&metric_consumer)
72
+ unless ignored.include? :events
73
+ event_consumer = setup_consumer(
74
+ @event_buffer, forced_flush_factor, event_limit, :dropped_events)
75
+ @subs << core.output.event_subscribe(&event_consumer)
76
+ end
67
77
 
68
- @c.connect
78
+ unless ignored.include? :metrics
79
+ metric_consumer = setup_consumer(
80
+ @metric_buffer, forced_flush_factor, metric_limit, :dropped_metrics)
81
+ @subs << core.output.metric_subscribe(&metric_consumer)
82
+ end
69
83
  end
70
84
 
71
85
  core.stopping do
72
- subs.each(&:unsubscribe).clear
86
+ @c.disconnect
73
87
 
74
88
  if @timer
75
89
  @timer.cancel
76
90
  @timer = nil
77
91
  end
78
92
 
79
- @c.disconnect
93
+ @subs.each(&:unsubscribe).clear
80
94
  end
81
95
  end
82
96
 
@@ -116,16 +130,19 @@ module FFWD::TCP
116
130
 
117
131
  private
118
132
 
119
- def setup_consumer buffer, buffer_limit, flush_limit, statistics_key
133
+ def setup_consumer buffer, drop, flush, statistics_key
134
+ drop_limit = drop
135
+ forced_flush_limit = drop * flush
136
+
120
137
  proc do |e|
121
- if buffer.size >= buffer_limit
138
+ if buffer.size >= drop_limit
122
139
  increment statistics_key, 1
123
140
  next
124
141
  end
125
142
 
126
143
  buffer << e
127
144
 
128
- if buffer.size >= flush_limit
145
+ if buffer.size >= forced_flush_limit
129
146
  increment :forced_flush, 1
130
147
  flush!
131
148
  end
@@ -19,6 +19,10 @@ module FFWD::TCP
19
19
  class PlainConnect
20
20
  include FFWD::Reporter
21
21
 
22
+ def self.prepare opts
23
+ opts
24
+ end
25
+
22
26
  setup_reporter :keys => [
23
27
  :dropped_events, :dropped_metrics,
24
28
  :sent_events, :sent_metrics,
@@ -33,16 +37,24 @@ module FFWD::TCP
33
37
 
34
38
  INITIAL_TIMEOUT = 2
35
39
 
36
- def initialize core, log, connection
40
+ def initialize core, log, connection, config
37
41
  @log = log
38
42
  @c = connection
39
43
 
44
+ ignored = config[:ignored]
45
+
40
46
  subs = []
41
47
 
42
48
  core.starting do
43
49
  @c.connect
44
- subs << core.output.event_subscribe{|e| handle_event e}
45
- subs << core.output.metric_subscribe{|e| handle_metric e}
50
+
51
+ unless ignored.include? :events
52
+ subs << core.output.event_subscribe{|e| handle_event e}
53
+ end
54
+
55
+ unless ignored.include? :metrics
56
+ subs << core.output.metric_subscribe{|e| handle_metric e}
57
+ end
46
58
  end
47
59
 
48
60
  core.stopping do
@@ -13,36 +13,61 @@
13
13
  # License for the specific language governing permissions and limitations under
14
14
  # the License.
15
15
 
16
- require 'eventmachine'
16
+ require_relative '../utils'
17
+ require_relative '../tunnel/udp'
17
18
 
18
19
  require_relative 'udp/connect'
19
20
  require_relative 'udp/bind'
20
21
 
21
- require_relative '../tunnel'
22
-
23
22
  module FFWD::UDP
24
23
  def self.family
25
24
  :udp
26
25
  end
27
26
 
28
- DEFAULT_REBIND_TIMEOUT = 10
27
+ class SetupOutput
28
+ attr_reader :config
29
+
30
+ def initialize config, log, handler
31
+ @config = config
32
+ @log = log
33
+ @handler = handler
34
+ @config = Connect.prepare Hash[@config]
35
+ end
36
+
37
+ def connect core
38
+ raise "Missing required key :host" if (host = @config[:host]).nil?
39
+ raise "Missing required key :port" if (port = @config[:port]).nil?
40
+ Connect.new core, @log, host, port, @handler, @config
41
+ end
42
+ end
29
43
 
30
- def self.connect opts, core, log, handler
31
- raise "Missing required key :host" if (host = opts[:host]).nil?
32
- raise "Missing required key :port" if (port = opts[:port]).nil?
33
- Connect.new core, log, host, port, handler
44
+ def self.connect config, log, handler
45
+ SetupOutput.new config, log, handler
34
46
  end
35
47
 
36
- def self.bind opts, core, log, connection, *args
37
- raise "Missing required key :host" if (host = opts[:host]).nil?
38
- raise "Missing required key :port" if (port = opts[:port]).nil?
39
- rebind_timeout = opts[:rebind_timeout] || DEFAULT_REBIND_TIMEOUT
40
- Bind.new core, log, host, port, connection, args, rebind_timeout
48
+ class SetupInput
49
+ attr_reader :config
50
+
51
+ def initialize config, log, connection
52
+ @config = config
53
+ @log = log
54
+ @connection = connection
55
+ end
56
+
57
+ def bind core
58
+ raise "Missing required key :host" if (host = @config[:host]).nil?
59
+ raise "Missing required key :port" if (port = @config[:port]).nil?
60
+ @config = Bind.prepare Hash[@config]
61
+ Bind.new core, @log, host, port, @connection, @config
62
+ end
63
+
64
+ def tunnel core, plugin
65
+ raise "Missing required key :port" if (port = @config[:port]).nil?
66
+ FFWD::Tunnel::UDP.new port, core, plugin, @log, @connection, @config
67
+ end
41
68
  end
42
69
 
43
- def self.tunnel opts, core, plugin, log, connection, *args
44
- raise "Missing required key :port" if (port = opts[:port]).nil?
45
- FFWD.tunnel self.family, port, core, plugin, log, connection, args
70
+ def self.bind config, log, connection
71
+ SetupInput.new config, log, connection
46
72
  end
47
73
  end
48
-
@@ -22,42 +22,53 @@ module FFWD::UDP
22
22
  class Bind
23
23
  include FFWD::Reporter
24
24
 
25
+ DEFAULT_REBIND_TIMEOUT = 10
26
+
27
+ def self.prepare opts
28
+ opts[:rebind_timeout] ||= DEFAULT_REBIND_TIMEOUT
29
+ opts
30
+ end
31
+
25
32
  setup_reporter :keys => [
26
33
  :received_events, :received_metrics,
27
34
  :failed_events, :failed_metrics
28
35
  ]
29
36
 
30
- attr_reader :reporter_meta
37
+ attr_reader :reporter_meta, :log, :config
31
38
 
32
- def initialize core, log, host, port, connection, args, rebind_timeout
39
+ def initialize core, log, host, port, connection, config
40
+ @log = log
33
41
  @peer = "#{host}:#{port}"
34
42
  @reporter_meta = {
35
43
  :type => connection.plugin_type,
36
44
  :listen => @peer, :family => 'udp'
37
45
  }
38
46
 
39
- @sig = nil
47
+ rebind_timeout = config[:rebind_timeout]
48
+
49
+ @socket = nil
40
50
 
41
51
  info = "udp://#{@peer}"
42
52
 
43
53
  r = FFWD.retry :timeout => rebind_timeout do |a|
44
- @sig = EM.open_datagram_socket host, port, connection, self, core, *args
54
+ @socket = EM.open_datagram_socket host, port, connection, self, core, config
45
55
  log.info "Bind on #{info} (attempt #{a})"
56
+ log.info " config: #{config.inspect}"
46
57
  end
47
58
 
48
59
  r.error do |a, t, e|
49
- log.error "Failed to bind #{info} (attempt #{a}), retry in #{t}s", e
60
+ log.warning "Bind on #{info} failed, retry ##{a} in #{t}s: #{e}"
50
61
  end
51
62
 
52
63
  r.depend_on core
53
64
 
54
65
  core.stopping do
55
- log.info "Unbinding #{info}"
56
-
57
- if @sig
58
- @sig.unbind
59
- @sig = nil
66
+ if @socket
67
+ @socket.unbind
68
+ @socket = nil
60
69
  end
70
+
71
+ log.info "Unbound #{info}"
61
72
  end
62
73
  end
63
74
  end
@@ -22,19 +22,26 @@ module FFWD::UDP
22
22
 
23
23
  RESOLVE_TIMEOUT = 10
24
24
 
25
- attr_reader :reporter_meta, :log
25
+ def self.prepare config
26
+ config[:ignored] = (config[:ignored] || []).map{|v| Utils.check_ignored v}
27
+ config
28
+ end
29
+
30
+ attr_reader :reporter_meta, :log, :config
26
31
 
27
32
  setup_reporter :keys => [
28
33
  :dropped_events, :dropped_metrics,
29
34
  :sent_events, :sent_metrics
30
35
  ]
31
36
 
32
- def initialize core, log, host, port, handler
37
+ def initialize core, log, host, port, handler, config
33
38
  @log = log
34
39
  @host = host
35
40
  @port = port
36
41
  @handler = handler
37
42
 
43
+ ignored = config[:ignored]
44
+
38
45
  @bind_host = "0.0.0.0"
39
46
  @host_ip = nil
40
47
  @c = nil
@@ -45,7 +52,7 @@ module FFWD::UDP
45
52
 
46
53
  info = "udp://#{@peer}"
47
54
 
48
- subs = []
55
+ @subs = []
49
56
 
50
57
  r = FFWD.retry :timeout => RESOLVE_TIMEOUT do |a|
51
58
  unless @host_ip
@@ -53,16 +60,22 @@ module FFWD::UDP
53
60
  raise "Could not resolve: #{@host}" if @host_ip.nil?
54
61
  end
55
62
 
56
- @c = EM.open_datagram_socket(@bind_host, nil, @handler, self)
63
+ @c = EM.open_datagram_socket @bind_host, nil, @handler, self, config
57
64
 
58
- log.info "Setup of output to #{info} successful"
65
+ unless ignored.include? :events
66
+ @subs << core.output.event_subscribe{|e| handle_event e}
67
+ end
68
+
69
+ unless ignored.include? :metrics
70
+ @subs << core.output.metric_subscribe{|m| handle_metric m}
71
+ end
59
72
 
60
- subs << core.output.event_subscribe{|e| handle_event e}
61
- subs << core.output.metric_subscribe{|m| handle_metric m}
73
+ log.info "Connect to #{info} (attempt #{a})"
74
+ log.info " config: #{config.inspect}"
62
75
  end
63
76
 
64
77
  r.error do |a, t, e|
65
- log.error "Setup of output to #{info} failed (attempt #{a}), retry in #{t}s", e
78
+ log.warning "Connect to #{info} failed, retry ##{a} in #{t}s: #{e}"
66
79
  end
67
80
 
68
81
  r.depend_on core
@@ -73,7 +86,7 @@ module FFWD::UDP
73
86
  @c = nil
74
87
  end
75
88
 
76
- subs.each(&:unsubscribe).clear
89
+ @subs.each(&:unsubscribe).clear
77
90
  end
78
91
  end
79
92
 
@@ -22,10 +22,13 @@ module FFWD
22
22
  class Retrier
23
23
  include FFWD::Lifecycle
24
24
 
25
+ MAX_FACTOR = 7
26
+
25
27
  def initialize timeout, &block
26
28
  @block = block
27
29
  @timer = nil
28
30
  @timeout = timeout
31
+ @max_timeout = timeout * 2**MAX_FACTOR
29
32
  @current_timeout = @timeout
30
33
  @attempt = 0
31
34
  @error_callbacks = []
@@ -56,7 +59,7 @@ module FFWD
56
59
  end
57
60
 
58
61
  @timer = EM::Timer.new(@current_timeout) do
59
- @current_timeout *= 2
62
+ @current_timeout *= 2 unless @current_timeout >= @max_timeout
60
63
  @timer = nil
61
64
  try_block
62
65
  end