hermann 0.11-x86-darwin-12 → 0.15-x86-darwin-12
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.
- 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