ffwd 0.1.7 → 0.2.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.
@@ -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