racecar 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 92c4f52171961fdbf4dcf6db7ed265eec6a729dd
4
- data.tar.gz: 8bb0fc336b839f91da037cc7046dc64fa699acdd
3
+ metadata.gz: 63f11732ede854b710bf164d64cd1815e1edf588
4
+ data.tar.gz: 2f4451111982eaa874334ae309c8e6063fbd6bea
5
5
  SHA512:
6
- metadata.gz: 180c8bc2e058b7626b1f2d3f277d176c12b75b27191222ae40f80cb893142037132b8f469c5d693482be317e554453019b09c2ff941bbf5fc88665825dc9c2b4
7
- data.tar.gz: d19f91361e5d5a59914c74fa89e4d574792dbeb0e6c27cc1ddc153bd463dc23498ed8b0d9d76e63d1b525058af0eaad16cc75aab58f3926c6a825bb3fb3ca1c8
6
+ metadata.gz: 2ccc72f3e1e5e76f4c16f20451a281cd7640c4a643a75dea4c4100e41e071433a2a3c297cc06bfcb8fdcf066a22b20e8856176d2a238bb79ca241b42bbd8fd1f
7
+ data.tar.gz: b9a03db77deaae0e50e17822931ce0e46b1f499ce80b9edfaee82bd334115cc226041f9a138bc87266ade1b29fbcf5f2e97d1b12fbefce432b8e236aaa0bf3c5
data/README.md CHANGED
@@ -116,13 +116,48 @@ The possible configuration keys are:
116
116
  * `offset_commit_interval` (_optional_) – How often to save the consumer's position in Kafka.
117
117
  * `heartbeat_interval` (_optional_) – How often to send a heartbeat message to Kafka.
118
118
  * `pause_timeout` (_optional_) – How long to pause a partition for if the consumer raises an exception while processing a message.
119
- * `connect_timeout` (_optional_) – How long to wait when trying to connect to a Kafka broker.
120
- * `socket_timeout` (_optional_) – How long to wait when trying to communicate with a Kafka broker.
119
+ * `connect_timeout` (_optional_) – How long to wait when trying to connect to a Kafka broker. Default is 10 seconds.
120
+ * `socket_timeout` (_optional_) – How long to wait when trying to communicate with a Kafka broker. Default is 30 seconds.
121
+ * `max_wait_time` (_optional_) – How long to allow the Kafka brokers to wait before returning messages. A higher number means larger batches, at the cost of higher latency. Default is 5 seconds.
121
122
 
