Skip to content

Conversation

@linkyndy
Copy link
Contributor

@linkyndy linkyndy commented Jan 24, 2020

Everytime a new Aws::SQS::Client is instantiated, requests to retrieve SQS credentials are triggered. Since this happens for merely every Sqewer.submit! and derivates, a lot of SQS credential requests are fired, leading to quite a big slowdown.

Here's a small test run with connection pooling:

/app # bundle console
irb(main):001:0> require'./app'
=> true
irb(main):002:0> Net::HTTP.class_eval do alias original_request request; def request(request, body = nil, &block); puts "#{request.method} #{use_ssl? ? "https" : "http"}://#{request["host"] || address}"; original_request(request, body, &block) end end
=> :request
irb(main):004:0> Sqewer.submit!(Job.new(data: 'test'))
GET http://169.254.169.254
GET http://169.254.169.254
POST https://sqs.eu-west-1.amazonaws.com
=> nil
irb(main):005:0> Sqewer.submit!(Job.new(data: 'test'))
POST https://sqs.eu-west-1.amazonaws.com
=> nil
irb(main):006:0> Sqewer.submit!(Job.new(data: 'test'))
POST https://sqs.eu-west-1.amazonaws.com
=> nil
irb(main):007:0> Sqewer.submit!(Job.new(data: 'test'))
POST https://sqs.eu-west-1.amazonaws.com
=> nil

and without connection pooling:

/app # bundle console
irb(main):001:0> require'./app'
=> true
irb(main):002:2" Net::HTTP.class_eval do alias original_request request; def request(request, body = nil, &block); puts "#{request.method} #{use_ssl? ? "https" : "http"}://#{request["host"] || address}"; original_request(request, body, &block) end end
=> :request
irb(main):004:0> Sqewer.submit!(Job.new(data: 'test'))
GET http://169.254.169.254
GET http://169.254.169.254
POST https://sqs.eu-west-1.amazonaws.com
=> nil
irb(main):005:0> Sqewer.submit!(Job.new(data: 'test'))
GET http://169.254.169.254
GET http://169.254.169.254
POST https://sqs.eu-west-1.amazonaws.com
=> nil
irb(main):006:0> Sqewer.submit!(Job.new(data: 'test'))
GET http://169.254.169.254
GET http://169.254.169.254
POST https://sqs.eu-west-1.amazonaws.com
=> nil
irb(main):007:0> Sqewer.submit!(Job.new(data: 'test'))
GET http://169.254.169.254
GET http://169.254.169.254
POST https://sqs.eu-west-1.amazonaws.com
=> nil

We basically start an app console, perform some meta-magic on Net::HTTP to print requests that are being made and then fire a few Sqewer.submit!. You can observe two GET requests to http://169.254.169.254 happening for every submit where connection pooling is not used.

With this PR, there will be a default connection pool available, the same as there was previously a default connection available. The connection pool can also be injected to Sqewer, in a similar way to how a connection would have been previously injected.

@linkyndy linkyndy changed the title Initial attempt at connection pooling Introduce SQS connection pooling Feb 13, 2020
@linkyndy linkyndy requested a review from julik February 13, 2020 15:42
@linkyndy linkyndy marked this pull request as ready for review February 19, 2020 12:28
Copy link
Contributor

@julik julik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually looks very good, see minor suggestions above. 🚀 🚀

@@ -1,3 +1,5 @@
require 'connection_pool'

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not need to require connection_pool or depend on it - we can provide a NullPool instead (Prorate gem has a similar setup). Less dependencies that way :-) and if we document connection_pool compatibility people may install it themselves

Sqewer::Submitter.default.submit!(*jobs, **options)
end

def self.default_connection_pool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we create an accessor pair (client_pool / client_pool=) instead, to adhere by the principle of least surprise? We can then also return a NullPool from the reader if nothing was configured

serializer.serialize(job)
end
connection.send_message(message_body, **kwargs_for_send)
if connection_pool.is_a?(Sqewer::ConnectionMessagebox)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use a pool always, but with a stand-in NullPool when none is configured?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit trickier, because the message box also uses a connection (it acts as a composed object). Given the API around this, this is the best solution I found. If you have something better in mind, I'd be happy to make it work :)

client.delete_queue(queue_url: ENV.fetch('SQS_QUEUE_URL')) rescue Aws::SQS::Errors::NonExistentQueue

ENV.delete('SQS_QUEUE_URL')
Sqewer.reset_default_connection_pool!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have an accessor pair we could do an explicit assignment here instead?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants