hermann 0.11-x86-darwin-12 → 0.15-x86-darwin-12
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +17 -23
- data/ext/.ruby-version +1 -0
- data/ext/{extconf.rb → hermann/extconf.rb} +5 -3
- data/ext/hermann/hermann_lib.c +1029 -0
- data/ext/{hermann_lib.h → hermann/hermann_lib.h} +47 -22
- data/lib/hermann/consumer.rb +19 -0
- data/lib/hermann/errors.rb +8 -0
- data/lib/hermann/producer.rb +122 -0
- data/lib/hermann/result.rb +74 -0
- data/lib/hermann/timeout.rb +37 -0
- data/lib/hermann/version.rb +3 -0
- data/lib/hermann.rb +2 -1
- metadata +25 -18
- data/bin/hermann +0 -4
- data/ext/hermann_lib.c +0 -727
- data/ext/hermann_lib.o +0 -0
- data/test/test_hermann.rb +0 -9
@@ -2,16 +2,17 @@
|
|
2
2
|
* hermann_lib.h - Ruby wrapper for the librdkafka library
|
3
3
|
*
|
4
4
|
* Copyright (c) 2014 Stan Campbell
|
5
|
+
* Copyright (c) 2014 Lookout, Inc.
|
5
6
|
* All rights reserved.
|
6
7
|
*
|
7
8
|
* Redistribution and use in source and binary forms, with or without
|
8
9
|
* modification, are permitted provided that the following conditions are met:
|
9
10
|
*
|
10
11
|
* 1. Redistributions of source code must retain the above copyright notice,
|
11
|
-
*
|
12
|
+
* this list of conditions and the following disclaimer.
|
12
13
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
13
|
-
*
|
14
|
-
*
|
14
|
+
* this list of conditions and the following disclaimer in the documentation
|
15
|
+
* and/or other materials provided with the distribution.
|
15
16
|
*
|
16
17
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
17
18
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
@@ -42,8 +43,21 @@
|
|
42
43
|
|
43
44
|
#include <librdkafka/rdkafka.h>
|
44
45
|
|
46
|
+
#ifdef TRACE
|
47
|
+
#define TRACER(...) do { \
|
48
|
+
fprintf(stderr, "%i:%s()> ", __LINE__, __PRETTY_FUNCTION__); \
|
49
|
+
fprintf(stderr, __VA_ARGS__); \
|
50
|
+
fflush(stderr); \
|
51
|
+
} while (0)
|
52
|
+
#else
|
53
|
+
#define TRACER(...) do { } while (0)
|
54
|
+
#endif
|
55
|
+
|
45
56
|
// Holds the defined Ruby module for Hermann
|
46
|
-
static VALUE
|
57
|
+
static VALUE hermann_module;
|
58
|
+
|
59
|
+
#define HERMANN_MAX_ERRSTR_LEN 512
|
60
|
+
#define HERMANN_MAX_TOPIC_LEN 512
|
47
61
|
|
48
62
|
static int DEBUG = 0;
|
49
63
|
|
@@ -57,27 +71,38 @@ static enum {
|
|
57
71
|
} output = OUTPUT_HEXDUMP;
|
58
72
|
|
59
73
|
typedef struct HermannInstanceConfig {
|
74
|
+
char *topic;
|
60
75
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
int64_t start_offset;
|
73
|
-
int do_conf_dump;
|
76
|
+
/* Kafka configuration */
|
77
|
+
rd_kafka_t *rk;
|
78
|
+
rd_kafka_topic_t *rkt;
|
79
|
+
char *brokers;
|
80
|
+
int partition;
|
81
|
+
rd_kafka_topic_conf_t *topic_conf;
|
82
|
+
char errstr[512];
|
83
|
+
rd_kafka_conf_t *conf;
|
84
|
+
const char *debug;
|
85
|
+
int64_t start_offset;
|
86
|
+
int do_conf_dump;
|
74
87
|
|
75
|
-
|
76
|
-
|
77
|
-
|
88
|
+
int run;
|
89
|
+
int exit_eof;
|
90
|
+
int quiet;
|
78
91
|
|
79
|
-
|
92
|
+
int isInitialized;
|
93
|
+
int isConnected;
|
80
94
|
|
95
|
+
int isErrored;
|
96
|
+
char *error;
|
81
97
|
} HermannInstanceConfig;
|
82
98
|
|
83
|
-
|
99
|
+
typedef HermannInstanceConfig hermann_conf_t;
|
100
|
+
|
101
|
+
typedef struct {
|
102
|
+
/* Hermann::Lib::Producer */
|
103
|
+
hermann_conf_t *producer;
|
104
|
+
/* Hermann::Result */
|
105
|
+
VALUE result;
|
106
|
+
} hermann_push_ctx_t;
|
107
|
+
|
108
|
+
#endif
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'hermann'
|
2
|
+
require 'hermann_lib'
|
3
|
+
|
4
|
+
module Hermann
|
5
|
+
class Consumer
|
6
|
+
attr_reader :topic, :brokers, :partition, :internal
|
7
|
+
|
8
|
+
def initialize(topic, brokers, partition)
|
9
|
+
@topic = topic
|
10
|
+
@brokers = brokers
|
11
|
+
@partition = partition
|
12
|
+
@internal = Hermann::Lib::Consumer.new(topic, brokers, partition)
|
13
|
+
end
|
14
|
+
|
15
|
+
def consume(&block)
|
16
|
+
@internal.consume(&block)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'hermann'
|
2
|
+
require 'hermann/result'
|
3
|
+
require 'hermann_lib'
|
4
|
+
|
5
|
+
module Hermann
|
6
|
+
class Producer
|
7
|
+
attr_reader :topic, :brokers, :internal, :children
|
8
|
+
|
9
|
+
def initialize(topic, brokers)
|
10
|
+
@topic = topic
|
11
|
+
@brokers = brokers
|
12
|
+
@internal = Hermann::Lib::Producer.new(topic, brokers)
|
13
|
+
# We're tracking children so we can make sure that at Producer exit we
|
14
|
+
# make a reasonable attempt to clean up outstanding result objects
|
15
|
+
@children = []
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [Boolean] True if our underlying producer object thinks it's
|
19
|
+
# connected to a Kafka broker
|
20
|
+
def connected?
|
21
|
+
return @internal.connected?
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Boolean] True if the underlying producer object has errored
|
25
|
+
def errored?
|
26
|
+
return @internal.errored?
|
27
|
+
end
|
28
|
+
|
29
|
+
def connect(timeout=0)
|
30
|
+
return @internal.connect(timeout * 1000)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Push a value onto the Kafka topic passed to this +Producer+
|
34
|
+
#
|
35
|
+
# @param [Array] value An array of values to push, will push each one
|
36
|
+
# separately
|
37
|
+
# @param [Object] value A single object to push
|
38
|
+
# @return [Hermann::Result] A future-like object which will store the
|
39
|
+
# result from the broker
|
40
|
+
def push(value)
|
41
|
+
result = create_result
|
42
|
+
|
43
|
+
if value.kind_of? Array
|
44
|
+
return value.map { |e| self.push(e) }
|
45
|
+
else
|
46
|
+
@internal.push_single(value, result)
|
47
|
+
end
|
48
|
+
|
49
|
+
return result
|
50
|
+
end
|
51
|
+
|
52
|
+
# Create a +Hermann::Result+ that is tracked in the Producer's children
|
53
|
+
# array
|
54
|
+
#
|
55
|
+
# @return [Hermann::Result] A new, unused, result
|
56
|
+
def create_result
|
57
|
+
@children << Hermann::Result.new(self)
|
58
|
+
return @children.last
|
59
|
+
end
|
60
|
+
|
61
|
+
# Tick the underlying librdkafka reacter and clean up any unreaped but
|
62
|
+
# reapable children results
|
63
|
+
#
|
64
|
+
# @param [FixNum] timeout Seconds to block on the internal reactor
|
65
|
+
# @return [FixNum] Number of +Hermann::Result+ children reaped
|
66
|
+
def tick_reactor(timeout=0)
|
67
|
+
begin
|
68
|
+
execute_tick(rounded_timeout(timeout))
|
69
|
+
rescue StandardError => ex
|
70
|
+
@children.each do |child|
|
71
|
+
# Skip over any children that should already be reaped for other
|
72
|
+
# reasons
|
73
|
+
next if child.reap?
|
74
|
+
# Propagate errors to the remaining children
|
75
|
+
child.internal_set_error(ex)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Reaping the children at this point will also reap any children marked
|
80
|
+
# as errored by an exception out of #execute_tick
|
81
|
+
return reap_children
|
82
|
+
end
|
83
|
+
|
84
|
+
# @return [FixNum] number of children reaped
|
85
|
+
def reap_children
|
86
|
+
# Filter all children who are no longer pending/fulfilled
|
87
|
+
total_children = @children.size
|
88
|
+
@children = @children.reject { |c| c.reap? }
|
89
|
+
|
90
|
+
return (total_children - children.size)
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def rounded_timeout(timeout)
|
97
|
+
# Handle negative numbers, those can be zero
|
98
|
+
return 0 if (timeout < 0)
|
99
|
+
# Since we're going to sleep for each second, round any potential floats
|
100
|
+
# off
|
101
|
+
return timeout.round if timeout.kind_of?(Float)
|
102
|
+
return timeout
|
103
|
+
end
|
104
|
+
|
105
|
+
# Perform the actual reactor tick
|
106
|
+
# @raises [StandardError[ in case of underlying failures in librdkafka
|
107
|
+
def execute_tick(timeout)
|
108
|
+
if timeout == 0
|
109
|
+
@internal.tick(0)
|
110
|
+
else
|
111
|
+
(timeout * 2).times do
|
112
|
+
# We're going to Thread#sleep in Ruby to avoid a
|
113
|
+
# pthread_cond_timedwait(3) inside of librdkafka
|
114
|
+
events = @internal.tick(0)
|
115
|
+
# If we find events, break out early
|
116
|
+
break if events > 0
|
117
|
+
sleep 0.5
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
|
2
|
+
module Hermann
|
3
|
+
class Result
|
4
|
+
attr_reader :reason, :state
|
5
|
+
|
6
|
+
STATES = [:pending,
|
7
|
+
:rejected,
|
8
|
+
:fulfilled,
|
9
|
+
:unfulfilled,
|
10
|
+
].freeze
|
11
|
+
|
12
|
+
def initialize(producer)
|
13
|
+
@producer = producer
|
14
|
+
@reason = nil
|
15
|
+
@value = nil
|
16
|
+
@state = :unfulfilled
|
17
|
+
end
|
18
|
+
|
19
|
+
STATES.each do |state|
|
20
|
+
define_method("#{state}?".to_sym) do
|
21
|
+
return @state == state
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [Boolean] True if this child can be reaped
|
26
|
+
def reap?
|
27
|
+
return true if rejected? || fulfilled?
|
28
|
+
return false
|
29
|
+
end
|
30
|
+
|
31
|
+
# Access the value of the future
|
32
|
+
#
|
33
|
+
# @param [FixNum] timeout Seconds to wait on the underlying machinery for a
|
34
|
+
# result
|
35
|
+
# @return [NilClass] nil if no value could be received in the time alotted
|
36
|
+
# @return [Object]
|
37
|
+
def value(timeout=0)
|
38
|
+
@producer.tick_reactor(timeout)
|
39
|
+
return @value
|
40
|
+
end
|
41
|
+
|
42
|
+
# INTERNAL METHOD ONLY. Do not use
|
43
|
+
#
|
44
|
+
# This method will be invoked by the underlying extension to indicate set
|
45
|
+
# the actual value after a callback has completed
|
46
|
+
#
|
47
|
+
# @param [Object] value The actual resulting value
|
48
|
+
# @param [Boolean] is_error True if the result was errored for whatever
|
49
|
+
# reason
|
50
|
+
def internal_set_value(value, is_error)
|
51
|
+
@value = value
|
52
|
+
|
53
|
+
if is_error
|
54
|
+
puts "Hermann::Result#set_internal_value(#{value.class}:\"#{value}\", error?:#{is_error})"
|
55
|
+
@state = :rejected
|
56
|
+
else
|
57
|
+
@state = :fulfilled
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# INTERNAL METHOD ONLY. Do not use
|
62
|
+
#
|
63
|
+
# This method will set our internal #reason with the details from the
|
64
|
+
# exception
|
65
|
+
#
|
66
|
+
# @param [Exception] exception
|
67
|
+
def internal_set_error(exception)
|
68
|
+
return if exception.nil?
|
69
|
+
|
70
|
+
@reason = exception
|
71
|
+
@state = :rejected
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
begin
|
2
|
+
require 'system_timer'
|
3
|
+
module Hermann
|
4
|
+
class Timeout
|
5
|
+
USE_SYSTEM_TIMER = true
|
6
|
+
end
|
7
|
+
end
|
8
|
+
rescue LoadError
|
9
|
+
require 'timeout'
|
10
|
+
|
11
|
+
if RUBY_VERSION == '1.8.7'
|
12
|
+
puts ">>> You are running on 1.8.7 without SystemTimer"
|
13
|
+
puts ">>> which means Hermann::Timeout will not work as expected"
|
14
|
+
end
|
15
|
+
module Hermann
|
16
|
+
class Timeout
|
17
|
+
USE_SYSTEM_TIMER = false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module Hermann
|
23
|
+
class Timeout
|
24
|
+
def self.system_timer?
|
25
|
+
Hermann::Timeout::USE_SYSTEM_TIMER
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.timeout(seconds, klass=nil, &block)
|
29
|
+
if system_timer?
|
30
|
+
SystemTimer.timeout_after(seconds, klass, &block)
|
31
|
+
else
|
32
|
+
::Timeout.timeout(seconds, klass, &block)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
data/lib/hermann.rb
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
|
1
|
+
module Hermann
|
2
|
+
end
|
metadata
CHANGED
@@ -1,40 +1,47 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hermann
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 21
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: "0.
|
8
|
+
- 15
|
9
|
+
version: "0.15"
|
10
10
|
platform: x86-darwin-12
|
11
11
|
authors:
|
12
12
|
- Stan Campbell
|
13
|
+
- R. Tyler Croy
|
13
14
|
autorequire:
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2014-
|
18
|
+
date: 2014-09-11 00:00:00 Z
|
18
19
|
dependencies: []
|
19
20
|
|
20
|
-
description: Ruby gem wrapper for
|
21
|
-
email:
|
21
|
+
description: Ruby gem wrapper for librdkafka
|
22
|
+
email:
|
23
|
+
- stan.campbell3@gmail.com
|
24
|
+
- rtyler.croy@lookout.com
|
22
25
|
executables: []
|
23
26
|
|
24
27
|
extensions:
|
25
|
-
- ext/extconf.rb
|
28
|
+
- ext/hermann/extconf.rb
|
26
29
|
extra_rdoc_files: []
|
27
30
|
|
28
31
|
files:
|
29
32
|
- Rakefile
|
30
|
-
- ext/hermann_lib.h
|
31
|
-
- ext/hermann_lib.c
|
32
|
-
- ext/extconf.rb
|
33
|
-
- bin/hermann
|
34
33
|
- lib/hermann.rb
|
35
|
-
-
|
36
|
-
-
|
37
|
-
|
34
|
+
- lib/hermann/consumer.rb
|
35
|
+
- lib/hermann/errors.rb
|
36
|
+
- lib/hermann/producer.rb
|
37
|
+
- lib/hermann/result.rb
|
38
|
+
- lib/hermann/timeout.rb
|
39
|
+
- lib/hermann/version.rb
|
40
|
+
- ext/.ruby-version
|
41
|
+
- ext/hermann/extconf.rb
|
42
|
+
- ext/hermann/hermann_lib.c
|
43
|
+
- ext/hermann/hermann_lib.h
|
44
|
+
homepage: https://github.com/lookout/Hermann
|
38
45
|
licenses:
|
39
46
|
- MIT
|
40
47
|
post_install_message:
|
@@ -42,7 +49,7 @@ rdoc_options: []
|
|
42
49
|
|
43
50
|
require_paths:
|
44
51
|
- lib
|
45
|
-
- ext
|
52
|
+
- ext/hermann
|
46
53
|
required_ruby_version: !ruby/object:Gem::Requirement
|
47
54
|
none: false
|
48
55
|
requirements:
|
@@ -67,6 +74,6 @@ rubyforge_project:
|
|
67
74
|
rubygems_version: 1.8.25
|
68
75
|
signing_key:
|
69
76
|
specification_version: 3
|
70
|
-
summary:
|
71
|
-
test_files:
|
72
|
-
|
77
|
+
summary: A Kafka consumer/producer gem based on the librdkafka C library.
|
78
|
+
test_files: []
|
79
|
+
|
data/bin/hermann
DELETED