fluent-plugin-kinesis 0.4.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +13 -18
- data/.travis.yml +9 -9
- data/CHANGELOG.md +9 -0
- data/CONTRIBUTORS.txt +1 -1
- data/Gemfile +12 -9
- data/LICENSE.txt +39 -201
- data/Makefile +40 -0
- data/NOTICE.txt +1 -1
- data/README-v0.4.md +348 -0
- data/README.md +398 -183
- data/Rakefile +20 -14
- data/benchmark/dummer.conf +13 -0
- data/benchmark/firehose.conf +24 -0
- data/benchmark/producer.conf +28 -0
- data/benchmark/streams.conf +24 -0
- data/fluent-plugin-kinesis.gemspec +34 -23
- data/gemfiles/Gemfile.fluentd-0.10.58 +20 -0
- data/lib/fluent/plugin/kinesis_helper.rb +30 -0
- data/lib/fluent/plugin/kinesis_helper/api.rb +164 -0
- data/lib/fluent/plugin/kinesis_helper/class_methods.rb +120 -0
- data/lib/fluent/plugin/kinesis_helper/client.rb +36 -0
- data/lib/fluent/plugin/kinesis_helper/credentials.rb +51 -0
- data/lib/fluent/plugin/kinesis_helper/error.rb +38 -0
- data/lib/fluent/plugin/kinesis_helper/format.rb +85 -0
- data/lib/fluent/plugin/kinesis_helper/initialize.rb +58 -0
- data/lib/fluent/plugin/kinesis_helper/kpl.rb +81 -0
- data/lib/fluent/plugin/out_kinesis.rb +13 -11
- data/lib/fluent/plugin/out_kinesis_firehose.rb +44 -0
- data/lib/fluent/plugin/out_kinesis_producer.rb +38 -0
- data/lib/fluent/plugin/out_kinesis_streams.rb +47 -0
- data/lib/fluent/plugin/patched_detach_process_impl.rb +103 -0
- data/lib/fluent_plugin_kinesis/version.rb +17 -0
- data/lib/kinesis_producer.rb +24 -0
- data/lib/kinesis_producer/binary.rb +10 -0
- data/lib/kinesis_producer/daemon.rb +238 -0
- data/lib/kinesis_producer/library.rb +122 -0
- data/lib/kinesis_producer/protobuf/config.pb.rb +66 -0
- data/lib/kinesis_producer/protobuf/messages.pb.rb +151 -0
- data/lib/kinesis_producer/tasks/binary.rake +73 -0
- metadata +196 -36
- data/lib/fluent/plugin/version.rb +0 -16
- data/test/helper.rb +0 -32
- data/test/plugin/test_out_kinesis.rb +0 -641
@@ -0,0 +1,47 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2014-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Amazon Software License (the "License").
|
5
|
+
# You may not use this file except in compliance with the License.
|
6
|
+
# A copy of the License is located at
|
7
|
+
#
|
8
|
+
# http://aws.amazon.com/asl/
|
9
|
+
#
|
10
|
+
# or in the "license" file accompanying this file. This file is distributed
|
11
|
+
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
12
|
+
# express or implied. See the License for the specific language governing
|
13
|
+
# permissions and limitations under the License.
|
14
|
+
|
15
|
+
require 'fluent/plugin/kinesis_helper'
|
16
|
+
|
17
|
+
module Fluent
|
18
|
+
class KinesisStreamsOutput < BufferedOutput
|
19
|
+
include KinesisHelper
|
20
|
+
Fluent::Plugin.register_output('kinesis_streams', self)
|
21
|
+
config_param_for_streams
|
22
|
+
|
23
|
+
def write(chunk)
|
24
|
+
records = convert_to_records(chunk)
|
25
|
+
split_to_batches(records).each do |batch|
|
26
|
+
batch_request_with_retry(batch)
|
27
|
+
end
|
28
|
+
log.debug("Written #{records.size} records")
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def convert_format(tag, time, record)
|
34
|
+
{
|
35
|
+
data: data_format(tag, time, record),
|
36
|
+
partition_key: key(record),
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def batch_request(batch)
|
41
|
+
client.put_records(
|
42
|
+
stream_name: @stream_name,
|
43
|
+
records: batch,
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2014-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Amazon Software License (the "License").
|
5
|
+
# You may not use this file except in compliance with the License.
|
6
|
+
# A copy of the License is located at
|
7
|
+
#
|
8
|
+
# http://aws.amazon.com/asl/
|
9
|
+
#
|
10
|
+
# or in the "license" file accompanying this file. This file is distributed
|
11
|
+
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
12
|
+
# express or implied. See the License for the specific language governing
|
13
|
+
# permissions and limitations under the License.
|
14
|
+
|
15
|
+
module Fluent
|
16
|
+
module PatchedDetachProcessImpl
|
17
|
+
def on_detach_process(i)
|
18
|
+
end
|
19
|
+
|
20
|
+
def on_exit_process(i)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def detach_process_impl(num, &block)
|
26
|
+
children = []
|
27
|
+
|
28
|
+
num.times do |i|
|
29
|
+
pid, forward_thread = DetachProcessManager.instance.fork(self)
|
30
|
+
|
31
|
+
if pid
|
32
|
+
# parent process
|
33
|
+
$log.info "detached process", :class=>self.class, :pid=>pid
|
34
|
+
children << [pid, forward_thread]
|
35
|
+
next
|
36
|
+
end
|
37
|
+
|
38
|
+
# child process
|
39
|
+
begin
|
40
|
+
on_detach_process(i)
|
41
|
+
|
42
|
+
block.call
|
43
|
+
|
44
|
+
# disable Engine.stop called by signal handler
|
45
|
+
Engine.define_singleton_method(:stop) do
|
46
|
+
# do nothing
|
47
|
+
end
|
48
|
+
# override signal handlers called by parent process
|
49
|
+
fin = ::Fluent::DetachProcessImpl::FinishWait.new
|
50
|
+
trap :INT do
|
51
|
+
fin.stop
|
52
|
+
end
|
53
|
+
trap :TERM do
|
54
|
+
fin.stop
|
55
|
+
end
|
56
|
+
#forward_thread.join # TODO this thread won't stop because parent doesn't close pipe
|
57
|
+
fin.wait
|
58
|
+
|
59
|
+
on_exit_process(i)
|
60
|
+
exit! 0
|
61
|
+
ensure
|
62
|
+
$log.error "unknown error while shutting down this child process", :error=>$!.to_s, :pid=>Process.pid
|
63
|
+
$log.error_backtrace
|
64
|
+
end
|
65
|
+
|
66
|
+
exit! 1
|
67
|
+
end
|
68
|
+
|
69
|
+
# parent process
|
70
|
+
# override shutdown method to kill child processes
|
71
|
+
define_singleton_method(:shutdown) do
|
72
|
+
children.each {|pair|
|
73
|
+
begin
|
74
|
+
pid = pair[0]
|
75
|
+
forward_thread = pair[1]
|
76
|
+
if pid
|
77
|
+
Process.kill(:TERM, pid)
|
78
|
+
forward_thread.join # wait until child closes pipe
|
79
|
+
Process.waitpid(pid)
|
80
|
+
pair[0] = nil
|
81
|
+
end
|
82
|
+
rescue
|
83
|
+
$log.error "unknown error while shutting down remote child process", :error=>$!.to_s
|
84
|
+
$log.error_backtrace
|
85
|
+
end
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
# override target.emit and write event stream to the pipe
|
90
|
+
forwarders = children.map {|pair| pair[1].forwarder }
|
91
|
+
if forwarders.length > 1
|
92
|
+
# use roundrobin
|
93
|
+
fwd = DetachProcessManager::MultiForwarder.new(forwarders)
|
94
|
+
else
|
95
|
+
fwd = forwarders[0]
|
96
|
+
end
|
97
|
+
define_singleton_method(:emit) do |tag,es,chain|
|
98
|
+
chain.next
|
99
|
+
fwd.emit(tag, es)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2014-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Amazon Software License (the "License").
|
5
|
+
# You may not use this file except in compliance with the License.
|
6
|
+
# A copy of the License is located at
|
7
|
+
#
|
8
|
+
# http://aws.amazon.com/asl/
|
9
|
+
#
|
10
|
+
# or in the "license" file accompanying this file. This file is distributed
|
11
|
+
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
12
|
+
# express or implied. See the License for the specific language governing
|
13
|
+
# permissions and limitations under the License.
|
14
|
+
|
15
|
+
module FluentPluginKinesis
|
16
|
+
VERSION = '1.0.0'
|
17
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2014-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Amazon Software License (the "License").
|
5
|
+
# You may not use this file except in compliance with the License.
|
6
|
+
# A copy of the License is located at
|
7
|
+
#
|
8
|
+
# http://aws.amazon.com/asl/
|
9
|
+
#
|
10
|
+
# or in the "license" file accompanying this file. This file is distributed
|
11
|
+
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
12
|
+
# express or implied. See the License for the specific language governing
|
13
|
+
# permissions and limitations under the License.
|
14
|
+
|
15
|
+
require 'protobuf'
|
16
|
+
require 'kinesis_producer/binary'
|
17
|
+
require 'kinesis_producer/library'
|
18
|
+
require 'kinesis_producer/daemon'
|
19
|
+
require 'kinesis_producer/protobuf/config.pb'
|
20
|
+
require 'kinesis_producer/protobuf/messages.pb'
|
21
|
+
|
22
|
+
module KinesisProducer
|
23
|
+
ConfigurationFields = Protobuf::Configuration.all_fields.reject(&:repeated?)
|
24
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module KinesisProducer
|
2
|
+
class Binary
|
3
|
+
Dir = 'amazon-kinesis-producer-native-binaries'
|
4
|
+
Files = {
|
5
|
+
'linux' => File.join(Dir, 'linux', 'kinesis_producer'),
|
6
|
+
'osx' => File.join(Dir, 'osx', 'kinesis_producer'),
|
7
|
+
'windows' => File.join(Dir, 'windows', 'kinesis_producer.exe'),
|
8
|
+
}
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,238 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2014-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Amazon Software License (the "License").
|
5
|
+
# You may not use this file except in compliance with the License.
|
6
|
+
# A copy of the License is located at
|
7
|
+
#
|
8
|
+
# http://aws.amazon.com/asl/
|
9
|
+
#
|
10
|
+
# or in the "license" file accompanying this file. This file is distributed
|
11
|
+
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
12
|
+
# express or implied. See the License for the specific language governing
|
13
|
+
# permissions and limitations under the License.
|
14
|
+
|
15
|
+
require 'tempfile'
|
16
|
+
require 'concurrent'
|
17
|
+
|
18
|
+
module KinesisProducer
|
19
|
+
class Daemon
|
20
|
+
FixnumMax = (2 ** (64 - 2)) - 1
|
21
|
+
|
22
|
+
def initialize(binary, handler, options)
|
23
|
+
@binary = binary
|
24
|
+
@handler = handler
|
25
|
+
|
26
|
+
@configuration = options[:configuration] || {}
|
27
|
+
@credentials = options[:credentials]
|
28
|
+
@metrics_credentials = options[:metrics_credentials]
|
29
|
+
@credentials_refresh_delay = options[:credentials_refresh_delay] || 5000
|
30
|
+
@logger = options[:logger]
|
31
|
+
@debug = options[:debug]
|
32
|
+
|
33
|
+
@executor = Concurrent::CachedThreadPool.new
|
34
|
+
@shutdown = Concurrent::AtomicBoolean.new(false)
|
35
|
+
@outgoing_messages = Queue.new
|
36
|
+
@incoming_messages = Queue.new
|
37
|
+
|
38
|
+
if debug?
|
39
|
+
@meters = {
|
40
|
+
add_message: Meter.new,
|
41
|
+
send_message: Meter.new,
|
42
|
+
receive_message: Meter.new,
|
43
|
+
return_message: Meter.new,
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def start
|
49
|
+
@executor.post do
|
50
|
+
create_pipes
|
51
|
+
start_child
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def destroy
|
56
|
+
@shutdown.make_true
|
57
|
+
if @pid
|
58
|
+
Process.kill("TERM", @pid)
|
59
|
+
Process.waitpid(@pid)
|
60
|
+
sleep 1 # TODO
|
61
|
+
end
|
62
|
+
delete_pipes
|
63
|
+
end
|
64
|
+
|
65
|
+
def add(message)
|
66
|
+
@outgoing_messages.push(message)
|
67
|
+
@meters[:add_message].mark if debug?
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def create_pipes
|
73
|
+
@in_pipe = temp_pathname('amz-aws-kpl-in-pipe-')
|
74
|
+
@out_pipe = temp_pathname('amz-aws-kpl-out-pipe-')
|
75
|
+
system("mkfifo", @in_pipe.to_path, @out_pipe.to_path)
|
76
|
+
sleep 1 # TODO
|
77
|
+
end
|
78
|
+
|
79
|
+
def delete_pipes
|
80
|
+
@in_channel.close unless @in_channel.nil?
|
81
|
+
@out_channel.close unless @out_channel.nil?
|
82
|
+
@in_pipe.unlink
|
83
|
+
@out_pipe.unlink
|
84
|
+
rescue Errno::ENOENT
|
85
|
+
end
|
86
|
+
|
87
|
+
def temp_pathname(basename)
|
88
|
+
tempfile = Tempfile.new(basename)
|
89
|
+
ObjectSpace.undefine_finalizer(tempfile)
|
90
|
+
file = tempfile.path
|
91
|
+
File.delete(file)
|
92
|
+
Pathname.new(file)
|
93
|
+
end
|
94
|
+
|
95
|
+
def start_child
|
96
|
+
start_child_daemon
|
97
|
+
connect_to_child
|
98
|
+
start_loops
|
99
|
+
end
|
100
|
+
|
101
|
+
def start_child_daemon
|
102
|
+
@pid = Process.fork do
|
103
|
+
Process.setsid
|
104
|
+
configuration = make_configuration_message
|
105
|
+
credentials = make_set_credentials_message
|
106
|
+
command = [@binary, @out_pipe.to_path, @in_pipe.to_path, to_hex(configuration), to_hex(credentials)]
|
107
|
+
if @metrics_credentials
|
108
|
+
metrics_credentials = make_set_metrics_credentials_message
|
109
|
+
command.push(to_hex(metrics_credentials))
|
110
|
+
end
|
111
|
+
exec(*command)
|
112
|
+
end
|
113
|
+
sleep 1 # TODO
|
114
|
+
end
|
115
|
+
|
116
|
+
def connect_to_child
|
117
|
+
@in_channel = @in_pipe.open('r')
|
118
|
+
@out_channel = @out_pipe.open('w')
|
119
|
+
end
|
120
|
+
|
121
|
+
def start_loops
|
122
|
+
start_loop_for(:send_message)
|
123
|
+
start_loop_for(:receive_message)
|
124
|
+
start_loop_for(:return_message)
|
125
|
+
start_loop_for(:update_credentials)
|
126
|
+
start_loop_for(:tick) if debug?
|
127
|
+
end
|
128
|
+
|
129
|
+
def start_loop_for(method)
|
130
|
+
@executor.post do
|
131
|
+
while @shutdown.false?
|
132
|
+
send(method)
|
133
|
+
@meters[method].mark if debug? and @meters.include?(method)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def send_message
|
139
|
+
message = @outgoing_messages.pop
|
140
|
+
size = [message.size].pack('N*')
|
141
|
+
@out_channel.write(size)
|
142
|
+
@out_channel.write(message)
|
143
|
+
@out_channel.flush
|
144
|
+
end
|
145
|
+
|
146
|
+
def receive_message
|
147
|
+
size = @in_channel.read(4)
|
148
|
+
data = @in_channel.read(size.unpack('N*').first)
|
149
|
+
@incoming_messages.push(data)
|
150
|
+
end
|
151
|
+
|
152
|
+
def return_message
|
153
|
+
data = @incoming_messages.pop
|
154
|
+
message = KinesisProducer::Protobuf::Message.decode(data)
|
155
|
+
@handler.on_message(message)
|
156
|
+
end
|
157
|
+
|
158
|
+
def update_credentials
|
159
|
+
add(make_set_credentials_message)
|
160
|
+
add(make_set_metrics_credentials_message) if @metrics_credentials
|
161
|
+
sleep @credentials_refresh_delay
|
162
|
+
end
|
163
|
+
|
164
|
+
def make_configuration_message
|
165
|
+
configuration = @configuration
|
166
|
+
KinesisProducer::ConfigurationFields.each do |field|
|
167
|
+
if configuration[field.name].nil?
|
168
|
+
configuration[field.name] = field.default_value
|
169
|
+
end
|
170
|
+
end
|
171
|
+
config = KinesisProducer::Protobuf::Configuration.new(configuration)
|
172
|
+
make_message(0, :configuration, config)
|
173
|
+
end
|
174
|
+
|
175
|
+
def make_set_credentials_message
|
176
|
+
make_set_credential_message(@credentials)
|
177
|
+
end
|
178
|
+
|
179
|
+
def make_set_metrics_credentials_message
|
180
|
+
make_set_credential_message(@metrics_credentials, true)
|
181
|
+
end
|
182
|
+
|
183
|
+
def make_set_credential_message(credentials, for_metrics = false)
|
184
|
+
return nil if credentials.nil?
|
185
|
+
cred = KinesisProducer::Protobuf::Credentials.new(
|
186
|
+
akid: credentials.access_key_id,
|
187
|
+
secret_key: credentials.secret_access_key,
|
188
|
+
token: credentials.session_token
|
189
|
+
)
|
190
|
+
set_credentials = KinesisProducer::Protobuf::SetCredentials.new(credentials: cred, for_metrics: for_metrics)
|
191
|
+
make_message(FixnumMax, :set_credentials, set_credentials)
|
192
|
+
end
|
193
|
+
|
194
|
+
def make_message(id, target, value)
|
195
|
+
KinesisProducer::Protobuf::Message.new(id: id, target => value).encode
|
196
|
+
end
|
197
|
+
|
198
|
+
def to_hex(message)
|
199
|
+
message.unpack('H*').first
|
200
|
+
end
|
201
|
+
|
202
|
+
def tick
|
203
|
+
out = @meters.each_value.map do |meter|
|
204
|
+
sprintf("%5d", meter.tick)
|
205
|
+
end
|
206
|
+
@logger.debug("[#{Thread.current.object_id}] "+out.join(" ")) if debug?
|
207
|
+
sleep 1
|
208
|
+
end
|
209
|
+
|
210
|
+
def debug?
|
211
|
+
@debug
|
212
|
+
end
|
213
|
+
|
214
|
+
class Meter
|
215
|
+
def initialize
|
216
|
+
@count = Concurrent::AtomicFixnum.new(0)
|
217
|
+
@previous_tick_time = Time.now.to_f
|
218
|
+
@current_rate = 0.0
|
219
|
+
tick
|
220
|
+
end
|
221
|
+
|
222
|
+
def mark(count = 1)
|
223
|
+
@count.increment(count)
|
224
|
+
end
|
225
|
+
|
226
|
+
def tick
|
227
|
+
@current_rate = @count.value.to_f / (Time.now.to_f - @previous_tick_time)
|
228
|
+
@count.value = 0
|
229
|
+
@previous_tick_time = Time.now.to_f
|
230
|
+
current_rate
|
231
|
+
end
|
232
|
+
|
233
|
+
def current_rate
|
234
|
+
@current_rate
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2014-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Amazon Software License (the "License").
|
5
|
+
# You may not use this file except in compliance with the License.
|
6
|
+
# A copy of the License is located at
|
7
|
+
#
|
8
|
+
# http://aws.amazon.com/asl/
|
9
|
+
#
|
10
|
+
# or in the "license" file accompanying this file. This file is distributed
|
11
|
+
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
12
|
+
# express or implied. See the License for the specific language governing
|
13
|
+
# permissions and limitations under the License.
|
14
|
+
|
15
|
+
require 'concurrent'
|
16
|
+
require 'os'
|
17
|
+
|
18
|
+
module KinesisProducer
|
19
|
+
class Library
|
20
|
+
class Handler
|
21
|
+
def initialize(futures)
|
22
|
+
@futures = futures
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_message(message)
|
26
|
+
if !message.put_record_result.nil?
|
27
|
+
on_put_record_result(message)
|
28
|
+
elsif !message.metrics_response.nil?
|
29
|
+
on_metrics_response(message)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def on_put_record_result(message)
|
34
|
+
source_id = message.source_id
|
35
|
+
result = message.put_record_result
|
36
|
+
f = @futures.fetch(source_id)
|
37
|
+
@futures.delete(source_id)
|
38
|
+
if result.success
|
39
|
+
f.set(result)
|
40
|
+
else
|
41
|
+
f.fail(StandardError.new(result.to_hash))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def on_metrics_response(message)
|
46
|
+
source_id = message.source_id
|
47
|
+
response = message.metrics_response
|
48
|
+
f = @futures.fetch(source_id)
|
49
|
+
@futures.delete(source_id)
|
50
|
+
f.set(response.metrics)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class << self
|
55
|
+
def binary
|
56
|
+
case
|
57
|
+
when OS.linux?; Binary::Files['linux']
|
58
|
+
when OS.osx?; Binary::Files['osx']
|
59
|
+
when OS.windows?; Binary::Files['windows']
|
60
|
+
else; raise
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def default_binary_path
|
65
|
+
root_dir = File.expand_path('../../..', __FILE__)
|
66
|
+
File.join(root_dir, binary)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def initialize(options)
|
71
|
+
@binary_path = options.delete(:binary_path) || self.class.default_binary_path
|
72
|
+
@message_id = Concurrent::AtomicFixnum.new(1)
|
73
|
+
@futures = Concurrent::Map.new
|
74
|
+
@child = Daemon.new(@binary_path, Handler.new(@futures), options)
|
75
|
+
@child.start
|
76
|
+
end
|
77
|
+
|
78
|
+
def destroy
|
79
|
+
flush_sync
|
80
|
+
@child.destroy
|
81
|
+
end
|
82
|
+
|
83
|
+
def put_record(options)
|
84
|
+
f = Concurrent::IVar.new
|
85
|
+
id = add_message(:put_record, KinesisProducer::Protobuf::PutRecord.new(options))
|
86
|
+
@futures[id] = f
|
87
|
+
f
|
88
|
+
end
|
89
|
+
|
90
|
+
def get_metrics(options = {})
|
91
|
+
f = Concurrent::IVar.new
|
92
|
+
id = add_message(:metrics_request, KinesisProducer::Protobuf::MetricsRequest.new(options))
|
93
|
+
@futures[id] = f
|
94
|
+
f.wait
|
95
|
+
f.value
|
96
|
+
end
|
97
|
+
|
98
|
+
def flush(options = {})
|
99
|
+
add_message(:flush, KinesisProducer::Protobuf::Flush.new(options))
|
100
|
+
end
|
101
|
+
|
102
|
+
def flush_sync
|
103
|
+
while @futures.size > 0
|
104
|
+
flush()
|
105
|
+
sleep 0.5
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def add_message(target, value)
|
112
|
+
id = get_and_inc_message_id
|
113
|
+
message = KinesisProducer::Protobuf::Message.new(id: id, target => value)
|
114
|
+
@child.add(message.encode)
|
115
|
+
id
|
116
|
+
end
|
117
|
+
|
118
|
+
def get_and_inc_message_id
|
119
|
+
@message_id.increment
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|