phobos 1.5.0 → 1.6.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: 576b1c85a5d3f389caac3e09a2c1738bb2743eda
4
- data.tar.gz: 5fc77dbf88b6bfc824245cb58c9a34558562d379
3
+ metadata.gz: 05b2968d8c1fc02f229055c4163bd4c4472a9c1c
4
+ data.tar.gz: cca82af11ae5d8aa4996a8802bc516eda2fd8b4a
5
5
  SHA512:
6
- metadata.gz: 74ff72c5bd479ee5e495d8da7ccc97c2bbe998c5ef7d601ce245a1b5e0a0054c5327c1cb100798dfb0832956fe91c8611f2aa66735d3af90e06b8dc8a915d713
7
- data.tar.gz: 9dbf97945d5bb802fec64af350af7749273e30b9fd000e1ce435b1bfb2780b5f2dfc99d03c3a4cb3eaa1fa8e8a53b3e16ea75431c5057381f7d0993b22cb04b6
6
+ metadata.gz: 61cb3f4940c594967b2c0c20a8ef3e7ac22c811baaea4dd8b2aa482c76aa7c4ae2d93d4a1986b91fc4a6b4b7d6cdae66cc63e23278646b20dbed5346cfa3f31d
7
+ data.tar.gz: a5fbf1313da2817be54c5213a64d0e3e9220d621fc1efd9230cc9285c926d8041a72badf4133679065a888891c8f54da04c80e94d62e9f792542c3c76d02ebb6
data/.dockerignore ADDED
@@ -0,0 +1,13 @@
1
+ .idea
2
+ .DS_Store
3
+ .dockerignore
4
+ .gitignore
5
+ .travis.yml
6
+ Dockerfile
7
+ docker-compose.yml
8
+ Gemfile.lock
9
+ logo.png
10
+ log/
11
+ coverage/
12
+ spec/examples.txt
13
+ pkg/
data/.env ADDED
@@ -0,0 +1 @@
1
+ DEFAULT_TIMEOUT=10
data/.travis.yml ADDED
@@ -0,0 +1,35 @@
1
+ sudo: required
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.1
5
+
6
+ services:
7
+ - docker
8
+
9
+ env:
10
+ global:
11
+ - DEFAULT_TIMEOUT=20
12
+ - CC_TEST_REPORTER_ID=ce08ec7297e0161c25b6b0c86e7bd4b9eb85b15de9e2a17fe419b8439aa40def
13
+
14
+ before_install:
15
+ - env
16
+ - docker-compose --version
17
+ - docker --version
18
+ - docker-compose config
19
+ - docker-compose up -d --force-recreate kafka zookeeper
20
+ - docker-compose build test
21
+
22
+ before_script:
23
+ - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
24
+ - chmod +x ./cc-test-reporter
25
+ - if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then ./cc-test-reporter before-build || echo "Skipping CC coverage before-build"; fi
26
+ - mkdir coverage/
27
+ - touch ./coverage/.resultset.json
28
+
29
+ script:
30
+ - docker-compose run --rm test
31
+
32
+ after_script:
33
+ - cat ./coverage/.resultset.json | sed "s|/opt/phobos|$PWD|" > ./coverage/.newresultset.json
34
+ - cp ./coverage/.newresultset.json ./coverage/.resultset.json
35
+ - if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT || echo "Skipping CC coverage after-build"; fi
data/CHANGELOG.md CHANGED
@@ -4,45 +4,69 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](http://keepachangelog.com/)
5
5
  and this project adheres to [Semantic Versioning](http://semver.org/).
6
6
 
7
- ## 1.5.0 (2017-10-25)
8
-
9
- - [enhancement] Add `before_consume` callback to support single point of decoding a message.
10
- - [enhancement] Add module `Phobos::Test::Helper` for testing, to test consumers with minimal setup required
11
-
12
- ## 1.4.2 (2017-09-29)
13
-
14
- - [bugfix] Async publishing always delivers messages #33
15
-
16
- ## 1.4.1 (2017-08-22)
17
-
18
- - [enhancement] Update dev dependencies to fix warnings for the new unified Integer class
19
- - [bugfix] Include the error `Kafka::ProcessingError` into the abort block
20
-
21
- ## 1.4.0 (2017-08-21)
22
-
23
- - [enhancement] Add support for hash provided settings #30
24
-
25
- ## 1.3.0 (2017-06-15)
26
-
27
- - [enhancement] Add support for erb syntax in phobos config file #26
28
-
29
- ## 1.2.1 (2016-10-12)
30
-
31
- - [bugfix] Ensure JSON layout for log files
32
-
33
- ## 1.2.0 (2016-10-10)
34
-
35
- - [enhancement] Log file can be disabled #20
36
- - [enhancement] Add a new extra (time_elapsed) for notifications "listener.process_message" and "listener.process_batch" #24
37
- - [enhancement] Add option to configure ruby-kafka logger #23
38
-
39
- ## 1.1.0 (2016-09-02)
40
-
41
- - [enhancement] Removed Hashie as a dependency #12
42
- - [feature] Allow configuring consumers min_bytes & max_wait_time #15
43
- - [feature] Allow configuring producers max_queue_size, delivery_threshold & delivery_interval #16
44
- - [feature] Allow configuring force_encoding for message payload #18
45
-
46
- ## 1.0.0 (2016-08-08)
47
-
48
- - Published on Github with full fledged consumers and producers
7
+ ## UNRELEASED
8
+
9
+ ## [1.6.0] - 2017-11-16
10
+ ### Added
11
+ - Support for outputting logs as json #50
12
+ - Make configuration, especially of listeners, more flexible. #31
13
+ - Phobos Discord chat
14
+ - Support for consuming `each_message` instead of `each_batch` via the delivery listener option. #21
15
+ - Instantiate a single handler class instance and use that both for `consume` and `before_consume`. #47
16
+
17
+ ### Changed
18
+ - Pin ruby-kafka version to < 0.5.0 #48
19
+ - Update changelog to follow the [Keep a Changelog](http://keepachangelog.com/) structure
20
+
21
+ ## [1.5.0] - 2017-10-25
22
+ ### Added
23
+ - Add `before_consume` callback to support single point of decoding a message klarna/phobos_db_checkpoint#34
24
+ - Add `Phobos::Test::Helper` for testing, to test consumers with minimal setup required
25
+
26
+ ### Changed
27
+ - Allow configuration of backoff per listener #35
28
+ - Move container orchestration into docker-compose
29
+ - Update docker images #38
30
+
31
+ ### Fixed
32
+ - Make specs run locally #36
33
+
34
+ ## [1.4.2] - 2017-09-29
35
+ ### Fixed
36
+ - Async publishing always delivers messages #33
37
+
38
+ ## [1.4.1] - 2017-08-22
39
+ ### Added
40
+ - Update dev dependencies to fix warnings for the new unified Integer class
41
+
42
+ ### Fixed
43
+ - Include the error `Kafka::ProcessingError` into the abort block
44
+
45
+ ## [1.4.0] - 2017-08-21
46
+ ### Added
47
+ - Support for hash provided settings #30
48
+
49
+ ## [1.3.0] - 2017-06-15
50
+ ### Added
51
+ - Support for ERB syntax in Phobos config file #26
52
+
53
+ ## [1.2.1] - 2016-10-12
54
+ ### Fixed
55
+ - Ensure JSON layout for log files
56
+
57
+ ## [1.2.0] - 2016-10-10
58
+ ### Added
59
+ - Log file can be disabled #20
60
+ - Property (time_elapsed) available for notifications `listener.process_message` and `listener.process_batch` #24
61
+ - Option to configure ruby-kafka logger #23
62
+
63
+ ## [1.1.0] - 2016-09-02
64
+ ### Added
65
+ - Removed Hashie as a dependency #12
66
+ - Allow configuring consumers min_bytes & max_wait_time #15
67
+ - Allow configuring producers max_queue_size, delivery_threshold & delivery_interval #16
68
+ - Allow configuring force_encoding for message payload #18
69
+
70
+ ## [1.0.0] - 2016-08-08
71
+ ### Added
72
+ - Published on Github!
data/Dockerfile CHANGED
@@ -1,13 +1,14 @@
1
- FROM ruby:2.3.1-alpine
1
+ FROM ruby:2.4.1-alpine
2
2
 
3
3
  RUN apk update && apk upgrade && \
4
4
  apk add --no-cache bash git openssh build-base
5
+ RUN gem install bundler -v 1.16.0
5
6
 
6
7
  WORKDIR /opt/phobos
7
8
 
8
9
  ADD Gemfile Gemfile
9
10
  ADD phobos.gemspec phobos.gemspec
10
11
  ADD lib/phobos/version.rb lib/phobos/version.rb
12
+ RUN bundle install
11
13
 
12
- RUN ["bundle", "install"]
13
14
  ADD . .
data/README.md CHANGED
@@ -1,7 +1,9 @@
1
1
  ![Phobos](https://raw.githubusercontent.com/klarna/phobos/master/logo.png)
2
2
 
3
- [![Circle CI](https://circleci.com/gh/klarna/phobos.svg?style=shield&circle-token=2289e0fe5bd934074597b32e7f8f0bc98ea0e3c7)](https://circleci.com/gh/klarna/phobos/tree/master)
4
- [![Coverage Status](https://coveralls.io/repos/github/klarna/phobos/badge.svg?branch=master)](https://coveralls.io/github/klarna/phobos?branch=master)
3
+ [![Build Status](https://travis-ci.org/klarna/phobos.svg?branch=master)](https://travis-ci.org/klarna/phobos)
4
+ [![Maintainability](https://api.codeclimate.com/v1/badges/2d00845fc6e7e83df6e7/maintainability)](https://codeclimate.com/github/klarna/phobos/maintainability)
5
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/2d00845fc6e7e83df6e7/test_coverage)](https://codeclimate.com/github/klarna/phobos/test_coverage)
6
+ [![Chat with us on Discord](https://discordapp.com/api/guilds/379938130326847488/widget.png)](https://discord.gg/rfMUBVD)
5
7
 
6
8
  # Phobos
7
9
 
@@ -117,6 +119,14 @@ By default, the __start__ command will look for the configuration file at `confi
117
119
  ```sh
118
120
  $ phobos start -c /var/configs/my.yml -b /opt/apps/boot.rb
119
121
  ```
122
+
123
+ You may also choose to configure phobos with a hash from within your boot file.
124
+ In this case, disable loading the config file with the `--skip-config` option:
125
+
126
+ ```sh
127
+ $ phobos start -b /opt/apps/boot.rb --skip-config
128
+ ```
129
+
120
130
  ### <a name="usage-consuming-messages-from-kafka"></a> Consuming messages from Kafka
121
131
 
122
132
  Messages from Kafka are consumed using __handlers__. You can use Phobos __executors__ or include it in your own project [as a library](#usage-as-library), but __handlers__ will always be used. To create a handler class, simply include the module `Phobos::Handler`. This module allows Phobos to manage the life cycle of your handler.
@@ -325,6 +335,20 @@ __listeners__ is the list of listeners configured, each listener represents a co
325
335
  [ruby-kafka-consumer]: http://www.rubydoc.info/gems/ruby-kafka/Kafka%2FClient%3Aconsumer
326
336
  [ruby-kafka-producer]: http://www.rubydoc.info/gems/ruby-kafka/Kafka%2FClient%3Aproducer
327
337
 
338
+ #### Additional listener configuration
339
+
340
+ In some cases it's useful to share _most_ of the configuration between
341
+ multiple phobos processes, but have each process run different listeners. In
342
+ that case, a separate yaml file can be created and loaded with the `-l` flag.
343
+ Example:
344
+
345
+ ```sh
346
+ $ phobos start -c /var/configs/my.yml -l /var/configs/additional_listeners.yml
347
+ ```
348
+
349
+ Note that the config file _must_ still specify a listeners section, though it
350
+ can be empty.
351
+
328
352
  ### <a name="usage-instrumentation"></a> Instrumentation
329
353
 
330
354
  Some operations are instrumented using [Active Support Notifications](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html).
@@ -1,6 +1,9 @@
1
1
  logger:
2
2
  # Optional log file, set to false or remove to disable it
3
3
  file: log/phobos.log
4
+ # Optional output format for stdout, default is false (human readable).
5
+ # Set to true to enable json output.
6
+ stdout_json: false
4
7
  level: info
5
8
  # Comment the block to disable ruby-kafka logs
6
9
  ruby_kafka:
@@ -95,6 +98,16 @@ listeners:
95
98
  # Apply this encoding to the message payload, if blank it uses the original encoding. This property accepts values
96
99
  # defined by the ruby Encoding class (https://ruby-doc.org/core-2.3.0/Encoding.html). Ex: UTF_8, ASCII_8BIT, etc
97
100
  force_encoding:
101
+ # Specify the delivery method for a listener.
102
+ # Possible values: [`message`, `batch` (default)]
103
+ # - `message` will yield individual messages from Ruby Kafka using `each_message` and will commit/heartbeat at every consumed message.
104
+ # This is overall a bit slower than using batch, but easier to configure.
105
+ # - `batch` will yield batches from Ruby Kafka using `each_batch`, and commit/heartbeat at every consumed batch.
106
+ # Due to implementation in Ruby Kafka, it should be noted that when configuring large batch sizes in combination with taking long time to consume messages,
107
+ # your consumers might get kicked from the Kafka cluster for not sending a heartbeat within the expected interval.
108
+ # Take this into consideration when determining your configuration.
109
+ # Note: Ultimately commit/heartbeart will depend on the offset commit options and the heartbeat interval.
110
+ delivery: batch
98
111
  # Use this if custom backoff is required for a listener
99
112
  backoff:
100
113
  min_ms: 500
data/docker-compose.yml CHANGED
@@ -1,5 +1,17 @@
1
1
  version: '2'
2
2
  services:
3
+ test:
4
+ depends_on:
5
+ - kafka
6
+ build:
7
+ context: .
8
+ network_mode: service:kafka
9
+ environment:
10
+ - "DEFAULT_TIMEOUT=${DEFAULT_TIMEOUT}"
11
+ command: rspec
12
+ volumes:
13
+ - ./coverage:/opt/phobos/coverage
14
+
3
15
  zookeeper:
4
16
  image: jplock/zookeeper:3.4.10
5
17
  ports:
data/lib/phobos.rb CHANGED
@@ -35,10 +35,16 @@ module Phobos
35
35
  @config = DeepStruct.new(fetch_settings(configuration))
36
36
  @config.class.send(:define_method, :producer_hash) { Phobos.config.producer&.to_hash }
37
37
  @config.class.send(:define_method, :consumer_hash) { Phobos.config.consumer&.to_hash }
38
+ @config.listeners ||= []
38
39
  configure_logger
39
40
  logger.info { Hash(message: 'Phobos configured', env: ENV['RACK_ENV']) }
40
41
  end
41
42
 
43
+ def add_listeners(listeners_configuration)
44
+ listeners_config = DeepStruct.new(fetch_settings(listeners_configuration))
45
+ @config.listeners += listeners_config.listeners
46
+ end
47
+
42
48
  def create_kafka_client
43
49
  Kafka.new(config.kafka.to_hash.merge(logger: @ruby_kafka_logger))
44
50
  end
@@ -54,8 +60,14 @@ module Phobos
54
60
  log_file = config.logger.file
55
61
  ruby_kafka = config.logger.ruby_kafka
56
62
  date_pattern = '%Y-%m-%dT%H:%M:%S:%L%zZ'
57
- file_layout = Logging.layouts.json(date_pattern: date_pattern)
58
- stdout_layout = Logging.layouts.pattern(date_pattern: date_pattern)
63
+ json_layout = Logging.layouts.json(date_pattern: date_pattern)
64
+
65
+ stdout_layout = if config.logger.stdout_json == true
66
+ json_layout
67
+ else
68
+ Logging.layouts.pattern(date_pattern: date_pattern)
69
+ end
70
+
59
71
  appenders = [Logging.appenders.stdout(layout: stdout_layout)]
60
72
 
61
73
  Logging.backtrace(true)
@@ -63,7 +75,7 @@ module Phobos
63
75
 
64
76
  if log_file
65
77
  FileUtils.mkdir_p(File.dirname(log_file))
66
- appenders << Logging.appenders.file(log_file, layout: file_layout)
78
+ appenders << Logging.appenders.file(log_file, layout: json_layout)
67
79
  end
68
80
 
69
81
  @ruby_kafka_logger = nil
@@ -3,57 +3,32 @@ module Phobos
3
3
  class ProcessBatch
4
4
  include Phobos::Instrumentation
5
5
 
6
+ attr_reader :metadata
7
+
6
8
  def initialize(listener:, batch:, listener_metadata:)
7
9
  @listener = listener
8
10
  @batch = batch
9
11
  @listener_metadata = listener_metadata
12
+ @metadata = listener_metadata.merge(
13
+ batch_size: batch.messages.count,
14
+ partition: batch.partition,
15
+ offset_lag: batch.offset_lag
16
+ )
10
17
  end
11
18
 
12
19
  def execute
13
- @batch.messages.each do |message|
14
- backoff = @listener.create_exponential_backoff
15
- metadata = @listener_metadata.merge(
16
- key: message.key,
17
- partition: message.partition,
18
- offset: message.offset,
19
- retry_count: 0
20
- )
21
-
22
- begin
23
- instrument('listener.process_message', metadata) do |metadata|
24
- time_elapsed = measure do
25
- Phobos::Actions::ProcessMessage.new(
26
- listener: @listener,
27
- message: message,
28
- metadata: metadata,
29
- encoding: @listener.encoding
30
- ).execute
31
- end
32
- metadata.merge!(time_elapsed: time_elapsed)
33
- end
34
- rescue => e
35
- retry_count = metadata[:retry_count]
36
- interval = backoff.interval_at(retry_count).round(2)
37
-
38
- error = {
39
- waiting_time: interval,
40
- exception_class: e.class.name,
41
- exception_message: e.message,
42
- backtrace: e.backtrace
43
- }
44
-
45
- instrument('listener.retry_handler_error', error.merge(metadata)) do
46
- Phobos.logger.error do
47
- { message: "error processing message, waiting #{interval}s" }.merge(error).merge(metadata)
48
- end
49
-
50
- sleep interval
51
- metadata.merge!(retry_count: retry_count + 1)
20
+ instrument('listener.process_batch', @metadata) do |metadata|
21
+ time_elapsed = measure do
22
+ @batch.messages.each do |message|
23
+ Phobos::Actions::ProcessMessage.new(
24
+ listener: @listener,
25
+ message: message,
26
+ listener_metadata: @listener_metadata
27
+ ).execute
52
28
  end
53
-
54
- raise Phobos::AbortError if @listener.should_stop?
55
- retry
56
29
  end
30
+
31
+ metadata.merge!(time_elapsed: time_elapsed)
57
32
  end
58
33
  end
59
34
  end
@@ -1,25 +1,73 @@
1
1
  module Phobos
2
2
  module Actions
3
3
  class ProcessMessage
4
- def initialize(listener:, message:, metadata:, encoding:)
4
+ include Phobos::Instrumentation
5
+
6
+ attr_reader :metadata
7
+
8
+ def initialize(listener:, message:, listener_metadata:)
5
9
  @listener = listener
6
10
  @message = message
7
- @metadata = metadata
8
- @encoding = encoding
11
+ @listener_metadata = listener_metadata
12
+ @metadata = listener_metadata.merge(
13
+ key: message.key,
14
+ partition: message.partition,
15
+ offset: message.offset,
16
+ retry_count: 0
17
+ )
9
18
  end
10
19
 
11
20
  def execute
21
+ backoff = @listener.create_exponential_backoff
12
22
  payload = force_encoding(@message.value)
13
- decoded_payload = @listener.handler_class.new.before_consume(payload)
14
- @listener.handler_class.around_consume(decoded_payload, @metadata) do
15
- @listener.handler_class.new.consume(decoded_payload, @metadata)
23
+
24
+ begin
25
+ process_message(payload)
26
+ rescue => e
27
+ retry_count = @metadata[:retry_count]
28
+ interval = backoff.interval_at(retry_count).round(2)
29
+
30
+ error = {
31
+ waiting_time: interval,
32
+ exception_class: e.class.name,
33
+ exception_message: e.message,
34
+ backtrace: e.backtrace
35
+ }
36
+
37
+ instrument('listener.retry_handler_error', error.merge(@metadata)) do
38
+ Phobos.logger.error do
39
+ { message: "error processing message, waiting #{interval}s" }.merge(error).merge(@metadata)
40
+ end
41
+
42
+ sleep interval
43
+ end
44
+
45
+ raise Phobos::AbortError if @listener.should_stop?
46
+
47
+ @metadata.merge!(retry_count: retry_count + 1)
48
+ retry
16
49
  end
17
50
  end
18
51
 
19
52
  private
20
53
 
21
54
  def force_encoding(value)
22
- @encoding ? value.force_encoding(@encoding) : value
55
+ @listener.encoding ? value.force_encoding(@listener.encoding) : value
56
+ end
57
+
58
+ def process_message(payload)
59
+ instrument('listener.process_message', @metadata) do |metadata|
60
+ time_elapsed = measure do
61
+ handler = @listener.handler_class.new
62
+ preprocessed_payload = handler.before_consume(payload)
63
+
64
+ @listener.handler_class.around_consume(preprocessed_payload, @metadata) do
65
+ handler.consume(preprocessed_payload, @metadata)
66
+ end
67
+ end
68
+
69
+ metadata.merge!(time_elapsed: time_elapsed)
70
+ end
23
71
  end
24
72
  end
25
73
  end
data/lib/phobos/cli.rb CHANGED
@@ -49,6 +49,13 @@ module Phobos
49
49
  aliases: ['-b'],
50
50
  banner: 'File path to load application specific code',
51
51
  default: 'phobos_boot.rb'
52
+ option :listeners,
53
+ aliases: ['-l'],
54
+ banner: 'Separate listeners config file (optional)'
55
+ option :skip_config,
56
+ default: false,
57
+ type: :boolean,
58
+ banner: 'Skip config file'
52
59
  def start
53
60
  Phobos::CLI::Start.new(options).execute
54
61
  end
@@ -4,14 +4,28 @@ module Phobos
4
4
  module CLI
5
5
  class Start
6
6
  def initialize(options)
7
- @config_file = File.expand_path(options[:config])
7
+ unless options[:skip_config]
8
+ @config_file = File.expand_path(options[:config])
9
+ end
8
10
  @boot_file = File.expand_path(options[:boot])
11
+
12
+ if options[:listeners]
13
+ @listeners_file = File.expand_path(options[:listeners])
14
+ end
9
15
  end
10
16
 
11
17
  def execute
12
- validate_config_file!
13
- Phobos.configure(config_file)
18
+ if config_file
19
+ validate_config_file!
20
+ Phobos.configure(config_file)
21
+ end
22
+
14
23
  load_boot_file
24
+
25
+ if listeners_file
26
+ Phobos.add_listeners(listeners_file)
27
+ end
28
+
15
29
  validate_listeners!
16
30
 
17
31
  Phobos::CLI::Runner.new.run!
@@ -19,7 +33,7 @@ module Phobos
19
33
 
20
34
  private
21
35
 
22
- attr_reader :config_file, :boot_file
36
+ attr_reader :config_file, :boot_file, :listeners_file
23
37
 
24
38
  def validate_config_file!
25
39
  unless File.exist?(config_file)
@@ -29,13 +43,27 @@ module Phobos
29
43
  end
30
44
 
31
45
  def validate_listeners!
32
- Phobos.config.listeners.collect(&:handler).each do |handler_class|
46
+ Phobos.config.listeners.each do |listener|
47
+ handler_class = listener.handler
48
+
33
49
  begin
34
50
  handler_class.constantize
35
51
  rescue NameError
36
52
  Phobos::CLI.logger.error { Hash(message: "Handler '#{handler_class}' not defined") }
37
53
  exit(1)
38
54
  end
55
+
56
+ delivery = listener.delivery
57
+ if delivery.nil?
58
+ Phobos::CLI.logger.warn do
59
+ Hash(message: "Delivery option should be specified, defaulting to 'batch' - specify this option to silence this message")
60
+ end
61
+ elsif !Listener::DELIVERY_OPTS.include?(delivery)
62
+ Phobos::CLI.logger.error do
63
+ Hash(message: "Invalid delivery option '#{delivery}'. Please specify one of: #{Listener::DELIVERY_OPTS.join(', ')}")
64
+ end
65
+ exit(1)
66
+ end
39
67
  end
40
68
  end
41
69
 
@@ -11,6 +11,7 @@ module Phobos
11
11
  start_from_beginning
12
12
  max_bytes_per_partition
13
13
  backoff
14
+ delivery
14
15
  ).freeze
15
16
 
16
17
  def initialize
@@ -4,6 +4,7 @@ module Phobos
4
4
 
5
5
  KAFKA_CONSUMER_OPTS = %i(session_timeout offset_commit_interval offset_commit_threshold heartbeat_interval).freeze
6
6
  DEFAULT_MAX_BYTES_PER_PARTITION = 524288 # 512 KB
7
+ DELIVERY_OPTS = %w[batch message].freeze
7
8
 
8
9
  attr_reader :group_id, :topic, :id
9
10
  attr_reader :handler_class, :encoding
@@ -11,12 +12,14 @@ module Phobos
11
12
  def initialize(handler:, group_id:, topic:, min_bytes: nil,
12
13
  max_wait_time: nil, force_encoding: nil,
13
14
  start_from_beginning: true, backoff: nil,
15
+ delivery: 'batch',
14
16
  max_bytes_per_partition: DEFAULT_MAX_BYTES_PER_PARTITION)
15
17
  @id = SecureRandom.hex[0...6]
16
18
  @handler_class = handler
17
19
  @group_id = group_id
18
20
  @topic = topic
19
21
  @backoff = backoff
22
+ @delivery = delivery.to_s
20
23
  @subscribe_opts = {
21
24
  start_from_beginning: start_from_beginning,
22
25
  max_bytes_per_partition: max_bytes_per_partition
@@ -42,29 +45,7 @@ module Phobos
42
45
  end
43
46
 
44
47
  begin
45
- @consumer.each_batch(@consumer_opts) do |batch|
46
- batch_metadata = {
47
- batch_size: batch.messages.count,
48
- partition: batch.partition,
49
- offset_lag: batch.offset_lag,
50
- # the offset of the most recent message in the partition
51
- highwater_mark_offset: batch.highwater_mark_offset
52
- }.merge(listener_metadata)
53
-
54
- instrument('listener.process_batch', batch_metadata) do |batch_metadata|
55
- time_elapsed = measure do
56
- Phobos::Actions::ProcessBatch.new(
57
- listener: self,
58
- batch: batch,
59
- listener_metadata: listener_metadata
60
- ).execute
61
- end
62
- batch_metadata.merge!(time_elapsed: time_elapsed)
63
- Phobos.logger.info { Hash(message: 'Committed offset').merge(batch_metadata) }
64
- end
65
-
66
- return if should_stop?
67
- end
48
+ @delivery == 'batch' ? consume_each_batch : consume_each_message
68
49
 
69
50
  # Abort is an exception to prevent the consumer from committing the offset.
70
51
  # Since "listener" had a message being retried while "stop" was called
@@ -95,6 +76,34 @@ module Phobos
95
76
  end
96
77
  end
97
78
 
79
+ def consume_each_batch
80
+ @consumer.each_batch(@consumer_opts) do |batch|
81
+ batch_processor = Phobos::Actions::ProcessBatch.new(
82
+ listener: self,
83
+ batch: batch,
84
+ listener_metadata: listener_metadata
85
+ )
86
+
87
+ batch_processor.execute
88
+ Phobos.logger.info { Hash(message: 'Committed offset').merge(batch_processor.metadata) }
89
+ return if should_stop?
90
+ end
91
+ end
92
+
93
+ def consume_each_message
94
+ @consumer.each_message(@consumer_opts) do |message|
95
+ message_processor = Phobos::Actions::ProcessMessage.new(
96
+ listener: self,
97
+ message: message,
98
+ listener_metadata: listener_metadata
99
+ )
100
+
101
+ message_processor.execute
102
+ Phobos.logger.info { Hash(message: 'Committed offset').merge(message_processor.metadata) }
103
+ return if should_stop?
104
+ end
105
+ end
106
+
98
107
  def stop
99
108
  return if should_stop?
100
109
  instrument('listener.stopping', listener_metadata) do
@@ -1,3 +1,3 @@
1
1
  module Phobos
2
- VERSION = '1.5.0'
2
+ VERSION = '1.6.0'
3
3
  end
data/phobos.gemspec CHANGED
@@ -44,16 +44,14 @@ Gem::Specification.new do |spec|
44
44
  spec.require_paths = ['lib']
45
45
  spec.required_ruby_version = '>= 2.3'
46
46
 
47
- spec.add_development_dependency 'bundler', '~> 1.12'
47
+ spec.add_development_dependency 'bundler'
48
48
  spec.add_development_dependency 'rake', '~> 10.0'
49
- spec.add_development_dependency 'rspec', '~> 3.6'
50
- spec.add_development_dependency 'pry-byebug', '~> 3.4.3'
51
- spec.add_development_dependency 'rspec_junit_formatter', '0.3.0'
52
- spec.add_development_dependency 'simplecov', '~> 0.14.1'
53
- spec.add_development_dependency 'coveralls', '~> 0.8.21'
49
+ spec.add_development_dependency 'rspec', '~> 3.0'
50
+ spec.add_development_dependency 'pry-byebug'
51
+ spec.add_development_dependency 'simplecov'
54
52
  spec.add_development_dependency 'timecop'
55
53
 
56
- spec.add_dependency 'ruby-kafka', '>= 0.3.14'
54
+ spec.add_dependency 'ruby-kafka', '>= 0.3.14', '< 0.5.0'
57
55
  spec.add_dependency 'concurrent-ruby', '>= 1.0.2'
58
56
  spec.add_dependency 'concurrent-ruby-ext', '>= 1.0.2'
59
57
  spec.add_dependency 'activesupport', '>= 4.0.0'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phobos
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Túlio Ornelas
@@ -14,22 +14,22 @@ authors:
14
14
  autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
- date: 2017-10-26 00:00:00.000000000 Z
17
+ date: 2017-11-16 00:00:00.000000000 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: bundler
21
21
  requirement: !ruby/object:Gem::Requirement
22
22
  requirements:
23
- - - "~>"
23
+ - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: '1.12'
25
+ version: '0'
26
26
  type: :development
27
27
  prerelease: false
28
28
  version_requirements: !ruby/object:Gem::Requirement
29
29
  requirements:
30
- - - "~>"
30
+ - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: '1.12'
32
+ version: '0'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: rake
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -50,70 +50,42 @@ dependencies:
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '3.6'
53
+ version: '3.0'
54
54
  type: :development
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: '3.6'
60
+ version: '3.0'
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: pry-byebug
63
63
  requirement: !ruby/object:Gem::Requirement
64
64
  requirements:
65
- - - "~>"
66
- - !ruby/object:Gem::Version
67
- version: 3.4.3
68
- type: :development
69
- prerelease: false
70
- version_requirements: !ruby/object:Gem::Requirement
71
- requirements:
72
- - - "~>"
73
- - !ruby/object:Gem::Version
74
- version: 3.4.3
75
- - !ruby/object:Gem::Dependency
76
- name: rspec_junit_formatter
77
- requirement: !ruby/object:Gem::Requirement
78
- requirements:
79
- - - '='
65
+ - - ">="
80
66
  - !ruby/object:Gem::Version
81
- version: 0.3.0
67
+ version: '0'
82
68
  type: :development
83
69
  prerelease: false
84
70
  version_requirements: !ruby/object:Gem::Requirement
85
71
  requirements:
86
- - - '='
72
+ - - ">="
87
73
  - !ruby/object:Gem::Version
88
- version: 0.3.0
74
+ version: '0'
89
75
  - !ruby/object:Gem::Dependency
90
76
  name: simplecov
91
77
  requirement: !ruby/object:Gem::Requirement
92
78
  requirements:
93
- - - "~>"
94
- - !ruby/object:Gem::Version
95
- version: 0.14.1
96
- type: :development
97
- prerelease: false
98
- version_requirements: !ruby/object:Gem::Requirement
99
- requirements:
100
- - - "~>"
101
- - !ruby/object:Gem::Version
102
- version: 0.14.1
103
- - !ruby/object:Gem::Dependency
104
- name: coveralls
105
- requirement: !ruby/object:Gem::Requirement
106
- requirements:
107
- - - "~>"
79
+ - - ">="
108
80
  - !ruby/object:Gem::Version
109
- version: 0.8.21
81
+ version: '0'
110
82
  type: :development
111
83
  prerelease: false
112
84
  version_requirements: !ruby/object:Gem::Requirement
113
85
  requirements:
114
- - - "~>"
86
+ - - ">="
115
87
  - !ruby/object:Gem::Version
116
- version: 0.8.21
88
+ version: '0'
117
89
  - !ruby/object:Gem::Dependency
118
90
  name: timecop
119
91
  requirement: !ruby/object:Gem::Requirement
@@ -135,6 +107,9 @@ dependencies:
135
107
  - - ">="
136
108
  - !ruby/object:Gem::Version
137
109
  version: 0.3.14
110
+ - - "<"
111
+ - !ruby/object:Gem::Version
112
+ version: 0.5.0
138
113
  type: :runtime
139
114
  prerelease: false
140
115
  version_requirements: !ruby/object:Gem::Requirement
@@ -142,6 +117,9 @@ dependencies:
142
117
  - - ">="
143
118
  - !ruby/object:Gem::Version
144
119
  version: 0.3.14
120
+ - - "<"
121
+ - !ruby/object:Gem::Version
122
+ version: 0.5.0
145
123
  - !ruby/object:Gem::Dependency
146
124
  name: concurrent-ruby
147
125
  requirement: !ruby/object:Gem::Requirement
@@ -242,9 +220,12 @@ executables:
242
220
  extensions: []
243
221
  extra_rdoc_files: []
244
222
  files:
223
+ - ".dockerignore"
224
+ - ".env"
245
225
  - ".gitignore"
246
226
  - ".rspec"
247
227
  - ".ruby-version"
228
+ - ".travis.yml"
248
229
  - CHANGELOG.md
249
230
  - Dockerfile
250
231
  - Gemfile
@@ -254,7 +235,6 @@ files:
254
235
  - bin/console
255
236
  - bin/phobos
256
237
  - bin/setup
257
- - circle.yml
258
238
  - config/phobos.yml.example
259
239
  - docker-compose.yml
260
240
  - examples/handler_saving_events_database.rb
data/circle.yml DELETED
@@ -1,27 +0,0 @@
1
- machine:
2
- pre:
3
- - curl -sSL https://s3.amazonaws.com/circle-downloads/install-circleci-docker.sh | bash -s -- 1.10.0
4
- services:
5
- - docker
6
- environment:
7
- LOG_LEVEL: DEBUG
8
- CI: true
9
- DEFAULT_TIMEOUT: 20
10
- ruby:
11
- version: 2.4.1
12
-
13
- dependencies:
14
- pre:
15
- - docker -v
16
- - docker pull ches/kafka:0.10.2.1
17
- - docker pull jplock/zookeeper:3.4.10
18
- - gem install bundler -v 1.13.2
19
- - bundle install
20
-
21
- test:
22
- override:
23
- - docker run -d -p 2003:2181 --name zookeeper jplock/zookeeper:3.4.10; sleep 5
24
- - docker run -d -p 9092:9092 --name kafka -e KAFKA_BROKER_ID=0 -e KAFKA_ADVERTISED_HOST_NAME=localhost -e KAFKA_ADVERTISED_PORT=9092 -e ZOOKEEPER_CONNECTION_STRING=zookeeper:2181 --link zookeeper:zookeeper ches/kafka:0.10.2.1; sleep 5
25
- - bundle exec rspec -r rspec_junit_formatter --format RspecJunitFormatter -o $CIRCLE_TEST_REPORTS/rspec/unit.xml
26
- post:
27
- - cp log/*.log $CIRCLE_ARTIFACTS/ || true