racecar 0.1.4 → 0.2.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.
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