active_publisher 0.1.5-java → 0.2.0.pre-java
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 +4 -4
- data/.gitignore +1 -0
- data/README.md +68 -1
- data/active_publisher.gemspec +2 -0
- data/lib/active_publisher/async/in_memory_adapter/async_queue.rb +61 -0
- data/lib/active_publisher/async/in_memory_adapter/consumer_thread.rb +89 -0
- data/lib/active_publisher/async/in_memory_adapter.rb +31 -125
- data/lib/active_publisher/connection.rb +2 -0
- data/lib/active_publisher/message.rb +3 -0
- data/lib/active_publisher/version.rb +1 -1
- data/lib/active_publisher.rb +23 -0
- metadata +22 -5
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 63df17dc2a58125dcc18b1cb9c7d20f0517da0ac
         | 
| 4 | 
            +
              data.tar.gz: 9bd8dcd05da48bde7c14ff49a3dc10928ab609a6
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: e8dbdc550e66d0d4182ed96333a03e37ab89466371bc3bc4634ff6d78e1f02e8daeafb5ed3b13f0bc3cb91d793af46ee6954ba2519e148122d8b2dde569a025c
         | 
| 7 | 
            +
              data.tar.gz: d6a7965709096d9f9828afe0655982aaa5ddafa87b3256e44d6c815df4b6467af64bd0236d5f3c49edb9dd366f28dbb2e3011c1373ebd86a8ea9928bf8418bb6
         | 
    
        data/.gitignore
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | @@ -20,9 +20,76 @@ Or install it yourself as: | |
| 20 20 |  | 
| 21 21 | 
             
                $ gem install active_publisher
         | 
| 22 22 |  | 
| 23 | 
            +
            ## Configuration
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            ActivePublisher will use a `config/active_publisher.yml` or `config/action_subscriber.yml` automatically.
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            Create a `config/active_publisher.yml` similar to a database.yml, with your configuration nested in your environments keys.
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            ```yaml
         | 
| 30 | 
            +
            default: &default
         | 
| 31 | 
            +
              host: localhost
         | 
| 32 | 
            +
              username: guest
         | 
| 33 | 
            +
              password: guest
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            development:
         | 
| 36 | 
            +
              <<: *default
         | 
| 37 | 
            +
             | 
| 38 | 
            +
            test:
         | 
| 39 | 
            +
              <<: *default
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            production:
         | 
| 42 | 
            +
              <<: *default
         | 
| 43 | 
            +
              host: <%= ENV['RABBIT_MQ_HOST'] %>
         | 
| 44 | 
            +
              username: <%= ENV['RABBIT_MQ_USERNAME'] %>
         | 
| 45 | 
            +
              password: <%= ENV['RABBIT_MQ_PASSWORD'] %>
         | 
| 46 | 
            +
            ```
         | 
| 47 | 
            +
             | 
| 48 | 
            +
            Defaults for the configuration are:
         | 
| 49 | 
            +
            ```ruby
         | 
| 50 | 
            +
            {
         | 
| 51 | 
            +
              :heartbeat => 5,
         | 
| 52 | 
            +
              :host => "localhost",
         | 
| 53 | 
            +
              :hosts => [],
         | 
| 54 | 
            +
              :port => 5672,
         | 
| 55 | 
            +
              :publisher_confirms => false,
         | 
| 56 | 
            +
              :seconds_to_wait_for_graceful_shutdown => 30,
         | 
| 57 | 
            +
              :timeout => 1,
         | 
| 58 | 
            +
              :username => "guest",
         | 
| 59 | 
            +
              :password => "guest",
         | 
| 60 | 
            +
              :virtual_host => "/"
         | 
| 61 | 
            +
            }
         | 
| 62 | 
            +
            ```
         | 
| 63 | 
            +
             | 
| 23 64 | 
             
            ## Usage
         | 
| 24 65 |  | 
| 25 | 
            -
             | 
| 66 | 
            +
            Basic publishing is simple.
         | 
| 67 | 
            +
             | 
