phobos 1.5.0 → 1.6.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: 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