122
123
  Note that many of these configuration keys correspond directly with similarly named concepts in [ruby-kafka](https://github.com/zendesk/ruby-kafka) for more details on low-level operations, read that project's documentation.
123
124
 
124
125
  It's also possible to configure Racecar using environment variables. For any given configuration key, there should be a corresponding environment variable with the prefix `RACECAR_`, in upper case. For instance, in order to configure the client id, set `RACECAR_CLIENT_ID=some-id` in the process in which the Racecar consumer is launched. You can set `brokers` by passing a comma-separated list, e.g. `RACECAR_BROKERS=kafka1:9092,kafka2:9092,kafka3:9092`.
125
126
 
127
+ ### Testing consumers
128
+
129
+ Since consumers are merely classes that implement a simple interface, they're dead simple to test.
130
+
131
+ Here's an example of testing a consumer class using [RSpec](http://rspec.info/) and Rails:
132
+
133
+ ```ruby
134
+ # app/consumers/create_contacts_consumer.rb
135
+ #
136
+ # Creates a Contact whenever an email address is written to the
137
+ # `email-addresses` topic.
138
+ class CreateContactsConsumer < Racecar::Consumer
139
+ subscribes_to "email-addresses"
140
+
141
+ def process(message)
142
+ email = message.value
143
+
144
+ Contact.create!(email: email)
145
+ end
146
+ end
147
+
148
+ # spec/consumers/create_contacts_consumer_spec.rb
149
+ describe CreateContactsConsumer do
150
+ it "creates a Contact for each email address in the topic" do
151
+ message = double("message", value: "john@example.com")
152
+ consumer = CreateContactsConsumer.new
153
+
154
+ consumer.process(message)
155
+
156
+ expect(Contact.where(email: "john@example.com")).to exist
157
+ end
158
+ end
159
+ ```
160
+
126
161
  ## Development
127
162
 
128
163
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -3,4 +3,6 @@
3
3
  require "racecar"
4
4
  require "racecar/cli"
5
5
 
6
+ $LOAD_PATH.unshift(Dir.pwd)
7
+
6
8
  Racecar::Cli.main(ARGV)
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "racecar"
4
+ require "racecar/ctl"
5
+
6
+ Racecar::Ctl.main(ARGV)
@@ -1,4 +1,5 @@
1
1
  require "optparse"
2
+ require "racecar/config_loader"
2
3
 
3
4
  module Racecar
4
5
  module Cli
@@ -20,31 +21,10 @@ module Racecar
20
21
  parser.parse!(args)
21
22
 
22
23
  consumer_name = args.first or raise Racecar::Error, "no consumer specified"
23
- config_file = "config/racecar.yml"
24
24
 
25
25
  puts "=> Starting Racecar consumer #{consumer_name}..."
26
26
 
27
- begin
28
- require "rails"
29
-
30
- puts "=> Detected Rails, booting application..."
31
-
32
- require "./config/environment"
33
-
34
- Racecar.config.load_file(config_file, Rails.env)
35
-
36
- if Racecar.config.log_to_stdout
37
- # Write to STDOUT as well as to the log file.
38
- console = ActiveSupport::Logger.new($stdout)
39
- console.formatter = Rails.logger.formatter
40
- console.level = Rails.logger.level
41
- Rails.logger.extend(ActiveSupport::Logger.broadcast(console))
42
- end
43
-
44
- Racecar.logger = Rails.logger
45
- rescue LoadError
46
- # Not a Rails application.
47
- end
27
+ ConfigLoader.load!
48
28
 
49
29
  # Find the consumer class by name.
50
30
  consumer_class = Kernel.const_get(consumer_name)
@@ -45,10 +45,21 @@ module Racecar
45
45
  # Default is to not pause partitions on processing errors.
46
46
  pause_timeout: 0,
47
47
 
48
- connect_timeout: nil,
49
- socket_timeout: nil,
50
- error_handler: proc {},
48
+ # Default is to allow at most 10 seconds when connecting to a broker.
49
+ connect_timeout: 10,
50
+
51
+ # Default is to allow at most 30 seconds when reading or writing to
52
+ # a broker socket.
53
+ socket_timeout: 30,
54
+
55
+ # Default is to allow the brokers up to 5 seconds before returning
56
+ # messages.
51
57
  max_wait_time: 5,
58
+
59
+ # Default is to do nothing on exceptions.
60
+ error_handler: proc {},
61
+
62
+ # Default is to only log to the logger.
52
63
  log_to_stdout: false,
53
64
  }
54
65
 
@@ -65,6 +76,14 @@ module Racecar
65
76
  raise ConfigError, "required configuration key `#{key}` not defined"
66
77
  end
67
78
  end
79
+
80
+ if socket_timeout <= max_wait_time
81
+ raise ConfigError, "`socket_timeout` must be longer than `max_wait_time`"
82
+ end
83
+
84
+ if connect_timeout <= max_wait_time
85
+ raise ConfigError, "`connect_timeout` must be longer than `max_wait_time`"
86
+ end
68
87
  end
69
88
 
70
89
  def load_file(path, environment)
@@ -0,0 +1,29 @@
1
+ module Racecar
2
+ module ConfigLoader
3
+ def self.load!
4
+ config_file = "config/racecar.yml"
5
+
6
+ begin
7
+ require "rails"
8
+
9
+ puts "=> Detected Rails, booting application..."
10
+
11
+ require "./config/environment"
12
+
13
+ Racecar.config.load_file(config_file, Rails.env)
14
+
15
+ if Racecar.config.log_to_stdout
16
+ # Write to STDOUT as well as to the log file.
17
+ console = ActiveSupport::Logger.new($stdout)
18
+ console.formatter = Rails.logger.formatter
19
+ console.level = Rails.logger.level
20
+ Rails.logger.extend(ActiveSupport::Logger.broadcast(console))
21
+ end
22
+
23
+ Racecar.logger = Rails.logger
24
+ rescue LoadError
25
+ # Not a Rails application.
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,6 +1,6 @@
1
1
  module Racecar
2
2
  class Consumer
3
- Subscription = Struct.new(:topic, :start_from_beginning)
3
+ Subscription = Struct.new(:topic, :start_from_beginning, :max_bytes_per_partition)
4
4
 
5
5
  class << self
6
6
  attr_accessor :max_wait_time
@@ -11,9 +11,14 @@ module Racecar
11
11
  end
12
12
 
13
13
  # Adds one or more topic subscriptions.