| 68 | 
            +
            ```ruby
         | 
| 69 | 
            +
              # @param [String] route The routing key to use for this message.
         | 
| 70 | 
            +
              # @param [String] payload The message you are sending. Should already be encoded as a string.
         | 
| 71 | 
            +
              # @param [String] exchange The exchange you want to publish to.
         | 
| 72 | 
            +
              # @param [Hash] options hash to set message parameters (e.g. headers)
         | 
| 73 | 
            +
             | 
| 74 | 
            +
              ::ActivePublisher.publish("user.created", user.to_json, "events", {})
         | 
| 75 | 
            +
            ```
         | 
| 76 | 
            +
             | 
| 77 | 
            +
             | 
| 78 | 
            +
            Async publishing is as simple as configuring the async publishing adapter and running `publish_sync` the same was as publish.
         | 
| 79 | 
            +
            You can use the `::ActivePublisher::Async::InMemoryAdapter` that ships with `ActivePublisher`.
         | 
| 80 | 
            +
             | 
| 81 | 
            +
             | 
| 82 | 
            +
            `initializers/active_publisher.rb`
         | 
| 83 | 
            +
            ```ruby
         | 
| 84 | 
            +
            require "active_publisher"
         | 
| 85 | 
            +
            ::ActivePublisher::Async.publisher_adapter = ::ActivePublisher::Async::InMemoryAdapter.new
         | 
| 86 | 
            +
             | 
| 87 | 
            +
            ```
         | 
| 88 | 
            +
             | 
| 89 | 
            +
            ```ruby
         | 
| 90 | 
            +
            ::ActivePublisher.publish_async("user.created", user.to_json, "events", {})
         | 
| 91 | 
            +
            ```
         | 
| 92 | 
            +
             | 
| 26 93 |  | 
| 27 94 | 
             
            ## Development
         | 
| 28 95 |  | 
    
        data/active_publisher.gemspec
    CHANGED
    
    | @@ -26,6 +26,8 @@ Gem::Specification.new do |spec| | |
| 26 26 | 
             
                spec.add_dependency 'bunny', '~> 2.1'
         | 
| 27 27 | 
             
              end
         | 
| 28 28 |  | 
| 29 | 
            +
              spec.add_dependency 'multi_op_queue', '>= 0.1.2'
         | 
| 30 | 
            +
             | 
| 29 31 | 
             
              spec.add_development_dependency "bundler"
         | 
| 30 32 | 
             
              spec.add_development_dependency "pry"
         | 
| 31 33 | 
             
              spec.add_development_dependency "rake", "~> 10.0"
         | 
| @@ -0,0 +1,61 @@ | |
| 1 | 
            +
            module ActivePublisher
         | 
| 2 | 
            +
              module Async
         | 
| 3 | 
            +
                module InMemoryAdapter
         | 
| 4 | 
            +
                  class AsyncQueue
         | 
| 5 | 
            +
                    include ::ActivePublisher::Logging
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                    attr_accessor :drop_messages_when_queue_full,
         | 
| 8 | 
            +
                      :max_queue_size,
         | 
| 9 | 
            +
                      :supervisor_interval
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    attr_reader :consumer, :queue, :supervisor
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    def initialize(drop_messages_when_queue_full, max_queue_size, supervisor_interval)
         | 
| 14 | 
            +
                      @drop_messages_when_queue_full = drop_messages_when_queue_full
         | 
| 15 | 
            +
                      @max_queue_size = max_queue_size
         | 
| 16 | 
            +
                      @supervisor_interval = supervisor_interval
         | 
| 17 | 
            +
                      @queue = ::MultiOpQueue::Queue.new
         | 
| 18 | 
            +
                      create_and_supervise_consumer!
         | 
| 19 | 
            +
                    end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                    def push(message)
         | 
| 22 | 
            +
                      # default of 1_000_000 messages
         | 
| 23 | 
            +
                      if queue.size > max_queue_size
         | 
| 24 | 
            +
                        # Drop messages if the queue is full and we were configured to do so
         | 
| 25 | 
            +
                        return if drop_messages_when_queue_full
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                        # By default we will raise an error to push the responsibility onto the caller
         | 
