Skip to content

Commit

Permalink
Prevent OptionsCache from leaking memory
Browse files Browse the repository at this point in the history
  • Loading branch information
rwz committed May 18, 2016
1 parent 2087a95 commit aa74981
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Rakefile
Expand Up @@ -3,7 +3,7 @@ Bundler::GemHelper.install_tasks

require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:base_spec) do |task|
task.pattern = 'spec/multi_json_spec.rb'
task.pattern = 'spec/multi_json_spec.rb,spec/options_cache_spec.rb'
end

namespace :adapters do
Expand Down
16 changes: 15 additions & 1 deletion lib/multi_json/options_cache.rb
Expand Up @@ -9,7 +9,21 @@ def reset

def fetch(type, key)
cache = instance_variable_get("@#{type}_cache")
cache.key?(key) ? cache[key] : cache[key] = yield
cache.key?(key) ? cache[key] : write(cache, key, &Proc.new)
end

private

# Normally MultiJson is used with a few option sets for both dump/load
# methods. When options are generated dynamically though, every call would
# cause a cache miss and the cache would grow indefinitely. To prevent
# this, we just reset the cache every time the number of keys outgrows
# 1000.
MAX_CACHE_SIZE = 1000

def write(cache, key)
cache.clear if cache.length >= MAX_CACHE_SIZE
cache[key] = yield
end
end
end
20 changes: 20 additions & 0 deletions spec/options_cache_spec.rb
@@ -0,0 +1,20 @@
require "spec_helper"

describe MultiJson::OptionsCache do
before { described_class.reset }

it "doesn't leak memory" do
described_class::MAX_CACHE_SIZE.succ.times do |i|
described_class.fetch(:dump, :key => i) do
{ :foo => i }
end

described_class.fetch(:load, :key => i) do
{ :foo => i }
end
end

expect(described_class.instance_variable_get(:@dump_cache).length).to eq(1)
expect(described_class.instance_variable_get(:@load_cache).length).to eq(1)
end
end

0 comments on commit aa74981

Please sign in to comment.