fluent-plugin-kinesis 0.4.1 → 1.0.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 +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
|