| 28 | 
            +
                        fail ::ActivePublisher::Async::InMemoryAdapter::UnableToPersistMessageError, "Queue is full, messages will be dropped."
         | 
| 29 | 
            +
                      end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                      queue.push(message)
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    def size
         | 
| 35 | 
            +
                      queue.size
         | 
| 36 | 
            +
                    end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    private
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    def create_and_supervise_consumer!
         | 
| 41 | 
            +
                      @consumer = ::ActivePublisher::Async::InMemoryAdapter::ConsumerThread.new(queue)
         | 
| 42 | 
            +
                      @supervisor = ::Thread.new do
         | 
| 43 | 
            +
                        loop do
         | 
| 44 | 
            +
                          unless consumer.alive?
         | 
| 45 | 
            +
                            # We might need to requeue the last messages popped
         | 
| 46 | 
            +
                            current_consumer_messages = consumer.current_messages
         | 
| 47 | 
            +
                            queue.concat(current_consumer_messages) unless current_consumer_messages.empty?
         | 
| 48 | 
            +
                            consumer.kill
         | 
| 49 | 
            +
                            @consumer = ::ActivePublisher::Async::InMemoryAdapter::ConsumerThread.new(queue)
         | 
| 50 | 
            +
                          end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                          # Pause before checking the consumer again.
         | 
| 53 | 
            +
                          sleep supervisor_interval
         | 
| 54 | 
            +
                        end
         | 
| 55 | 
            +
                      end
         | 
| 56 | 
            +
                    end
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
              end
         | 
| 61 | 
            +
            end
         | 
| @@ -0,0 +1,89 @@ | |
| 1 | 
            +
            module ActivePublisher
         | 
| 2 | 
            +
              module Async
         | 
| 3 | 
            +
                module InMemoryAdapter
         | 
| 4 | 
            +
                  class ConsumerThread
         | 
| 5 | 
            +
                    attr_reader :current_messages, :thread, :queue
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                    if ::RUBY_PLATFORM == "java"
         | 
| 8 | 
            +
                      NETWORK_ERRORS = [::MarchHare::Exception, ::Java::ComRabbitmqClient::AlreadyClosedException, ::Java::JavaIo::IOException].freeze
         | 
| 9 | 
            +
                    else
         | 
| 10 | 
            +
                      NETWORK_ERRORS = [::Bunny::Exception, ::Timeout::Error, ::IOError].freeze
         | 
| 11 | 
            +
                    end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    def initialize(listen_queue)
         | 
| 14 | 
            +
                      @current_messages = []
         | 
| 15 | 
            +
                      @queue = listen_queue
         | 
| 16 | 
            +
                      start_thread
         | 
| 17 | 
            +
                    end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    def alive?
         | 
| 20 | 
            +
                      @thread && @thread.alive?
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    def kill
         | 
| 24 | 
            +
                      @thread.kill if @thread
         | 
| 25 | 
            +
                      @thread = nil
         | 
| 26 | 
            +
                    end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    private
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    def await_network_reconnect
         | 
| 31 | 
            +
                      if defined?(ActivePublisher::RabbitConnection)
         | 
| 32 | 
            +
                        sleep ::ActivePublisher::RabbitConnection::NETWORK_RECOVERY_INTERVAL
         | 
| 33 | 
            +
                      else
         | 
| 34 | 
            +
                        sleep 0.1
         | 
| 35 | 
            +
                      end
         | 
| 36 | 
            +
                    end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    def start_thread
         | 
| 39 | 
            +
                      return if alive?
         | 
| 40 | 
            +
                      @thread = ::Thread.new do
         | 
| 41 | 
            +
                        loop do
         | 
| 42 | 
            +
                          # Write "current_messages" so we can requeue should something happen to the consumer.
         | 
| 43 | 
            +
                          @current_messages.concat(queue.pop_up_to(20))
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                          begin
         | 
| 46 | 
            +
                            # Only open a single connection for each group of messages to an exchange
         | 
| 47 | 
            +
                            messages_to_retry = []
         | 
