hermann 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,107 @@
1
+ /*
2
+ * hermann_lib.h - Ruby wrapper for the librdkafka library
3
+ *
4
+ * Copyright (c) 2014 Stan Campbell
5
+ * Copyright (c) 2014 Lookout, Inc.
6
+ * All rights reserved.
7
+ *
8
+ * Redistribution and use in source and binary forms, with or without
9
+ * modification, are permitted provided that the following conditions are met:
10
+ *
11
+ * 1. Redistributions of source code must retain the above copyright notice,
12
+ * this list of conditions and the following disclaimer.
13
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
14
+ * this list of conditions and the following disclaimer in the documentation
15
+ * and/or other materials provided with the distribution.
16
+ *
17
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
+ * POSSIBILITY OF SUCH DAMAGE.
28
+ */
29
+
30
+ #ifndef HERMANN_H
31
+ #define HERMANN_H
32
+
33
+ #include <ruby.h>
34
+
35
+ #include <ctype.h>
36
+ #include <signal.h>
37
+ #include <string.h>
38
+ #include <unistd.h>
39
+ #include <stdlib.h>
40
+ #include <syslog.h>
41
+ #include <sys/time.h>
42
+ #include <errno.h>
43
+
44
+ #include <librdkafka/rdkafka.h>
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
+
56
+ // Holds the defined Ruby module for Hermann
57
+ static VALUE hermann_module;
58
+
59
+ #define HERMANN_MAX_ERRSTR_LEN 512
60
+
61
+ static int DEBUG = 0;
62
+
63
+ // Should we expect rb_thread_blocking_region to be present?
64
+ // #define RB_THREAD_BLOCKING_REGION
65
+ #undef RB_THREAD_BLOCKING_REGION
66
+
67
+ static enum {
68
+ OUTPUT_HEXDUMP,
69
+ OUTPUT_RAW,
70
+ } output = OUTPUT_HEXDUMP;
71
+
72
+ typedef struct HermannInstanceConfig {
73
+ char *topic;
74
+
75
+ /* Kafka configuration */
76
+ rd_kafka_t *rk;
77
+ rd_kafka_topic_t *rkt;
78
+ char *brokers;
79
+ int partition;
80
+ rd_kafka_topic_conf_t *topic_conf;
81
+ char errstr[512];
82
+ rd_kafka_conf_t *conf;
83
+ const char *debug;
84
+ int64_t start_offset;
85
+ int do_conf_dump;
86
+
87
+ int run;
88
+ int exit_eof;
89
+ int quiet;
90
+
91
+ int isInitialized;
92
+ int isConnected;
93
+
94
+ int isErrored;
95
+ char *error;
96
+ } HermannInstanceConfig;
97
+
98
+ typedef HermannInstanceConfig hermann_conf_t;
99
+
100
+ typedef struct {
101
+ /* Hermann::Lib::Producer */
102
+ hermann_conf_t *producer;
103
+ /* Hermann::Result */
104
+ VALUE result;
105
+ } hermann_push_ctx_t;
106
+
107
+ #endif
@@ -0,0 +1,57 @@
1
+ From 888ca33b571d99e877d665235b822f7c961c8fdb Mon Sep 17 00:00:00 2001
2
+ From: "R. Tyler Croy" <tyler@monkeypox.org>
3
+ Date: Thu, 28 Aug 2014 16:24:04 -0700
4
+ Subject: [PATCH 6/8] Update some headers to include the right headers to build
5
+ on FreeBSD
6
+
7
+ ---
8
+ src/rd.h | 9 +++++++++
9
+ src/rdaddr.h | 4 ++++
10
+ 2 files changed, 13 insertions(+)
11
+
12
+ diff --git a/src/rd.h b/src/rd.h
13
+ index c31501e..4789493 100644
14
+ --- a/src/rd.h
15
+ +++ b/src/rd.h
16
+ @@ -37,7 +37,11 @@
17
+ #include <errno.h>
18
+ #include <time.h>
19
+ #include <sys/time.h>
20
+ +
21
+ +#ifndef __FreeBSD__
22
+ +/* alloca(3) is in stdlib on FreeBSD */
23
+ #include <alloca.h>
24
+ +#endif
25
+ #include <assert.h>
26
+ #include <pthread.h>
27
+
28
+ @@ -110,6 +114,11 @@
29
+ # endif
30
+ #endif /* sun */
31
+
32
+ +#ifdef __FreeBSD__
33
+ +/* FreeBSD defines be64toh() in sys/endian.h */
34
+ +#include <sys/endian.h>
35
+ +#endif
36
+ +
37
+ #ifndef be64toh
38
+ #ifndef __APPLE__
39
+ #ifndef sun
40
+ diff --git a/src/rdaddr.h b/src/rdaddr.h
41
+ index 0b37354..e55bd55 100644
42
+ --- a/src/rdaddr.h
43
+ +++ b/src/rdaddr.h
44
+ @@ -32,6 +32,10 @@
45
+ #include <arpa/inet.h>
46
+ #include <netdb.h>
47
+
48
+ +#ifdef __FreeBSD__
49
+ +#include <sys/socket.h>
50
+ +#endif
51
+ +
52
+ /**
53
+ * rd_sockaddr_inx_t is a union for either ipv4 or ipv6 sockaddrs.
54
+ * It provides conveniant abstraction of AF_INET* agnostic operations.
55
+ --
56
+ 1.9.0
57
+
@@ -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,8 @@
1
+
2
+ module Hermann
3
+ module Errors
4
+ # Error for connectivity problems with the Kafka brokers
5
+ class ConnectivityError; end;
6
+ end
7
+ end
8
+
@@ -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
+
@@ -0,0 +1,3 @@
1
+ module Hermann
2
+ VERSION = '0.16.0'
3
+ end
data/lib/hermann.rb ADDED
@@ -0,0 +1,2 @@
1
+ module Hermann
2
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hermann
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.16.0
5
+ platform: ruby
6
+ authors:
7
+ - Stan Campbell
8
+ - R. Tyler Croy
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2014-09-12 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: mini_portile
17
+ requirement: &id001 !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.6.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *id001
25
+ description: Ruby gem wrapper for librdkafka
26
+ email:
27
+ - stan.campbell3@gmail.com
28
+ - rtyler.croy@lookout.com
29
+ executables: []
30
+
31
+ extensions:
32
+ - ext/hermann/extconf.rb
33
+ extra_rdoc_files: []
34
+
35
+ files:
36
+ - Rakefile
37
+ - ext/hermann/extconf.rb
38
+ - ext/hermann/hermann_lib.c
39
+ - ext/hermann/hermann_lib.h
40
+ - ext/patches/librdkafka/0006-Update-some-headers-to-include-the-right-headers-to-.patch
41
+ - lib/hermann.rb
42
+ - lib/hermann/consumer.rb
43
+ - lib/hermann/errors.rb
44
+ - lib/hermann/producer.rb
45
+ - lib/hermann/result.rb
46
+ - lib/hermann/timeout.rb
47
+ - lib/hermann/version.rb
48
+ homepage: https://github.com/lookout/Hermann
49
+ licenses:
50
+ - MIT
51
+ metadata: {}
52
+
53
+ post_install_message:
54
+ rdoc_options: []
55
+
56
+ require_paths:
57
+ - lib
58
+ - ext/hermann
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - &id002
62
+ - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - *id002
68
+ requirements: []
69
+
70
+ rubyforge_project:
71
+ rubygems_version: 2.4.1
72
+ signing_key:
73
+ specification_version: 3
74
+ summary: A Kafka consumer/producer gem based on the librdkafka C library.
75
+ test_files: []
76
+