Replace mime-types with mini_mime to save memory
On boot, mime/types/columnar allocates **109k** objects and ends up with a retained count of **31K** objects. This leaves objects that need to be marked and swept every major GC and bloats ruby processes. To circumvent, mini_mime was created. It uses the exact same database as the full-fledged mime-types gem and is capable of only loading on demand with a practical, safe and bound in-memory cache. mini_mime allocates **398** objects on boot and only retain **62** of them (due to rubygems inefficiency). In table form: | |boot allocations|boot retained|lookup|lookup uncached| |-|-:|-:|-:|-:| |mini_mime|398|62|641K/s|33K/s| |mime-types|109796|31165|361K/s|361K/s| The performance of mini_mime is pretty good: cached lookups are faster than the mime-types gem and uncached lookups are 10x slower, which is really not at huge issue considering the memory savings.
This commit is contained in:
parent
48cb6db25b
commit
634d91145c
50
.travis.yml
50
.travis.yml
|
@ -21,58 +21,8 @@ rvm:
|
|||
- jruby-head
|
||||
- rbx-2
|
||||
|
||||
gemfile:
|
||||
- gemfiles/mime_types_1.16.gemfile
|
||||
- gemfiles/mime_types_2.0.gemfile
|
||||
- gemfiles/mime_types_2.1.gemfile
|
||||
- gemfiles/mime_types_2.2.gemfile
|
||||
- gemfiles/mime_types_2.3.gemfile
|
||||
- gemfiles/mime_types_2.4.gemfile
|
||||
- gemfiles/mime_types_2.5.gemfile
|
||||
- gemfiles/mime_types_2.6.gemfile
|
||||
- gemfiles/mime_types_2.6_columnar.gemfile
|
||||
- gemfiles/mime_types_2.99.gemfile
|
||||
- gemfiles/mime_types_3.0.gemfile
|
||||
- gemfiles/mime_types_edge.gemfile
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- rvm: 1.8.7
|
||||
gemfile: gemfiles/mime_types_2.0.gemfile
|
||||
- rvm: 1.8.7
|
||||
gemfile: gemfiles/mime_types_2.1.gemfile
|
||||
- rvm: 1.8.7
|
||||
gemfile: gemfiles/mime_types_2.2.gemfile
|
||||
- rvm: 1.8.7
|
||||
gemfile: gemfiles/mime_types_2.3.gemfile
|
||||
- rvm: 1.8.7
|
||||
gemfile: gemfiles/mime_types_2.4.gemfile
|
||||
- rvm: 1.8.7
|
||||
gemfile: gemfiles/mime_types_2.5.gemfile
|
||||
- rvm: 1.8.7
|
||||
gemfile: gemfiles/mime_types_2.6.gemfile
|
||||
- rvm: 1.8.7
|
||||
gemfile: gemfiles/mime_types_2.6_columnar.gemfile
|
||||
- rvm: 1.8.7
|
||||
gemfile: gemfiles/mime_types_2.99.gemfile
|
||||
- rvm: 1.8.7
|
||||
gemfile: gemfiles/mime_types_2.latest.gemfile
|
||||
- rvm: 1.8.7
|
||||
gemfile: gemfiles/mime_types_3.0.gemfile
|
||||
- rvm: 1.8.7
|
||||
gemfile: gemfiles/mime_types_edge.gemfile
|
||||
- rvm: 1.9.2
|
||||
gemfile: gemfiles/mime_types_3.0.gemfile
|
||||
- rvm: 1.9.2
|
||||
gemfile: gemfiles/mime_types_edge.gemfile
|
||||
- rvm: 1.9.3
|
||||
gemfile: gemfiles/mime_types_3.0.gemfile
|
||||
- rvm: 1.9.3
|
||||
gemfile: gemfiles/mime_types_edge.gemfile
|
||||
- rvm: jruby
|
||||
gemfile: gemfiles/mime_types_3.0.gemfile
|
||||
- rvm: jruby
|
||||
gemfile: gemfiles/mime_types_edge.gemfile
|
||||
allow_failures:
|
||||
- rvm: ruby-head
|
||||
- rvm: jruby-9.0.5.0
|
||||
|
|
47
Appraisals
47
Appraisals
|
@ -1,47 +0,0 @@
|
|||
appraise "mime-types-1.16" do
|
||||
gem "mime-types", "~> 1.16"
|
||||
end
|
||||
|
||||
appraise "mime-types-2.0" do
|
||||
gem "mime-types", "~> 2.0.0"
|
||||
end
|
||||
|
||||
appraise "mime-types-2.1" do
|
||||
gem "mime-types", "~> 2.1.0"
|
||||
end
|
||||
|
||||
appraise "mime-types-2.2" do
|
||||
gem "mime-types", "~> 2.2.0"
|
||||
end
|
||||
|
||||
appraise "mime-types-2.3" do
|
||||
gem "mime-types", "~> 2.3.0"
|
||||
end
|
||||
|
||||
appraise "mime-types-2.4" do
|
||||
gem "mime-types", "~> 2.4.0"
|
||||
end
|
||||
|
||||
appraise "mime-types-2.5" do
|
||||
gem "mime-types", "~> 2.5.0"
|
||||
end
|
||||
|
||||
appraise "mime-types-2.6" do
|
||||
gem "mime-types", "~> 2.6.0"
|
||||
end
|
||||
|
||||
appraise "mime-types-2.6-columnar" do
|
||||
gem "mime-types", "~> 2.6.0", :require => 'mime/types/columnar'
|
||||
end
|
||||
|
||||
appraise "mime-types-2.99" do
|
||||
gem "mime-types", "~> 2.99.0"
|
||||
end
|
||||
|
||||
appraise "mime-types-3.0" do
|
||||
gem "mime-types", "~> 3.0.0"
|
||||
end
|
||||
|
||||
appraise "mime-types-edge" do
|
||||
gem "mime-types", :github => "mime-types/ruby-mime-types"
|
||||
end
|
|
@ -3,6 +3,9 @@
|
|||
Features:
|
||||
* #853 - `Mail::Message#set_sort_order` overrides the default message part sort order. (rafbm)
|
||||
|
||||
Performance:
|
||||
* #1059 - Switch from mime-types to mini_mime for a much smaller memory footprint. (SamSaffron)
|
||||
|
||||
Compatibility:
|
||||
* #655 - Sort attachments to the end of the parts list to work around email clients that may mistake a text attachment for the message body. (npickens)
|
||||
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
tlsmail: if ruby < 1.8.6... we could make it optional, or embed it in Mail
|
||||
mime/types: I think we embed a simplified version, or help maintain it, it is old (2006)
|
||||
|
|
11
Gemfile
11
Gemfile
|
@ -4,11 +4,8 @@ gemspec
|
|||
|
||||
gem 'tlsmail', '~> 0.0.1' if RUBY_VERSION <= '1.8.6'
|
||||
gem 'jruby-openssl', :platforms => :jruby
|
||||
gem 'rake', '< 11.0', :platforms => :ruby_18
|
||||
gem 'rdoc', '< 4.3', :platforms => [ :ruby_18, :ruby_19 ]
|
||||
gem 'mime-types', '< 2.0', :platforms => [ :ruby_18, :ruby_19 ]
|
||||
|
||||
# For gems not required to run tests
|
||||
group :local_development, :test do
|
||||
gem 'appraisal', '~> 1.0' unless RUBY_VERSION < '1.9'
|
||||
end
|
||||
gem 'rake', '< 11.0' if RUBY_VERSION < '1.9.3'
|
||||
gem 'rdoc', '< 4.3' if RUBY_VERSION < '2.0'
|
||||
|
||||
gem 'mini_mime', :github => 'discourse/mini_mime'
|
||||
|
|
6
Rakefile
6
Rakefile
|
@ -19,11 +19,5 @@ RSpec::Core::RakeTask.new(:spec) do |t|
|
|||
t.rspec_opts = %w(--backtrace --color)
|
||||
end
|
||||
|
||||
begin
|
||||
require "appraisal"
|
||||
rescue LoadError, SyntaxError
|
||||
warn "Appraisal is only available in test/development on Ruby 1.9+"
|
||||
end
|
||||
|
||||
# load custom rake tasks
|
||||
Dir["#{File.dirname(__FILE__)}/tasks/**/*.rake"].sort.each { |ext| load ext }
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
# This file was generated by Appraisal
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "jruby-openssl", :platforms => :jruby
|
||||
gem "rake", "< 11.0", :platforms => :ruby_18
|
||||
gem "rdoc", "< 4.3", :platforms => [:ruby_18, :ruby_19]
|
||||
gem "mime-types", "~> 1.16"
|
||||
|
||||
group :local_development, :test do
|
||||
gem "appraisal", "~> 1.0"
|
||||
end
|
||||
|
||||
gemspec :path => "../"
|
|
@ -1,14 +0,0 @@
|
|||
# This file was generated by Appraisal
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "jruby-openssl", :platforms => :jruby
|
||||
gem "rake", "< 11.0", :platforms => :ruby_18
|
||||
gem "rdoc", "< 4.3", :platforms => [:ruby_18, :ruby_19]
|
||||
gem "mime-types", "~> 2.0.0"
|
||||
|
||||
group :local_development, :test do
|
||||
gem "appraisal", "~> 1.0"
|
||||
end
|
||||
|
||||
gemspec :path => "../"
|
|
@ -1,14 +0,0 @@
|
|||
# This file was generated by Appraisal
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "jruby-openssl", :platforms => :jruby
|
||||
gem "rake", "< 11.0", :platforms => :ruby_18
|
||||
gem "rdoc", "< 4.3", :platforms => [:ruby_18, :ruby_19]
|
||||
gem "mime-types", "~> 2.1.0"
|
||||
|
||||
group :local_development, :test do
|
||||
gem "appraisal", "~> 1.0"
|
||||
end
|
||||
|
||||
gemspec :path => "../"
|
|
@ -1,14 +0,0 @@
|
|||
# This file was generated by Appraisal
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "jruby-openssl", :platforms => :jruby
|
||||
gem "rake", "< 11.0", :platforms => :ruby_18
|
||||
gem "rdoc", "< 4.3", :platforms => [:ruby_18, :ruby_19]
|
||||
gem "mime-types", "~> 2.2.0"
|
||||
|
||||
group :local_development, :test do
|
||||
gem "appraisal", "~> 1.0"
|
||||
end
|
||||
|
||||
gemspec :path => "../"
|
|
@ -1,14 +0,0 @@
|
|||
# This file was generated by Appraisal
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "jruby-openssl", :platforms => :jruby
|
||||
gem "rake", "< 11.0", :platforms => :ruby_18
|
||||
gem "rdoc", "< 4.3", :platforms => [:ruby_18, :ruby_19]
|
||||
gem "mime-types", "~> 2.3.0"
|
||||
|
||||
group :local_development, :test do
|
||||
gem "appraisal", "~> 1.0"
|
||||
end
|
||||
|
||||
gemspec :path => "../"
|
|
@ -1,14 +0,0 @@
|
|||
# This file was generated by Appraisal
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "jruby-openssl", :platforms => :jruby
|
||||
gem "rake", "< 11.0", :platforms => :ruby_18
|
||||
gem "rdoc", "< 4.3", :platforms => [:ruby_18, :ruby_19]
|
||||
gem "mime-types", "~> 2.4.0"
|
||||
|
||||
group :local_development, :test do
|
||||
gem "appraisal", "~> 1.0"
|
||||
end
|
||||
|
||||
gemspec :path => "../"
|
|
@ -1,14 +0,0 @@
|
|||
# This file was generated by Appraisal
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "jruby-openssl", :platforms => :jruby
|
||||
gem "rake", "< 11.0", :platforms => :ruby_18
|
||||
gem "rdoc", "< 4.3", :platforms => [:ruby_18, :ruby_19]
|
||||
gem "mime-types", "~> 2.5.0"
|
||||
|
||||
group :local_development, :test do
|
||||
gem "appraisal", "~> 1.0"
|
||||
end
|
||||
|
||||
gemspec :path => "../"
|
|
@ -1,14 +0,0 @@
|
|||
# This file was generated by Appraisal
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "jruby-openssl", :platforms => :jruby
|
||||
gem "rake", "< 11.0", :platforms => :ruby_18
|
||||
gem "rdoc", "< 4.3", :platforms => [:ruby_18, :ruby_19]
|
||||
gem "mime-types", "~> 2.6.0"
|
||||
|
||||
group :local_development, :test do
|
||||
gem "appraisal", "~> 1.0"
|
||||
end
|
||||
|
||||
gemspec :path => "../"
|
|
@ -1,14 +0,0 @@
|
|||
# This file was generated by Appraisal
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "jruby-openssl", :platforms => :jruby
|
||||
gem "rake", "< 11.0", :platforms => :ruby_18
|
||||
gem "rdoc", "< 4.3", :platforms => [:ruby_18, :ruby_19]
|
||||
gem "mime-types", "~> 2.6.0", :require => "mime/types/columnar"
|
||||
|
||||
group :local_development, :test do
|
||||
gem "appraisal", "~> 1.0"
|
||||
end
|
||||
|
||||
gemspec :path => "../"
|
|
@ -1,14 +0,0 @@
|
|||
# This file was generated by Appraisal
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "jruby-openssl", :platforms => :jruby
|
||||
gem "rake", "< 11.0", :platforms => :ruby_18
|
||||
gem "rdoc", "< 4.3", :platforms => [:ruby_18, :ruby_19]
|
||||
gem "mime-types", "~> 2.99.0"
|
||||
|
||||
group :local_development, :test do
|
||||
gem "appraisal", "~> 1.0"
|
||||
end
|
||||
|
||||
gemspec :path => "../"
|
|
@ -1,14 +0,0 @@
|
|||
# This file was generated by Appraisal
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "jruby-openssl", :platforms => :jruby
|
||||
gem "rake", "< 11.0", :platforms => :ruby_18
|
||||
gem "rdoc", "< 4.3", :platforms => [:ruby_18, :ruby_19]
|
||||
gem "mime-types", "~> 3.0.0"
|
||||
|
||||
group :local_development, :test do
|
||||
gem "appraisal", "~> 1.0"
|
||||
end
|
||||
|
||||
gemspec :path => "../"
|
|
@ -1,14 +0,0 @@
|
|||
# This file was generated by Appraisal
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "jruby-openssl", :platforms => :jruby
|
||||
gem "rake", "< 11.0", :platforms => :ruby_18
|
||||
gem "rdoc", "< 4.3", :platforms => [:ruby_18, :ruby_19]
|
||||
gem "mime-types", :github => "mime-types/ruby-mime-types"
|
||||
|
||||
group :local_development, :test do
|
||||
gem "appraisal", "~> 1.0"
|
||||
end
|
||||
|
||||
gemspec :path => "../"
|
|
@ -7,13 +7,7 @@ module Mail # :doc:
|
|||
|
||||
require 'uri'
|
||||
require 'net/smtp'
|
||||
|
||||
begin
|
||||
# Use mime/types/columnar if available, for reduced memory usage
|
||||
require 'mime/types/columnar'
|
||||
rescue LoadError
|
||||
require 'mime/types'
|
||||
end
|
||||
require 'mini_mime'
|
||||
|
||||
if RUBY_VERSION <= '1.8.6'
|
||||
begin
|
||||
|
|
|
@ -60,7 +60,7 @@ module Mail
|
|||
|
||||
if value[:mime_type]
|
||||
default_values[:content_type] = value.delete(:mime_type)
|
||||
@mime_type = MIME::Types[default_values[:content_type]].first
|
||||
@mime_type = MiniMime.lookup_by_content_type(default_values[:content_type])
|
||||
default_values[:content_transfer_encoding] ||= guess_encoding
|
||||
end
|
||||
|
||||
|
@ -99,7 +99,8 @@ module Mail
|
|||
filename = filename.encode(Encoding::UTF_8) if filename.respond_to?(:encode)
|
||||
end
|
||||
|
||||
@mime_type = MIME::Types.type_for(filename).first
|
||||
@mime_type = MiniMime.lookup_by_filename(filename)
|
||||
@mime_type && @mime_type.content_type
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -14,7 +14,7 @@ Gem::Specification.new do |s|
|
|||
s.extra_rdoc_files = ["README.md", "CONTRIBUTING.md", "CHANGELOG.rdoc", "TODO.rdoc"]
|
||||
s.rdoc_options << '--exclude' << 'lib/mail/values/unicode_tables.dat'
|
||||
|
||||
s.add_dependency('mime-types', [">= 1.16", "< 4"])
|
||||
s.add_dependency('mini_mime', '>= 0.1.1')
|
||||
|
||||
s.add_development_dependency('bundler', '>= 1.0.3')
|
||||
s.add_development_dependency('rake', '> 0.8.7')
|
||||
|
|
Loading…
Reference in New Issue