| 48 | 
            +
                            @current_messages.group_by(&:exchange_name).each do |exchange_name, messages|
         | 
| 49 | 
            +
                              begin
         | 
| 50 | 
            +
                                ::ActivePublisher.publish_all(exchange_name, messages)
         | 
| 51 | 
            +
                              ensure
         | 
| 52 | 
            +
                                messages_to_retry.concat(messages)
         | 
| 53 | 
            +
                              end
         | 
| 54 | 
            +
                            end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                            # Reset
         | 
| 57 | 
            +
                            @current_messages = []
         | 
| 58 | 
            +
                          rescue *NETWORK_ERRORS
         | 
| 59 | 
            +
                            # Sleep because connection is down
         | 
| 60 | 
            +
                            await_network_reconnect
         | 
| 61 | 
            +
                            @current_messages.concat(messages_to_retry)
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                            # Requeue and try again.
         | 
| 64 | 
            +
                            queue.concat(@current_messages) unless @current_messages.empty?
         | 
| 65 | 
            +
                          rescue => unknown_error
         | 
| 66 | 
            +
                            @current_messages.concat(messages_to_retry)
         | 
| 67 | 
            +
                            @current_messages.each do |message|
         | 
| 68 | 
            +
                              # Degrade to single message publish ... or at least attempt to
         | 
| 69 | 
            +
                              begin
         | 
| 70 | 
            +
                                ::ActivePublisher.publish(message.route, message.payload, message.exchange_name, message.options)
         | 
| 71 | 
            +
                              rescue => error
         | 
| 72 | 
            +
                                ::ActivePublisher.configuration.error_handler.call(unknown_error, {:route => message.route, :payload => message.payload, :exchange_name => message.exchange_name, :options => message.options})
         | 
| 73 | 
            +
                              end
         | 
| 74 | 
            +
                            end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                            # Do not requeue the message because something else horrible happened.
         | 
| 77 | 
            +
                            @current_messages = []
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                            # TODO: Find a way to bubble this out of the thread for logging purposes.
         | 
| 80 | 
            +
                            # Reraise the error out of the publisher loop. The Supervisor will restart the consumer.
         | 
| 81 | 
            +
                            raise unknown_error
         | 
| 82 | 
            +
                          end
         | 
| 83 | 
            +
                        end
         | 
| 84 | 
            +
                      end
         | 
| 85 | 
            +
                    end
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
              end
         | 
| 89 | 
            +
            end
         | 
| @@ -1,148 +1,54 @@ | |
| 1 | 
            +
            require "active_publisher/message"
         | 
| 2 | 
            +
            require "active_publisher/async/in_memory_adapter/async_queue"
         | 
| 3 | 
            +
            require "active_publisher/async/in_memory_adapter/consumer_thread"
         | 
| 4 | 
            +
            require "multi_op_queue"
         | 
| 5 | 
            +
             | 
| 1 6 | 
             
            module ActivePublisher
         | 
| 2 7 | 
             
              module Async
         | 
| 3 | 
            -
                 | 
| 4 | 
            -
                  include ::ActivePublisher::Logging
         | 
| 5 | 
            -
             | 
| 6 | 
            -
                  attr_reader :async_queue
         | 
| 7 | 
            -
             | 
| 8 | 
            -
                  def initialize(drop_messages_when_queue_full = false, max_queue_size = 1_000_000, supervisor_interval = 0.2)
         | 
| 9 | 
            -
                    logger.info "Starting in-memory publisher adapter"
         | 
| 10 | 
            -
             | 
| 11 | 
            -
                    @async_queue = ::ActivePublisher::Async::InMemoryAdapter::AsyncQueue.new(
         | 
| 12 | 
            -
                      drop_messages_when_queue_full,
         | 
| 13 | 
            -
                      max_queue_size,
         | 
| 14 | 
            -
                      supervisor_interval
         | 
| 15 | 
            -
                    )
         | 
| 16 | 
            -
                  end
         | 
| 8 | 
            +
                module InMemoryAdapter
         | 
| 17 9 |  | 
| 18 | 
            -
                  def  | 
| 19 | 
            -
                     | 
| 20 | 
            -
                    async_queue.push(message)
         | 
| 21 | 
            -
                    nil
         | 
| 10 | 
            +
                  def self.new(*args)
         | 
| 11 | 
            +
                    ::ActivePublisher::Async::InMemoryAdapter::Adapter.new(*args)
         | 
| 22 12 | 
             
                  end
         | 
| 23 13 |  | 
| 24 | 
            -
                   | 
| 25 | 
            -
                    max_wait_time = ::ActivePublisher.configuration.seconds_to_wait_for_graceful_shutdown
         | 
| 26 | 
            -
                    started_shutting_down_at = ::Time.now
         | 
| 14 | 
            +
                  class UnableToPersistMessageError < ::StandardError; end
         | 
| 27 15 |  | 
| 28 | 
            -
             | 
| 29 | 
            -
                    while async_queue.size > 0
         | 
| 30 | 
            -
                      if (::Time.now - started_shutting_down_at) > max_wait_time
         | 
| 31 | 
            -
                        logger.info "Forcing async publisher adapter shutdown because graceful shutdown period of #{max_wait_time} seconds was exceeded. Current queue size: #{async_queue.size}."
         | 
| 32 | 
            -
                        break
         | 
| 33 | 
            -
                      end
         | 
| 34 | 
            -
             | 
| 35 | 
            -
                      sleep 0.1
         | 
| 36 | 
            -
                    end
         | 
| 37 | 
            -
                  end
         | 
| 38 | 
            -
             | 
| 39 | 
            -
                  class AsyncQueue
         | 
| 16 | 
            +
                  class Adapter
         | 
| 40 17 | 
             
                    include ::ActivePublisher::Logging
         | 
| 41 18 |  | 
| 42 | 
            -
                     | 
| 43 | 
            -
                                  :max_queue_size,
         | 
| 44 | 
            -
                                  :supervisor_interval
         | 
| 45 | 
            -
             | 
| 46 | 
            -
                    attr_reader :consumer, :queue, :supervisor
         | 
| 47 | 
            -
             | 
| 48 | 
            -
                    if ::RUBY_PLATFORM == "java"
         | 
| 49 | 
            -
                      NETWORK_ERRORS = [::MarchHare::Exception, ::Java::ComRabbitmqClient::AlreadyClosedException, ::Java::JavaIo::IOException].freeze
         | 
| 50 | 
            -
                    else
         | 
| 51 | 
            -
                      NETWORK_ERRORS = [::Bunny::Exception, ::Timeout::Error, ::IOError].freeze
         | 
| 52 | 
            -
                    end
         | 
| 53 | 
            -
             | 
| 54 | 
            -
                    def initialize(drop_messages_when_queue_full, max_queue_size, supervisor_interval)
         | 
| 55 | 
            -
                      @drop_messages_when_queue_full = drop_messages_when_queue_full
         | 
| 56 | 
            -
                      @max_queue_size = max_queue_size
         | 
| 57 | 
            -
                      @supervisor_interval = supervisor_interval
         | 
| 58 | 
            -
                      @queue = ::Queue.new
         | 
| 59 | 
            -
                      create_and_supervise_consumer!
         | 
| 60 | 
            -
                    end
         | 
| 61 | 
            -
             | 
| 62 | 
            -
                    def push(message)
         | 
| 63 | 
            -
                      # default of 1_000_000 messages
         | 
| 64 | 
            -
                      if queue.size > max_queue_size
         | 
| 65 | 
            -
                        # Drop messages if the queue is full and we were configured to do so
         | 
| 66 | 
            -
                        return if drop_messages_when_queue_full
         | 
| 19 | 
            +
                    attr_reader :async_queue
         | 
| 67 20 |  | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
                      end
         | 
| 21 | 
            +
                    def initialize(drop_messages_when_queue_full = false, max_queue_size = 1_000_000, supervisor_interval = 0.2)
         | 
| 22 | 
            +
                      logger.info "Starting in-memory publisher adapter"
         | 
