Skip to content

Commit f98de9f

Browse files
author
Suszyński Krzysztof
committed
Adding cache for fetching remote URLs to increase speed significantly
1 parent 4558918 commit f98de9f

File tree

5 files changed

+132
-3
lines changed

5 files changed

+132
-3
lines changed

.rspec

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
--format documentation
2+
--color

lib/puppet_forge_server/http/http_client.rb

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,19 @@
1717
require 'open-uri'
1818
require 'open_uri_redirections'
1919
require 'timeout'
20+
require 'lrucache'
2021
require 'net/http'
2122
require 'net/http/post/multipart'
2223

2324

2425
module PuppetForgeServer::Http
2526
class HttpClient
2627

28+
def initialize(cache = nil)
29+
cache = LRUCache.new(:ttl => 60 * 15, :max_size => 250) if cache.nil?
30+
@cache = cache
31+
end
32+
2733
def post_file(url, file_hash, options = {})
2834
options = { :http => {}, :headers => {}}.merge(options)
2935

@@ -46,10 +52,19 @@ def download(url)
4652
end
4753

4854
private
55+
4956
def open_uri(url)
50-
::Timeout.timeout(10) do
51-
open(url, 'User-Agent' => "Puppet-Forge-Server/#{PuppetForgeServer::VERSION}", :allow_redirections => :safe)
57+
contents = @cache.fetch(url) do
58+
tmpfile = ::Timeout.timeout(10) do
59+
PuppetForgeServer::Logger.get.debug "Fetching data for url: #{url} from remote server"
60+
open(url, 'User-Agent' => "Puppet-Forge-Server/#{PuppetForgeServer::VERSION}", :allow_redirections => :safe)
61+
end
62+
contents = tmpfile.read
63+
tmpfile.close
64+
contents
5265
end
66+
PuppetForgeServer::Logger.get.debug "Data for url: #{url} fetched, #{contents.size} bytes"
67+
StringIO.new(contents)
5368
end
5469
end
5570
end

puppet-forge-server.gemspec

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,12 @@ Gem::Specification.new do |spec|
4545
spec.add_dependency 'deep_merge', '~> 1.0'
4646
spec.add_dependency 'multipart-post', '~> 2.0.0'
4747
spec.add_dependency 'redcarpet', '~> 3.3.0'
48+
spec.add_dependency 'lrucache', '~> 0.1.4'
4849

4950
spec.add_development_dependency 'rake', '~> 10.3'
5051
spec.add_development_dependency 'rspec', '~> 3.1'
5152
spec.add_development_dependency 'rspec-core', '~> 3.1'
53+
spec.add_development_dependency 'simplecov', '~> 0.11.0'
5254

5355
spec.required_ruby_version = '>= 1.9.3'
5456
end

spec/spec_helper.rb

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,47 @@
2323
module RSpecMixin
2424
include Rack::Test::Methods
2525
end
26+
begin
27+
gem 'simplecov'
28+
require 'simplecov'
29+
formatters = []
30+
formatters << SimpleCov::Formatter::HTMLFormatter
31+
32+
begin
33+
gem 'coveralls'
34+
require 'coveralls'
35+
formatters << Coveralls::SimpleCov::Formatter if ENV['TRAVIS']
36+
rescue Gem::LoadError
37+
# do nothing
38+
end
39+
40+
begin
41+
gem 'codeclimate-test-reporter'
42+
require 'codeclimate-test-reporter'
43+
formatters << CodeClimate::TestReporter::Formatter if (ENV['TRAVIS'] and ENV['CODECLIMATE_REPO_TOKEN'])
44+
rescue Gem::LoadError
45+
# do nothing
46+
end
47+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[*formatters]
48+
SimpleCov.start do
49+
add_filter "/spec/"
50+
add_filter "/.vendor/"
51+
add_filter "/vendor/"
52+
add_filter "/gems/"
53+
refuse_coverage_drop
54+
end
55+
rescue Gem::LoadError
56+
# do nothing
57+
end
58+
59+
begin
60+
gem 'pry'
61+
require 'pry'
62+
rescue Gem::LoadError
63+
# do nothing
64+
end
2665

2766
RSpec.configure do |c|
2867
c.include RSpecMixin
2968
c.add_formatter(:documentation)
30-
end
69+
end

spec/unit/http/http_client_spec.rb

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
require 'spec_helper'
2+
require 'puppet_forge_server'
3+
4+
describe PuppetForgeServer::Http::HttpClient do
5+
let(:port) do
6+
require 'socket'
7+
server = TCPServer.new('127.0.0.1', 0)
8+
port = server.addr[1]
9+
server.close
10+
port
11+
end
12+
let(:uri) { "http://localhost:#{port}/" }
13+
before(:each) do
14+
@server = TCPServer.new('localhost', port)
15+
@thr = Thread.new do
16+
loop do
17+
socket = @server.accept
18+
response = "Hello!"
19+
socket.print "HTTP/1.1 200 OK\r\n" +
20+
"Content-Type: text/plain\r\n" +
21+
"Content-Length: #{response.bytesize}\r\n" +
22+
"Connection: close\r\n"
23+
socket.print "\r\n"
24+
sleep 0.01
25+
socket.print response
26+
socket.close
27+
end
28+
end
29+
end
30+
after(:each) do
31+
@server.close
32+
@thr.kill
33+
end
34+
let(:instance) { described_class.new(cache) }
35+
describe '#download' do
36+
subject do
37+
99.times { instance.download(uri) }
38+
instance.download(uri)
39+
end
40+
context 'with 1sec LRU cache' do
41+
let(:cache) do
42+
require 'lrucache'
43+
LRUCache.new(:ttl => 1)
44+
end
45+
it { expect(subject).not_to be_nil }
46+
it { expect(subject).not_to be_closed }
47+
end
48+
context 'with default cache' do
49+
let(:cache) { nil }
50+
it { expect(subject).not_to be_nil }
51+
it { expect(subject).not_to be_closed }
52+
end
53+
end
54+
describe '#get' do
55+
subject do
56+
99.times { instance.get(uri) }
57+
instance.get(uri)
58+
end
59+
context 'with 1sec LRU cache' do
60+
let(:cache) do
61+
require 'lrucache'
62+
LRUCache.new(:ttl => 1)
63+
end
64+
it { expect(subject).to eq('Hello!') }
65+
end
66+
context 'with default cache' do
67+
let(:cache) { nil }
68+
it { expect(subject).to eq('Hello!') }
69+
end
70+
end
71+
end

0 commit comments

Comments
 (0)