railway-ipc 2.0.3 → 3.0.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
  SHA256:
3
- metadata.gz: 444dfeb8a9729ca23276ce6f4a899589a646c93d3671fb517ab81d0fdc85958e
4
- data.tar.gz: 9ede7408f2a428cf3345ef3b8aa684c6aa3fe43a6d460ef7eb6866a99f46fe71
3
+ metadata.gz: 5002df116daa50da3bfcefc3a966901a49c04671e869f23a645482ef9cf4c619
4
+ data.tar.gz: de49b0064f5b57f77893f46701007928a3e1c0fa4644a34041de50c6c4113f18
5
5
  SHA512:
6
- metadata.gz: d15afcbeec8272d7e9b3c9e589f7ed88ffc8fb50fbf370cbaabf596a753e435bd7c67980668c7ce27d13b1b4dff7adf2b4b11f3b7c2f28ec224295bfc7f8ec69
7
- data.tar.gz: 1d39cf3abce42366a096a83d15f8a287e32fbb7ef239fd17f04707ef0eed9fc7c5e3c06e129b4e4345871def82914a740fc6cab5039cc5d2934886342207aa67
6
+ metadata.gz: 9ed5098e317e039d3e4ed0305aca7f571be35f1ace0d88a513ef702ec381f10fe8291378b00ca181a6d23cfcbc7882c367dacedfb68d458ff71f9c8f808128d3
7
+ data.tar.gz: a30c8d4795fcd839588a8e9fc375c027b4a36e48b64eee9800a659fa4380f2d89d66f89c838c80fb70bf2072917146bcb6bd4ad4917141fa4dcfc0cfdf89db14
data/.gitignore CHANGED
@@ -8,6 +8,9 @@
8
8
  /tmp/
9
9
  Gemfile.lock
10
10
 
11
+ # built gems
12
+ railway-ipc-*.gem
13
+
11
14
  # rspec failure tracking
12
15
  .rspec_status
13
16
 
@@ -10,6 +10,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
10
10
  ### Removed
11
11
  ### Fixed
12
12
 
13
+ ## [3.0.0] - 2020-12-07
14
+ ### Changed
15
+ * Consumers _will no longer crash_ when an exception is raised. Instead, consumers will move the message that caused the exception to a single dead-letter exchange called 'ipc:errors'. Railway will configure the dead-letter exchange automatically.
16
+
17
+ ## [2.2.2] - 2020-11-20
18
+ ### Fixed
19
+ * Fixed Publisher class channel leak. Channels were being created on each instantiation of a Publisher instead of being re-used.
20
+
21
+ ## [2.2.1] - 2020-10-20
22
+ ### Added
23
+ * Logging to indicate when options passed via `listen_to`/`from_queue` are overriding the defaults.
24
+
25
+ ## [2.2.0] - 2020-10-20
26
+ ### Added
27
+ * The ability to configure workers to handle different workloads via `rake railway_ipc::consumers:spawn`
28
+
29
+ ## [2.1.0] - 2020-10-19
30
+ ### Added
31
+ * :options parameter to `listen_to` which passes keys along to Sneaker's `from_queue` method.
32
+
13
33
  ## [2.0.3] - 2020-09-02
14
34
  ### Fixed
15
35
  * Fix RPC server. RPC servers need to conform to the Sneaker worker API (i.e. their initializers need to be able to accept queue name / pool and they require a `stop` method.
@@ -66,7 +86,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66
86
  ### Added
67
87
  - Correlation ID and message UUID are auto generated for messages for IDs are not passed in [#23](https://github.com/learn-co/railway_ipc_gem/pull/23)
68
88
 
69
- [Unreleased]: https://github.com/learn-co/railway_ipc_gem/compare/v2.0.3...HEAD
89
+ [Unreleased]: https://github.com/learn-co/railway_ipc_gem/compare/v30.0.0...HEAD
90
+ [3.0.0]: https://github.com/learn-co/railway_ipc_gem/compare/v2.2.2...v3.0.0
91
+ [2.2.2]: https://github.com/learn-co/railway_ipc_gem/compare/v2.2.1...v2.2.2
92
+ [2.2.1]: https://github.com/learn-co/railway_ipc_gem/compare/v2.2.0...v2.2.1
93
+ [2.2.0]: https://github.com/learn-co/railway_ipc_gem/compare/v2.1.0...v2.2.0
94
+ [2.1.0]: https://github.com/learn-co/railway_ipc_gem/compare/v2.0.3...v2.1.0
70
95
  [2.0.3]: https://github.com/learn-co/railway_ipc_gem/compare/v2.0.2...v2.0.3
71
96
  [2.0.2]: https://github.com/learn-co/railway_ipc_gem/compare/v2.0.1...v2.0.2
72
97
  [2.0.1]: https://github.com/learn-co/railway_ipc_gem/compare/v2.0.0...v2.0.1
data/README.md CHANGED
@@ -52,6 +52,26 @@ Then, run your consumers
52
52
  bundle exec rake railway_ipc:consumers:start CONSUMERS=YourConsumer,YourOtherConsumer
53
53
  ```
54
54
 
55
+ You may also configure your consumers more granularly using:
56
+
57
+ `./config/sneaker_worker_groups.yml`
58
+ ```yaml
59
+ HighPriority:
60
+ classes: YourConsumer,YourOtherConsumer
61
+ workers: 5
62
+ LowPriority:
63
+ classes: YourThirdConsumer
64
+ workers: 1
65
+ ```
66
+
67
+ ```bash
68
+ bundle exec rake railway_ipc:consumers:spawn
69
+ ```
70
+
71
+ By default, `spawn` will map to `./config/sneaker_worker_groups.yml` but you can override it by using `WORKER_GROUP_CONFIG`.
72
+
73
+ See the [Sneaker Documentation](https://github.com/jondot/sneakers/wiki/Handling-different-workloads) for more information.
74
+
55
75
  # Request/Response
56
76
 
57
77
  Define your server, client and responder. Docs coming soon.
@@ -2,9 +2,10 @@
2
2
 
3
3
  require 'railway_ipc/version'
4
4
  require 'sneakers'
5
+ require 'sneakers/spawner'
5
6
  require 'bunny'
6
7
  require 'active_record'
7
- require 'railway_ipc/version'
8
+ require 'singleton'
8
9
  require 'railway_ipc/logger'
9
10
  require 'railway_ipc/unhandled_message_error'
10
11
  require 'railway_ipc/response'
@@ -14,6 +15,7 @@ require 'railway_ipc/rabbitmq/adapter'
14
15
  require 'railway_ipc/handler'
15
16
  require 'railway_ipc/handler_store'
16
17
  require 'railway_ipc/incoming_message'
18
+ require 'railway_ipc/connection_manager'
17
19
  require 'railway_ipc/publisher'
18
20
  require 'railway_ipc/responder'
19
21
  require 'railway_ipc/rpc/rpc'
@@ -29,12 +31,16 @@ module RailwayIpc
29
31
  Rake::Task['sneakers:run'].invoke
30
32
  end
31
33
 
32
- def self.configure(log_device=STDOUT, level=::Logger::INFO, log_formatter=nil)
34
+ def self.spawn
35
+ Sneakers::Spawner.spawn
36
+ end
37
+
38
+ def self.configure(log_device=$stdout, level=::Logger::INFO, log_formatter=nil)
33
39
  @logger = RailwayIpc::Logger.new(log_device, level, log_formatter)
34
40
  end
35
41
 
36
42
  def self.logger
37
- @logger || RailwayIpc::Logger.new(STDOUT)
43
+ @logger || RailwayIpc::Logger.new($stdout)
38
44
  end
39
45
 
40
46
  def self.bunny_connection
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+
5
+ module RailwayIpc
6
+ # RabbitMQ connection manager. Ensures there is a single RabbitMQ
7
+ # connection and channel per thread, which prevents channel leaks.
8
+ #
9
+ class ConnectionManager
10
+ include Singleton
11
+
12
+ def initialize
13
+ establish_connection
14
+ end
15
+
16
+ def establish_connection
17
+ @connection = Bunny.new(
18
+ host: settings[:host],
19
+ user: settings[:user],
20
+ pass: settings[:pass],
21
+ port: settings[:port],
22
+ vhost: settings[:vhost] || '/',
23
+ logger: RailwayIpc.logger
24
+ )
25
+ @connection.start
26
+ @channel = @connection.create_channel
27
+
28
+ @connection
29
+ end
30
+
31
+ def channel
32
+ return @channel if connected?
33
+
34
+ establish_connection
35
+ @channel
36
+ end
37
+
38
+ def connected?
39
+ @connection&.connected? && @channel&.open?
40
+ end
41
+
42
+ private
43
+
44
+ def amqp_url
45
+ @amqp_url ||= ENV.fetch('RAILWAY_RABBITMQ_CONNECTION_URL', 'amqp://guest:guest@localhost:5672')
46
+ end
47
+
48
+ def settings
49
+ @settings ||= AMQ::Settings.parse_amqp_url(amqp_url)
50
+ end
51
+ end
52
+ end
@@ -3,7 +3,10 @@
3
3
  module RailwayIpc
4
4
  class Consumer
5
5
  include Sneakers::Worker
6
+
6
7
  def self.inherited(base)
8
+ super
9
+
7
10
  base.instance_eval do
8
11
  def handlers
9
12
  @handlers ||= RailwayIpc::HandlerStore.new
@@ -11,13 +14,27 @@ module RailwayIpc
11
14
  end
12
15
  end
13
16
 
14
- def self.listen_to(queue:, exchange:)
15
- from_queue queue,
16
- exchange: exchange,
17
- durable: true,
18
- exchange_type: :fanout,
19
- connection: RailwayIpc.bunny_connection
17
+ # rubocop:disable Metrics/MethodLength
18
+ def self.listen_to(queue:, exchange:, options: {})
19
+ unless options.empty?
20
+ RailwayIpc.logger.info(
21
+ "Overriding configuration for #{queue} with new options",
22
+ feature: 'railway_ipc_consumer',
23
+ options: options
24
+ )
25
+ end
26
+
27
+ from_queue queue, {
28
+ exchange: exchange,
29
+ durable: true,
30
+ exchange_type: :fanout,
31
+ arguments: {
32
+ 'x-dead-letter-exchange' => 'ipc:errors'
33
+ },
34
+ connection: RailwayIpc.bunny_connection
35
+ }.merge(options)
20
36
  end
37
+ # rubocop:enable Metrics/MethodLength
21
38
 
22
39
  def self.handle(message_type, with:)
23
40
  handlers.register(message: message_type, handler: with)
@@ -52,7 +69,7 @@ module RailwayIpc
52
69
  error: e.class,
53
70
  payload: payload
54
71
  )
55
- raise e
72
+ reject!
56
73
  end
57
74
 
58
75
  def get_handler(type)
@@ -21,7 +21,7 @@ module RailwayIpc
21
21
  # logger = RailwayIpc::Logger.new(STDOUT, Logger::INFO, OjFormatter)
22
22
  #
23
23
  class Logger
24
- def initialize(device=STDOUT, level=::Logger::INFO, formatter=nil)
24
+ def initialize(device=$stdout, level=::Logger::INFO, formatter=nil)
25
25
  @logger = ::Logger.new(device)
26
26
  @logger.level = level
27
27
  @logger.formatter = formatter if formatter
@@ -32,7 +32,7 @@ module RailwayIpc
32
32
  data.merge!(feature: 'railway_ipc') unless data.key?(:feature)
33
33
  return logger.send(level, data.merge(message: message)) unless block
34
34
 
35
- data = message.merge(data) if message&.is_a?(Hash)
35
+ data = message.merge(data) if message.is_a?(Hash)
36
36
  data.merge!(message: block.call)
37
37
 
38
38
  # This is for compatability w/ Ruby's Logger. Ruby's Logger class
@@ -41,7 +41,7 @@ module RailwayIpc
41
41
  # is assumed to be the `progname`.
42
42
  #
43
43
  # https://github.com/ruby/logger/blob/master/lib/logger.rb#L471
44
- data.merge!(progname: message) if message&.is_a?(String)
44
+ data.merge!(progname: message) if message.is_a?(String)
45
45
  logger.send(level, data)
46
46
  end
47
47
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'singleton'
4
-
5
3
  module RailwayIpc
6
4
  class SingletonPublisher < Sneakers::Publisher
7
5
  include ::Singleton
@@ -53,19 +51,12 @@ module RailwayIpc
53
51
  end
54
52
 
55
53
  module RailwayIpc
56
- class Publisher < Sneakers::Publisher
54
+ class Publisher
57
55
  attr_reader :exchange_name, :message_store
58
56
 
59
57
  def initialize(opts={})
60
58
  @exchange_name = opts.fetch(:exchange_name)
61
59
  @message_store = opts.fetch(:message_store, RailwayIpc::PublishedMessage)
62
- connection = opts.fetch(:connection, nil)
63
- options = {
64
- exchange: exchange_name,
65
- connection: connection,
66
- exchange_type: :fanout
67
- }.compact
68
- super(options)
69
60
  end
70
61
 
71
62
  # rubocop:disable Metrics/AbcSize
@@ -75,7 +66,7 @@ module RailwayIpc
75
66
  RailwayIpc.logger.info('Publishing message', log_message_options(message))
76
67
 
77
68
  stored_message = message_store.store_message(exchange_name, message)
78
- super(RailwayIpc::Rabbitmq::Payload.encode(message))
69
+ exchange.publish(RailwayIpc::Rabbitmq::Payload.encode(message))
79
70
  rescue RailwayIpc::InvalidProtobuf => e
80
71
  RailwayIpc.logger.error('Invalid protobuf', log_message_options(message))
81
72
  raise e
@@ -88,8 +79,16 @@ module RailwayIpc
88
79
  end
89
80
  # rubocop:enable Metrics/AbcSize
90
81
 
82
+ def exchange
83
+ @exchange ||= channel.exchange(exchange_name, type: :fanout, durable: true, auto_delete: false, arguments: {})
84
+ end
85
+
91
86
  private
92
87
 
88
+ def channel
89
+ RailwayIpc::ConnectionManager.instance.channel
90
+ end
91
+
93
92
  def log_message_options(message)
94
93
  {
95
94
  feature: 'railway_ipc_publisher',
@@ -16,7 +16,7 @@ module RailwayIpc
16
16
  :port,
17
17
  :user
18
18
 
19
- def initialize(amqp_url: ENV['RAILWAY_RABBITMQ_CONNECTION_URL'], exchange_name:, queue_name: '', options: {})
19
+ def initialize(exchange_name:, amqp_url: ENV['RAILWAY_RABBITMQ_CONNECTION_URL'], queue_name: '', options: {})
20
20
  @queue_name = queue_name
21
21
  @exchange_name = exchange_name
22
22
  settings = AMQ::Settings.parse_amqp_url(amqp_url)
@@ -3,7 +3,7 @@
3
3
  module RailwayIpc
4
4
  module RPC
5
5
  module MessageObservationConfigurable
6
- def listen_to(exchange:, queue:)
6
+ def listen_to(queue:, exchange:)
7
7
  @exchange_name = exchange
8
8
  @queue_name = queue
9
9
  end
@@ -2,9 +2,15 @@
2
2
 
3
3
  namespace :railway_ipc do
4
4
  namespace :consumers do
5
+ desc 'Start consumers via explicitly defining them'
5
6
  task :start do
6
7
  ENV['WORKERS'] = ENV['CONSUMERS']
7
8
  RailwayIpc.start
8
9
  end
10
+
11
+ desc 'Start consumers via ./config/sneaker_worker_groups.yml'
12
+ task :spawn do
13
+ RailwayIpc.spawn
14
+ end
9
15
  end
10
16
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailwayIpc
4
- VERSION = '2.0.3'
4
+ VERSION = '3.0.0'
5
5
  end
@@ -34,7 +34,7 @@ Gem::Specification.new do |spec|
34
34
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
35
35
  spec.require_paths = ['lib']
36
36
 
37
- spec.add_development_dependency 'bundler', '2.0.1'
37
+ spec.add_development_dependency 'bundler', '2.1.4'
38
38
  spec.add_development_dependency 'factory_bot', '~> 5.1'
39
39
  spec.add_development_dependency 'google-protobuf', '~> 3.9'
40
40
  spec.add_development_dependency 'rake', '>= 10.0.0'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: railway-ipc
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.3
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ''
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-09-02 00:00:00.000000000 Z
11
+ date: 2020-12-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 2.0.1
19
+ version: 2.1.4
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 2.0.1
26
+ version: 2.1.4
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: factory_bot
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -225,6 +225,7 @@ files:
225
225
  - bin/setup
226
226
  - lib/railway_ipc.rb
227
227
  - lib/railway_ipc/Rakefile
228
+ - lib/railway_ipc/connection_manager.rb
228
229
  - lib/railway_ipc/consumer/consumer.rb
229
230
  - lib/railway_ipc/consumer/process_incoming_message.rb
230
231
  - lib/railway_ipc/errors.rb
@@ -278,8 +279,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
278
279
  - !ruby/object:Gem::Version
279
280
  version: '0'
280
281
  requirements: []
281
- rubyforge_project:
282
- rubygems_version: 2.7.6
282
+ rubygems_version: 3.0.3
283
283
  signing_key:
284
284
  specification_version: 4
285
285
  summary: IPC components for Rails