| 71 23 |  | 
| 72 | 
            -
                       | 
| 24 | 
            +
                      @async_queue = ::ActivePublisher::Async::InMemoryAdapter::AsyncQueue.new(
         | 
| 25 | 
            +
                        drop_messages_when_queue_full,
         | 
| 26 | 
            +
                        max_queue_size,
         | 
| 27 | 
            +
                        supervisor_interval
         | 
| 28 | 
            +
                      )
         | 
| 73 29 | 
             
                    end
         | 
| 74 30 |  | 
| 75 | 
            -
                    def  | 
| 76 | 
            -
                       | 
| 31 | 
            +
                    def publish(route, payload, exchange_name, options = {})
         | 
| 32 | 
            +
                      message = ::ActivePublisher::Message.new(route, payload, exchange_name, options)
         | 
| 33 | 
            +
                      async_queue.push(message)
         | 
| 34 | 
            +
                      nil
         | 
| 77 35 | 
             
                    end
         | 
| 78 36 |  | 
| 79 | 
            -
             | 
| 37 | 
            +
                    def shutdown!
         | 
| 38 | 
            +
                      max_wait_time = ::ActivePublisher.configuration.seconds_to_wait_for_graceful_shutdown
         | 
| 39 | 
            +
                      started_shutting_down_at = ::Time.now
         | 
| 80 40 |  | 
| 81 | 
            -
             | 
| 82 | 
            -
                       | 
| 83 | 
            -
             | 
| 84 | 
            -
             | 
| 85 | 
            -
             | 
| 86 | 
            -
                      @consumer = create_consumer
         | 
| 87 | 
            -
                      @supervisor = ::Thread.new do
         | 
| 88 | 
            -
                        loop do
         | 
| 89 | 
            -
                          unless consumer.alive?
         | 
| 90 | 
            -
                            # We might need to requeue the last message.
         | 
| 91 | 
            -
                            queue.push(@current_message) unless @current_message.nil?
         | 
| 92 | 
            -
                            consumer.kill
         | 
| 93 | 
            -
                            @consumer = create_consumer
         | 
| 94 | 
            -
                          end
         | 
| 95 | 
            -
             | 
| 96 | 
            -
                          # Pause before checking the consumer again.
         | 
| 97 | 
            -
                          sleep supervisor_interval
         | 
| 41 | 
            +
                      logger.info "Draining async publisher in-memory adapter queue before shutdown. current queue size: #{async_queue.size}."
         | 
| 42 | 
            +
                      while async_queue.size > 0
         | 
| 43 | 
            +
                        if (::Time.now - started_shutting_down_at) > max_wait_time
         | 
| 44 | 
            +
                          logger.info "Forcing async publisher adapter shutdown because graceful shutdown period of #{max_wait_time} seconds was exceeded. Current queue size: #{async_queue.size}."
         | 
| 45 | 
            +
                          break
         | 
| 98 46 | 
             
                        end
         | 
| 99 | 
            -
                      end
         | 
| 100 | 
            -
                    end
         | 
| 101 | 
            -
             | 
| 102 | 
            -
                    def create_consumer
         | 
| 103 | 
            -
                      ::Thread.new do
         | 
| 104 | 
            -
                        loop do
         | 
| 105 | 
            -
                          # Write "current_message" so we can requeue should something happen to the consumer.
         | 
| 106 | 
            -
                          @current_message = message = queue.pop
         | 
| 107 | 
            -
             | 
| 108 | 
            -
                          begin
         | 
| 109 | 
            -
                            ::ActivePublisher.publish(message.route, message.payload, message.exchange_name, message.options)
         | 
| 110 | 
            -
             | 
| 111 | 
            -
                            # Reset
         | 
| 112 | 
            -
                            @current_message = nil
         | 
| 113 | 
            -
                          rescue *NETWORK_ERRORS
         | 
| 114 | 
            -
                            # Sleep because connection is down
         | 
| 115 | 
            -
                            await_network_reconnect
         | 
| 116 47 |  | 
| 117 | 
            -
             | 
| 118 | 
            -
                            queue.push(message)
         | 