14
- def subscribes_to(*topics, start_from_beginning: true)
14
+ #
15
+ # start_from_beginning - whether to start from the beginning or the end of each
16
+ # partition.
17
+ # max_bytes_per_partition - the maximum number of bytes to fetch from each partition
18
+ # at a time.
19
+ def subscribes_to(*topics, start_from_beginning: true, max_bytes_per_partition: 1048576)
15
20
  topics.each do |topic|
16
- subscriptions << Subscription.new(topic, start_from_beginning)
21
+ subscriptions << Subscription.new(topic, start_from_beginning, max_bytes_per_partition)
17
22
  end
18
23
  end
19
24
  end
@@ -0,0 +1,58 @@
1
+ require "optparse"
2
+ require "racecar/config_loader"
3
+
4
+ module Racecar
5
+ class Ctl
6
+ ProduceMessage = Struct.new(:value, :key, :topic)
7
+
8
+ def self.main(args)
9
+ command = args.shift or raise Racecar::Error, "no command specified"
10
+
11
+ ctl = new
12
+
13
+ if ctl.respond_to?(command)
14
+ ctl.send(command, args)
15
+ else
16
+ raise Racecar::Error, "invalid command: #{command}"
17
+ end
18
+ end
19
+
20
+ def produce(args)
21
+ message = ProduceMessage.new
22
+
23
+ parser = OptionParser.new do |opts|
24
+ opts.banner = "Usage: racecarctl produce [options]"
25
+
26
+ opts.on("-v", "--value VALUE", "Set the message value") do |value|
27
+ message.value = value
28
+ end
29
+
30
+ opts.on("-k", "--key KEY", "Set the message key") do |key|
31
+ message.key = key
32
+ end
33
+
34
+ opts.on("-t", "--topic TOPIC", "Set the message topic") do |topic|
35
+ message.topic = topic
36
+ end
37
+ end
38
+
39
+ parser.parse!(args)
40
+
41
+ ConfigLoader.load!
42
+
43
+ Racecar.config.validate!
44
+
45
+ kafka = Kafka.new(
46
+ client_id: Racecar.config.client_id,
47
+ seed_brokers: Racecar.config.brokers,
48
+ logger: Racecar.logger,
49
+ connect_timeout: Racecar.config.connect_timeout,
50
+ socket_timeout: Racecar.config.socket_timeout,
51
+ )
52
+
53
+ kafka.deliver_message(message.value, key: message.key, topic: message.topic)
54
+
55
+ puts "=> Delivered message to Kafka cluster"
56
+ end
57
+ end
58
+ end
@@ -29,10 +29,11 @@ module Racecar
29
29
  trap("INT") { consumer.stop }
30
30
 
31
31
  config.subscriptions.each do |subscription|
32
- topic = subscription.topic
33
- start_from_beginning = subscription.start_from_beginning
34
-
35
- consumer.subscribe(topic, start_from_beginning: start_from_beginning)
32
+ consumer.subscribe(
33
+ subscription.topic,
34
+ start_from_beginning: subscription.start_from_beginning,
35
+ max_bytes_per_partition: subscription.max_bytes_per_partition,
36
+ )
36
37
  end
37
38
 
38
39
  begin
@@ -1,3 +1,3 @@
1
1
  module Racecar
2
- VERSION = "0.1.4"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: racecar
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Schierbeck
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2017-06-21 00:00:00.000000000 Z
12
+ date: 2017-07-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ruby-kafka
@@ -73,6 +73,7 @@ email:
73
73
  - bquorning@zendesk.com
74
74
  executables:
75
75
  - racecar
76
+ - racecarctl
76
77
  extensions: []
77
78
  extra_rdoc_files: []
78
79
  files:
@@ -86,6 +87,7 @@ files:
86
87
  - bin/setup
87
88
  - examples/cat_consumer.rb
88
89
  - exe/racecar
90
+ - exe/racecarctl
89
91
  - lib/generators/racecar/consumer_generator.rb
90
92
  - lib/generators/racecar/install_generator.rb
91
93
  - lib/generators/templates/consumer.rb.erb
@@ -93,7 +95,9 @@ files:
93
95
  - lib/racecar.rb
94
96
  - lib/racecar/cli.rb
95
97
  - lib/racecar/config.rb
98
+ - lib/racecar/config_loader.rb
96
99
  - lib/racecar/consumer.rb
100
+ - lib/racecar/ctl.rb
97
101
  - lib/racecar/env_loader.rb
98
102
  - lib/racecar/runner.rb
99
103
  - lib/racecar/version.rb