hermann 0.16.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.
@@ -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
+