| 119 | 
            -
                          rescue => unknown_error
         | 
| 120 | 
            -
                            # Do not requeue the message because something else horrible happened.
         | 
| 121 | 
            -
                            @current_message = nil
         | 
| 122 | 
            -
             | 
| 123 | 
            -
                            ::ActivePublisher.configuration.error_handler.call(unknown_error, {:route => message.route, :payload => message.payload, :exchange_name => message.exchange_name, :options => message.options})
         | 
| 124 | 
            -
             | 
| 125 | 
            -
                            # TODO: Find a way to bubble this out of the thread for logging purposes.
         | 
| 126 | 
            -
                            # Reraise the error out of the publisher loop. The Supervisor will restart the consumer.
         | 
| 127 | 
            -
                            raise unknown_error
         | 
| 128 | 
            -
                          end
         | 
| 129 | 
            -
                        end
         | 
| 48 | 
            +
                        sleep 0.1
         | 
| 130 49 | 
             
                      end
         | 
| 131 50 | 
             
                    end
         | 
| 132 | 
            -
                  end
         | 
| 133 | 
            -
             | 
| 134 | 
            -
                  class Message
         | 
| 135 | 
            -
                    attr_reader :route, :payload, :exchange_name, :options
         | 
| 136 | 
            -
             | 
| 137 | 
            -
                    def initialize(route, payload, exchange_name, options)
         | 
| 138 | 
            -
                      @route = route
         | 
| 139 | 
            -
                      @payload = payload
         | 
| 140 | 
            -
                      @exchange_name = exchange_name
         | 
| 141 | 
            -
                      @options = options
         | 
| 142 | 
            -
                    end
         | 
| 143 | 
            -
                  end
         | 
| 144 51 |  | 
| 145 | 
            -
                  class UnableToPersistMessageError < ::StandardError
         | 
| 146 52 | 
             
                  end
         | 
| 147 53 | 
             
                end
         | 
| 148 54 | 
             
              end
         | 
    
        data/lib/active_publisher.rb
    CHANGED
    
    | @@ -8,11 +8,15 @@ require "thread" | |
| 8 8 | 
             
            require "active_publisher/logging"
         | 
| 9 9 | 
             
            require "active_publisher/async"
         | 
| 10 10 | 
             
            require "active_publisher/async/in_memory_adapter"
         | 
| 11 | 
            +
            require "active_publisher/message"
         | 
| 11 12 | 
             
            require "active_publisher/version"
         | 
| 12 13 | 
             
            require "active_publisher/configuration"
         | 
| 13 14 | 
             
            require "active_publisher/connection"
         | 
| 14 15 |  | 
| 15 16 | 
             
            module ActivePublisher
         | 
| 17 | 
            +
              class UnknownMessageClassError < StandardError; end
         | 
| 18 | 
            +
              class ExchangeMismatchError < StandardError; end
         | 
| 19 | 
            +
             | 
| 16 20 | 
             
              def self.configuration
         | 
| 17 21 | 
             
                @configuration ||= ::ActivePublisher::Configuration.new
         | 
| 18 22 | 
             
              end
         | 
| @@ -33,6 +37,25 @@ module ActivePublisher | |
| 33 37 | 
             
                end
         | 
| 34 38 | 
             
              end
         | 
| 35 39 |  | 
| 40 | 
            +
              def self.publish_all(exchange_name, messages)
         | 
| 41 | 
            +
                with_exchange(exchange_name) do |exchange|
         | 
| 42 | 
            +
                  loop do
         | 
| 43 | 
            +
                    break if messages.empty?
         | 
| 44 | 
            +
                    message = messages.shift
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                    fail ActivePublisher::UnknownMessageClassError, "bulk publish messages must be ActivePublisher::Message" unless message.is_a?(ActivePublisher::Message)
         | 
| 47 | 
            +
                    fail ActivePublisher::ExchangeMismatchError, "bulk publish messages must match publish_all exchange_name" if message.exchange_name != exchange_name
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                    begin
         | 
