ffwd 0.1.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 +7 -0
- data/bin/ffwd +9 -0
- data/bin/fwc +15 -0
- data/lib/em/all.rb +68 -0
- data/lib/ffwd.rb +250 -0
- data/lib/ffwd/channel.rb +62 -0
- data/lib/ffwd/circular_buffer.rb +78 -0
- data/lib/ffwd/connection.rb +40 -0
- data/lib/ffwd/core.rb +173 -0
- data/lib/ffwd/core/emitter.rb +38 -0
- data/lib/ffwd/core/interface.rb +47 -0
- data/lib/ffwd/core/processor.rb +92 -0
- data/lib/ffwd/core/reporter.rb +32 -0
- data/lib/ffwd/debug.rb +76 -0
- data/lib/ffwd/debug/connection.rb +48 -0
- data/lib/ffwd/debug/monitor_session.rb +71 -0
- data/lib/ffwd/debug/tcp.rb +82 -0
- data/lib/ffwd/event.rb +65 -0
- data/lib/ffwd/event_emitter.rb +57 -0
- data/lib/ffwd/handler.rb +43 -0
- data/lib/ffwd/lifecycle.rb +92 -0
- data/lib/ffwd/logging.rb +139 -0
- data/lib/ffwd/metric.rb +55 -0
- data/lib/ffwd/metric_emitter.rb +50 -0
- data/lib/ffwd/plugin.rb +149 -0
- data/lib/ffwd/plugin/json_line.rb +47 -0
- data/lib/ffwd/plugin/json_line/connection.rb +118 -0
- data/lib/ffwd/plugin/log.rb +35 -0
- data/lib/ffwd/plugin/log/writer.rb +42 -0
- data/lib/ffwd/plugin_channel.rb +64 -0
- data/lib/ffwd/plugin_loader.rb +121 -0
- data/lib/ffwd/processor.rb +96 -0
- data/lib/ffwd/processor/count.rb +109 -0
- data/lib/ffwd/processor/histogram.rb +200 -0
- data/lib/ffwd/processor/rate.rb +116 -0
- data/lib/ffwd/producing_client.rb +181 -0
- data/lib/ffwd/protocol.rb +28 -0
- data/lib/ffwd/protocol/tcp.rb +126 -0
- data/lib/ffwd/protocol/tcp/bind.rb +64 -0
- data/lib/ffwd/protocol/tcp/connection.rb +107 -0
- data/lib/ffwd/protocol/tcp/flushing_connect.rb +135 -0
- data/lib/ffwd/protocol/tcp/plain_connect.rb +74 -0
- data/lib/ffwd/protocol/udp.rb +48 -0
- data/lib/ffwd/protocol/udp/bind.rb +64 -0
- data/lib/ffwd/protocol/udp/connect.rb +110 -0
- data/lib/ffwd/reporter.rb +65 -0
- data/lib/ffwd/retrier.rb +72 -0
- data/lib/ffwd/schema.rb +92 -0
- data/lib/ffwd/schema/default.rb +36 -0
- data/lib/ffwd/schema/spotify100.rb +58 -0
- data/lib/ffwd/statistics.rb +29 -0
- data/lib/ffwd/statistics/collector.rb +99 -0
- data/lib/ffwd/statistics/system_statistics.rb +255 -0
- data/lib/ffwd/tunnel.rb +27 -0
- data/lib/ffwd/tunnel/plugin.rb +47 -0
- data/lib/ffwd/tunnel/tcp.rb +60 -0
- data/lib/ffwd/tunnel/udp.rb +61 -0
- data/lib/ffwd/utils.rb +46 -0
- data/lib/ffwd/version.rb +18 -0
- data/lib/fwc.rb +206 -0
- metadata +163 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
# $LICENSE
|
2
|
+
# Copyright 2013-2014 Spotify AB. All rights reserved.
|
3
|
+
#
|
4
|
+
# The contents of this file are licensed under the Apache License, Version 2.0
|
5
|
+
# (the "License"); you may not use this file except in compliance with the
|
6
|
+
# License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
12
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
13
|
+
# License for the specific language governing permissions and limitations under
|
14
|
+
# the License.
|
15
|
+
|
16
|
+
require_relative 'protocol/udp'
|
17
|
+
require_relative 'protocol/tcp'
|
18
|
+
|
19
|
+
module FFWD
|
20
|
+
def self.parse_protocol(original)
|
21
|
+
string = original.downcase
|
22
|
+
|
23
|
+
return UDP if string == "udp"
|
24
|
+
return TCP if string == "tcp"
|
25
|
+
|
26
|
+
throw "Unknown protocol '#{original}'"
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# $LICENSE
|
2
|
+
# Copyright 2013-2014 Spotify AB. All rights reserved.
|
3
|
+
#
|
4
|
+
# The contents of this file are licensed under the Apache License, Version 2.0
|
5
|
+
# (the "License"); you may not use this file except in compliance with the
|
6
|
+
# License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
12
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
13
|
+
# License for the specific language governing permissions and limitations under
|
14
|
+
# the License.
|
15
|
+
|
16
|
+
require 'eventmachine'
|
17
|
+
|
18
|
+
require_relative '../tunnel'
|
19
|
+
|
20
|
+
require_relative 'tcp/bind'
|
21
|
+
require_relative 'tcp/plain_connect'
|
22
|
+
require_relative 'tcp/flushing_connect'
|
23
|
+
require_relative 'tcp/connection'
|
24
|
+
|
25
|
+
module FFWD::TCP
|
26
|
+
def self.family
|
27
|
+
:tcp
|
28
|
+
end
|
29
|
+
|
30
|
+
# default amount of bytes that the outbound connection will allow in its
|
31
|
+
# application-level buffer.
|
32
|
+
DEFAULT_OUTBOUND_LIMIT = 2 ** 20
|
33
|
+
# default flush period, if non-zero will cause the connection to be buffered.
|
34
|
+
DEFAULT_FLUSH_PERIOD = 10
|
35
|
+
# defaults for buffered connections.
|
36
|
+
# maximum amount of events to buffer up.
|
37
|
+
DEFAULT_EVENT_LIMIT = 1000
|
38
|
+
# maximum amount of metrics to buffer up.
|
39
|
+
DEFAULT_METRIC_LIMIT = 10000
|
40
|
+
# percent of maximum events/metrics which will cause a flush.
|
41
|
+
DEFAULT_FLUSH_LIMIT = 0.8
|
42
|
+
# Default initial timeout when binding fails.
|
43
|
+
DEFAULT_REBIND_TIMEOUT = 10
|
44
|
+
|
45
|
+
# Establish an outbound tcp connection.
|
46
|
+
#
|
47
|
+
# opts - Option hash.
|
48
|
+
# Expects the following keys.
|
49
|
+
# :host - The host to connect to.
|
50
|
+
# :port - The port to connect to.
|
51
|
+
# :outbound_limit - The amount of bytes that are allowed to be pending in
|
52
|
+
# the application level buffer for the connection to be considered
|
53
|
+
# 'writable'.
|
54
|
+
# :flush_period - The period in which outgoing data is buffered. If this
|
55
|
+
# is 0 no buffering will occur.
|
56
|
+
# Reads the following keys if the connection is buffered.
|
57
|
+
# :event_limit - The maximum amount of events the connection is allowed
|
58
|
+
# to buffer up.
|
59
|
+
# :metric_limimt - The maximum amount of metrics the connection is
|
60
|
+
# allowed to buffer up.
|
61
|
+
# :flush_limit - A percentage (0.0 - 1.0) indicating 'the percentage of
|
62
|
+
# events/metrics' that is required for a flush to be forced.
|
63
|
+
# If this percentage is reached, the connection will attempt to forcibly
|
64
|
+
# flush all buffered events and metrics prior to the end of the flushing
|
65
|
+
# period.
|
66
|
+
# core - The core interface associated with this connection.
|
67
|
+
# log - The logger to use for this connection.
|
68
|
+
# handler - An implementation of FFWD::Handler containing the connection
|
69
|
+
# logic.
|
70
|
+
# args - Arguments passed to the handler when a new instance is created.
|
71
|
+
def self.connect opts, core, log, handler, *args
|
72
|
+
raise "Missing required option :host" if (host = opts[:host]).nil?
|
73
|
+
raise "Missing required option :port" if (port = opts[:port]).nil?
|
74
|
+
|
75
|
+
outbound_limit = opts[:outbound_limit] || DEFAULT_OUTBOUND_LIMIT
|
76
|
+
flush_period = opts[:flush_period] || DEFAULT_FLUSH_PERIOD
|
77
|
+
|
78
|
+
connection = Connection.new log, host, port, handler, args, outbound_limit
|
79
|
+
|
80
|
+
if flush_period == 0
|
81
|
+
PlainConnect.new core, log, connection
|
82
|
+
else
|
83
|
+
event_limit = opts[:event_limit] || DEFAULT_EVENT_LIMIT
|
84
|
+
metric_limit = opts[:metric_limit] || DEFAULT_METRIC_LIMIT
|
85
|
+
flush_limit = opts[:flush_limit] || DEFAULT_FLUSH_LIMIT
|
86
|
+
|
87
|
+
FlushingConnect.new(
|
88
|
+
core, log, connection,
|
89
|
+
flush_period, event_limit, metric_limit, flush_limit
|
90
|
+
)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Bind and listen for a TCP connection.
|
95
|
+
#
|
96
|
+
# opts - Option hash.
|
97
|
+
# :host - The host to bind to.
|
98
|
+
# :port - The port to bind to.
|
99
|
+
# :rebind_timeout - The initial timeout to use when rebinding the
|
100
|
+
# connection.
|
101
|
+
# core - The core interface associated with this connection.
|
102
|
+
# log - The logger to use for this connection.
|
103
|
+
# connection - An implementation of FFWD::Connection containing the
|
104
|
+
# connection logic.
|
105
|
+
# args - Arguments passed to the connection when a new instance is created.
|
106
|
+
def self.bind opts, core, log, connection, *args
|
107
|
+
raise "Missing required option :host" if (host = opts[:host]).nil?
|
108
|
+
raise "Missing required option :port" if (port = opts[:port]).nil?
|
109
|
+
rebind_timeout = opts[:rebind_timeout] || DEFAULT_REBIND_TIMEOUT
|
110
|
+
Bind.new core, log, host, port, connection, args, rebind_timeout
|
111
|
+
end
|
112
|
+
|
113
|
+
# Set up a TCP tunnel.
|
114
|
+
#
|
115
|
+
# opts - Option hash.
|
116
|
+
# :port - The port to bind to on the remote side.
|
117
|
+
# core - The core interface associated with this connection.
|
118
|
+
# log - The logger to use for this connection.
|
119
|
+
# connection - An implementation of FFWD::Connection containing the
|
120
|
+
# connection logic.
|
121
|
+
# args - Arguments passed to the connection when a new instance is created.
|
122
|
+
def self.tunnel opts, core, plugin, log, connection, *args
|
123
|
+
raise "Missing required option :port" if (port = opts[:port]).nil?
|
124
|
+
FFWD.tunnel self.family, port, core, plugin, log, connection, args
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# $LICENSE
|
2
|
+
# Copyright 2013-2014 Spotify AB. All rights reserved.
|
3
|
+
#
|
4
|
+
# The contents of this file are licensed under the Apache License, Version 2.0
|
5
|
+
# (the "License"); you may not use this file except in compliance with the
|
6
|
+
# License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
12
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
13
|
+
# License for the specific language governing permissions and limitations under
|
14
|
+
# the License.
|
15
|
+
|
16
|
+
require 'eventmachine'
|
17
|
+
|
18
|
+
require_relative '../../reporter'
|
19
|
+
require_relative '../../retrier'
|
20
|
+
|
21
|
+
module FFWD::TCP
|
22
|
+
class Bind
|
23
|
+
include FFWD::Reporter
|
24
|
+
|
25
|
+
setup_reporter :keys => [
|
26
|
+
:received_events, :received_metrics,
|
27
|
+
:failed_events, :failed_metrics
|
28
|
+
]
|
29
|
+
|
30
|
+
attr_reader :reporter_meta
|
31
|
+
|
32
|
+
def initialize core, log, host, port, connection, args, rebind_timeout
|
33
|
+
@peer = "#{host}:#{port}"
|
34
|
+
@reporter_meta = {
|
35
|
+
:type => connection.plugin_type,
|
36
|
+
:listen => @peer, :family => 'tcp'
|
37
|
+
}
|
38
|
+
|
39
|
+
@sig = nil
|
40
|
+
|
41
|
+
info = "tcp://#{@peer}"
|
42
|
+
|
43
|
+
r = FFWD.retry :timeout => rebind_timeout do |a|
|
44
|
+
@sig = EM.start_server host, port, connection, self, core, *args
|
45
|
+
log.info "Bind on #{info} (attempt #{a})"
|
46
|
+
end
|
47
|
+
|
48
|
+
r.error do |a, t, e|
|
49
|
+
log.error "Failed to bind #{info} (attempt #{a}), retry in #{t}s", e
|
50
|
+
end
|
51
|
+
|
52
|
+
r.depend_on core
|
53
|
+
|
54
|
+
core.stopping do
|
55
|
+
log.info "Unbinding #{info}"
|
56
|
+
|
57
|
+
if @sig
|
58
|
+
EM.stop_server @sig
|
59
|
+
@sig = nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# $LICENSE
|
2
|
+
# Copyright 2013-2014 Spotify AB. All rights reserved.
|
3
|
+
#
|
4
|
+
# The contents of this file are licensed under the Apache License, Version 2.0
|
5
|
+
# (the "License"); you may not use this file except in compliance with the
|
6
|
+
# License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
12
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
13
|
+
# License for the specific language governing permissions and limitations under
|
14
|
+
# the License.
|
15
|
+
|
16
|
+
module FFWD::TCP
|
17
|
+
class Connection
|
18
|
+
INITIAL_TIMEOUT = 2
|
19
|
+
|
20
|
+
attr_reader :log, :peer, :reporter_meta
|
21
|
+
|
22
|
+
def initialize log, host, port, handler, args, outbound_limit
|
23
|
+
@log = log
|
24
|
+
@host = host
|
25
|
+
@port = port
|
26
|
+
@handler = handler
|
27
|
+
@args = args
|
28
|
+
@outbound_limit = outbound_limit
|
29
|
+
|
30
|
+
@peer = "#{host}:#{port}"
|
31
|
+
@closing = false
|
32
|
+
@reconnect_timeout = INITIAL_TIMEOUT
|
33
|
+
@reporter_meta = {:type => @handler.plugin_type, :peer => peer}
|
34
|
+
|
35
|
+
@timer = nil
|
36
|
+
@c = nil
|
37
|
+
@open = false
|
38
|
+
end
|
39
|
+
|
40
|
+
# Start attempting to connect.
|
41
|
+
def connect
|
42
|
+
log.info "Connecting to tcp://#{@host}:#{@port}"
|
43
|
+
@c = EM.connect @host, @port, @handler, self, *@args
|
44
|
+
end
|
45
|
+
|
46
|
+
# Explicitly disconnect and discard any reconnect attempts..
|
47
|
+
def disconnect
|
48
|
+
log.info "Disconnecting from tcp://#{@host}:#{@port}"
|
49
|
+
@closing = true
|
50
|
+
|
51
|
+
@c.close_connection if @c
|
52
|
+
@timer.cancel if @timer
|
53
|
+
@c = nil
|
54
|
+
@timer = nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def send_event event
|
58
|
+
@c.send_event event
|
59
|
+
end
|
60
|
+
|
61
|
+
def send_metric metric
|
62
|
+
@c.send_metric metric
|
63
|
+
end
|
64
|
+
|
65
|
+
def send_all events, metrics
|
66
|
+
@c.send_all events, metrics
|
67
|
+
end
|
68
|
+
|
69
|
+
def connection_completed
|
70
|
+
@open = true
|
71
|
+
@log.info "Connected tcp://#{peer}"
|
72
|
+
@reconnect_timeout = INITIAL_TIMEOUT
|
73
|
+
|
74
|
+
unless @timer.nil?
|
75
|
+
@timer.cancel
|
76
|
+
@timer = nil
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def unbind
|
81
|
+
@open = false
|
82
|
+
@c = nil
|
83
|
+
|
84
|
+
if @closing
|
85
|
+
return
|
86
|
+
end
|
87
|
+
|
88
|
+
@log.info "Disconnected from tcp://#{peer}, reconnecting in #{@reconnect_timeout}s"
|
89
|
+
|
90
|
+
unless @timer.nil?
|
91
|
+
@timer.cancel
|
92
|
+
@timer = nil
|
93
|
+
end
|
94
|
+
|
95
|
+
@timer = EM::Timer.new(@reconnect_timeout) do
|
96
|
+
@reconnect_timeout *= 2
|
97
|
+
@timer = nil
|
98
|
+
@c = EM.connect @host, @port, @handler, self, *@args
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Check if a connection is writable or not.
|
103
|
+
def writable?
|
104
|
+
not @c.nil? and @open and @c.get_outbound_data_size < @outbound_limit
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# $LICENSE
|
2
|
+
# Copyright 2013-2014 Spotify AB. All rights reserved.
|
3
|
+
#
|
4
|
+
# The contents of this file are licensed under the Apache License, Version 2.0
|
5
|
+
# (the "License"); you may not use this file except in compliance with the
|
6
|
+
# License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
12
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
13
|
+
# License for the specific language governing permissions and limitations under
|
14
|
+
# the License.
|
15
|
+
|
16
|
+
require_relative '../../reporter'
|
17
|
+
|
18
|
+
module FFWD::TCP
|
19
|
+
# A TCP connection implementation that buffers events and metrics in batches
|
20
|
+
# over a time window and calls 'send_all' on the connection.
|
21
|
+
class FlushingConnect
|
22
|
+
include FFWD::Reporter
|
23
|
+
|
24
|
+
setup_reporter :keys => [
|
25
|
+
:dropped_events, :dropped_metrics,
|
26
|
+
:sent_events, :sent_metrics,
|
27
|
+
:failed_events, :failed_metrics,
|
28
|
+
:forced_flush
|
29
|
+
]
|
30
|
+
|
31
|
+
attr_reader :log
|
32
|
+
|
33
|
+
def reporter_meta
|
34
|
+
@c.reporter_meta
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(
|
38
|
+
core, log, connection,
|
39
|
+
flush_period, event_limit, metric_limit, flush_limit
|
40
|
+
)
|
41
|
+
@log = log
|
42
|
+
@c = connection
|
43
|
+
|
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
|
49
|
+
|
50
|
+
@event_buffer = []
|
51
|
+
@metric_buffer = []
|
52
|
+
@timer = nil
|
53
|
+
|
54
|
+
subs = []
|
55
|
+
|
56
|
+
core.starting do
|
57
|
+
log.info "Flushing every #{@flush_period}s"
|
58
|
+
@timer = EM::PeriodicTimer.new(@flush_period){flush!}
|
59
|
+
|
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)
|
64
|
+
|
65
|
+
subs << core.output.event_subscribe(&event_consumer)
|
66
|
+
subs << core.output.metric_subscribe(&metric_consumer)
|
67
|
+
|
68
|
+
@c.connect
|
69
|
+
end
|
70
|
+
|
71
|
+
core.stopping do
|
72
|
+
subs.each(&:unsubscribe).clear
|
73
|
+
|
74
|
+
if @timer
|
75
|
+
@timer.cancel
|
76
|
+
@timer = nil
|
77
|
+
end
|
78
|
+
|
79
|
+
@c.disconnect
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def flush!
|
84
|
+
if @event_buffer.empty? and @metric_buffer.empty?
|
85
|
+
return
|
86
|
+
end
|
87
|
+
|
88
|
+
unless @c.writable?
|
89
|
+
increment :dropped_events, @event_buffer.size
|
90
|
+
increment :dropped_metrics, @metric_buffer.size
|
91
|
+
return
|
92
|
+
end
|
93
|
+
|
94
|
+
@c.send_all @event_buffer, @metric_buffer
|
95
|
+
increment :sent_events, @event_buffer.size
|
96
|
+
increment :sent_metrics, @metric_buffer.size
|
97
|
+
rescue => e
|
98
|
+
log.error "Failed to flush buffers", e
|
99
|
+
|
100
|
+
log.error "The following data could not be flushed:"
|
101
|
+
|
102
|
+
@event_buffer.each_with_index do |event, i|
|
103
|
+
log.error "##{i}: #{event.to_h}"
|
104
|
+
end
|
105
|
+
|
106
|
+
@metric_buffer.each_with_index do |metric, i|
|
107
|
+
log.error "##{i}: #{metric.to_h}"
|
108
|
+
end
|
109
|
+
|
110
|
+
increment :failed_events, @event_buffer.size
|
111
|
+
increment :failed_metrics, @metric_buffer.size
|
112
|
+
ensure
|
113
|
+
@event_buffer.clear
|
114
|
+
@metric_buffer.clear
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def setup_consumer buffer, buffer_limit, flush_limit, statistics_key
|
120
|
+
proc do |e|
|
121
|
+
if buffer.size >= buffer_limit
|
122
|
+
increment statistics_key, 1
|
123
|
+
next
|
124
|
+
end
|
125
|
+
|
126
|
+
buffer << e
|
127
|
+
|
128
|
+
if buffer.size >= flush_limit
|
129
|
+
increment :forced_flush, 1
|
130
|
+
flush!
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|