| 50 | 
            +
                      exchange.publish(message.payload, publishing_options(message.route, message.options || {}))
         | 
| 51 | 
            +
                    rescue
         | 
| 52 | 
            +
                      messages << message
         | 
| 53 | 
            +
                      raise
         | 
| 54 | 
            +
                    end
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
              end
         | 
| 58 | 
            +
             | 
| 36 59 | 
             
              def self.publishing_options(route, in_options = {})
         | 
| 37 60 | 
             
                options = {
         | 
| 38 61 | 
             
                  :mandatory => false,
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: active_publisher
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.2.0.pre
         | 
| 5 5 | 
             
            platform: java
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Brian Stien
         | 
| @@ -12,7 +12,7 @@ authors: | |
| 12 12 | 
             
            autorequire:
         | 
| 13 13 | 
             
            bindir: exe
         | 
| 14 14 | 
             
            cert_chain: []
         | 
| 15 | 
            -
            date: 2016- | 
| 15 | 
            +
            date: 2016-11-17 00:00:00.000000000 Z
         | 
| 16 16 | 
             
            dependencies:
         | 
| 17 17 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 18 18 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -28,6 +28,20 @@ dependencies: | |
| 28 28 | 
             
                - - "~>"
         | 
| 29 29 | 
             
                  - !ruby/object:Gem::Version
         | 
| 30 30 | 
             
                    version: '2.7'
         | 
| 31 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 32 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 33 | 
            +
                requirements:
         | 
| 34 | 
            +
                - - ">="
         | 
| 35 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 36 | 
            +
                    version: 0.1.2
         | 
| 37 | 
            +
              name: multi_op_queue
         | 
| 38 | 
            +
              prerelease: false
         | 
| 39 | 
            +
              type: :runtime
         | 
| 40 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 41 | 
            +
                requirements:
         | 
| 42 | 
            +
                - - ">="
         | 
| 43 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 44 | 
            +
                    version: 0.1.2
         | 
| 31 45 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 32 46 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 33 47 | 
             
                requirements:
         | 
| @@ -109,9 +123,12 @@ files: | |
| 109 123 | 
             
            - lib/active_publisher.rb
         | 
| 110 124 | 
             
            - lib/active_publisher/async.rb
         | 
| 111 125 | 
             
            - lib/active_publisher/async/in_memory_adapter.rb
         | 
| 126 | 
            +
            - lib/active_publisher/async/in_memory_adapter/async_queue.rb
         | 
| 127 | 
            +
            - lib/active_publisher/async/in_memory_adapter/consumer_thread.rb
         | 
| 112 128 | 
             
            - lib/active_publisher/configuration.rb
         | 
| 113 129 | 
             
            - lib/active_publisher/connection.rb
         | 
| 114 130 | 
             
            - lib/active_publisher/logging.rb
         | 
| 131 | 
            +
            - lib/active_publisher/message.rb
         | 
| 115 132 | 
             
            - lib/active_publisher/version.rb
         | 
| 116 133 | 
             
            homepage: https://github.com/mxenabled/active_publisher
         | 
| 117 134 | 
             
            licenses:
         | 
| @@ -128,12 +145,12 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 128 145 | 
             
                  version: '0'
         | 
| 129 146 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 130 147 | 
             
              requirements:
         | 
| 131 | 
            -
              - - " | 
| 148 | 
            +
              - - ">"
         | 
| 132 149 | 
             
                - !ruby/object:Gem::Version
         | 
| 133 | 
            -
                  version:  | 
| 150 | 
            +
                  version: 1.3.1
         | 
| 134 151 | 
             
            requirements: []
         | 
| 135 152 | 
             
            rubyforge_project:
         | 
| 136 | 
            -
            rubygems_version: 2.6. | 
| 153 | 
            +
            rubygems_version: 2.6.7
         | 
| 137 154 | 
             
            signing_key:
         | 
| 138 155 | 
             
            specification_version: 4
         | 
| 139 156 | 
             
            summary: Aims to make publishing work across MRI and jRuby painless and add some nice features like automatially publishing lifecycle events for ActiveRecord models.
         |