├── lib ├── cache │ ├── version.rb │ ├── redis_namespace.rb │ ├── active_support_cache_file_store.rb │ ├── active_support_cache_memory_store.rb │ ├── active_support_cache_dalli_store.rb │ ├── config.rb │ ├── mem_cache.rb │ ├── memcached_rails.rb │ ├── active_support_cache_store.rb │ ├── dalli_client.rb │ ├── redis.rb │ └── memcached.rb └── cache.rb ├── .gitignore ├── Gemfile ├── test ├── helper.rb ├── test_memcached_binary_storage.rb ├── test_active_support_cache_file_store.rb ├── test_default_storage.rb ├── test_dalli_storage.rb ├── test_dalli_store_storage.rb ├── test_memcached_rails_storage.rb ├── test_memcache_storage.rb ├── test_memcached_storage.rb ├── test_rails_cache_storage.rb ├── test_redis_storage.rb ├── test_redis_namespace_storage.rb ├── shared_tests.rb └── profile │ └── benchmark.rb ├── CHANGELOG ├── LICENSE.txt ├── Rakefile ├── cache.gemspec ├── benchmarks ├── afterrefactor.txt ├── midrefactor.txt ├── v0.0.2.txt ├── v0.1.2.txt ├── v0.2.1.txt ├── v0.2.2.txt └── v0.0.3.txt └── README.md /lib/cache/version.rb: -------------------------------------------------------------------------------- 1 | class Cache 2 | VERSION = '0.4.1' 3 | end 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | .bundle 3 | Gemfile.lock 4 | pkg/* 5 | secret.sh 6 | rdoc/* 7 | .yardoc 8 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source :rubygems 2 | 3 | # Specify your gem's dependencies in cache.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /lib/cache/redis_namespace.rb: -------------------------------------------------------------------------------- 1 | require 'cache/redis' 2 | module Cache::RedisNamespace 3 | def self.extended(base) 4 | base.extend Cache::Redis 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /lib/cache/active_support_cache_file_store.rb: -------------------------------------------------------------------------------- 1 | require 'cache/active_support_cache_store' 2 | module Cache::ActiveSupportCacheFileStore 3 | def self.extended(base) 4 | base.extend Cache::ActiveSupportCacheStore 5 | end 6 | 7 | def _stats 8 | {} 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/cache/active_support_cache_memory_store.rb: -------------------------------------------------------------------------------- 1 | require 'cache/active_support_cache_store' 2 | module Cache::ActiveSupportCacheMemoryStore 3 | def self.extended(base) 4 | base.extend Cache::ActiveSupportCacheStore 5 | end 6 | 7 | def _stats 8 | {} 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/helper.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler/setup' 3 | require 'test/unit' 4 | 5 | require 'cache' 6 | 7 | require 'shared_tests' 8 | 9 | class Test::Unit::TestCase 10 | def setup 11 | @cache = Cache.wrap raw_client 12 | @cache.flush 13 | end 14 | end 15 | 16 | ENV['CACHE_DEBUG'] = 'true' -------------------------------------------------------------------------------- /lib/cache/active_support_cache_dalli_store.rb: -------------------------------------------------------------------------------- 1 | require 'cache/active_support_cache_store' 2 | module Cache::ActiveSupportCacheDalliStore 3 | def self.extended(base) 4 | base.extend Cache::ActiveSupportCacheStore 5 | end 6 | 7 | def after_fork 8 | @metal.reset 9 | end 10 | 11 | def _stats 12 | @metal.stats 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /test/test_memcached_binary_storage.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | unless RUBY_PLATFORM == 'java' 4 | require 'memcached' 5 | 6 | class TestMemcachedBinaryStorage < Test::Unit::TestCase 7 | def raw_client 8 | Memcached.new 'localhost:11211', :support_cas => true, :binary => true 9 | end 10 | 11 | include SharedTests 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/cache/config.rb: -------------------------------------------------------------------------------- 1 | class Cache 2 | # Here's where config options are kept. 3 | # 4 | # Example: 5 | # cache.config.default_ttl = 120 # seconds 6 | class Config 7 | # TTL for method caches. Defaults to 60 seconds. 8 | # 9 | # Example: 10 | # cache.config.default_ttl = 120 # seconds 11 | def default_ttl=(seconds) 12 | @default_ttl = seconds 13 | end 14 | 15 | def default_ttl #:nodoc: 16 | @default_ttl || 60 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /test/test_active_support_cache_file_store.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | require 'tmpdir' 3 | require 'fileutils' 4 | 5 | require 'active_support/cache' 6 | require 'active_support/cache/file_store' 7 | 8 | class TestActiveSupportCacheFileStore < Test::Unit::TestCase 9 | def raw_client 10 | tmpdir = File.join(Dir.tmpdir, "Cache-TestActiveSupportCacheFileStore-#{rand(1e11)}") 11 | FileUtils.mkdir_p tmpdir 12 | ActiveSupport::Cache::FileStore.new tmpdir 13 | end 14 | 15 | include SharedTests 16 | end 17 | -------------------------------------------------------------------------------- /lib/cache/mem_cache.rb: -------------------------------------------------------------------------------- 1 | module Cache::MemCache 2 | def after_fork 3 | @metal.reset 4 | end 5 | 6 | def _get(k) 7 | @metal.get k 8 | end 9 | 10 | def _get_multi(ks) 11 | @metal.get_multi ks 12 | end 13 | 14 | def _set(k, v, ttl) 15 | @metal.set k, v, ttl 16 | end 17 | 18 | def _delete(k) 19 | @metal.delete k 20 | end 21 | 22 | def _flush 23 | @metal.flush_all 24 | end 25 | 26 | # sux 27 | def _exist?(k) 28 | !@metal.get(k).nil? 29 | end 30 | 31 | def _stats 32 | @metal.stats 33 | end 34 | 35 | # native 36 | def fetch(k, ttl = nil, &blk) 37 | handle_fork 38 | @metal.fetch k, extract_ttl(ttl), &blk 39 | end 40 | # -- 41 | end 42 | -------------------------------------------------------------------------------- /lib/cache/memcached_rails.rb: -------------------------------------------------------------------------------- 1 | require 'cache/memcached' 2 | module Cache::MemcachedRails 3 | def self.extended(base) 4 | base.extend Cache::Memcached 5 | base.extend Override 6 | end 7 | 8 | module Override 9 | def _exist?(k) 10 | thread_metal.exist? k 11 | # !get(k).nil? 12 | end 13 | 14 | def _get(k) 15 | thread_metal.get k 16 | end 17 | 18 | def _get_multi(ks) 19 | thread_metal.get_multi ks 20 | end 21 | 22 | def _delete(k) 23 | thread_metal.delete k 24 | end 25 | 26 | # native 27 | def cas(k, ttl = nil, &blk) 28 | handle_fork 29 | thread_metal.cas k, extract_ttl(ttl), &blk 30 | end 31 | # -- 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/cache/active_support_cache_store.rb: -------------------------------------------------------------------------------- 1 | module Cache::ActiveSupportCacheStore 2 | # native 3 | def fetch(k, ttl = nil, &blk) 4 | handle_fork 5 | @metal.fetch k, { :expires_in => extract_ttl(ttl) }, &blk 6 | end 7 | # -- 8 | 9 | def _get(k) 10 | @metal.read k 11 | end 12 | 13 | def _get_multi(ks) 14 | @metal.read_multi *ks 15 | end 16 | 17 | def _set(k, v, ttl) 18 | if ttl == 0 19 | @metal.write k, v # never expire 20 | else 21 | @metal.write k, v, :expires_in => ttl 22 | end 23 | end 24 | 25 | def _delete(k) 26 | @metal.delete k 27 | end 28 | 29 | def _flush 30 | @metal.clear 31 | end 32 | 33 | def _exist?(k) 34 | @metal.exist? k 35 | # !get(k).nil? 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/cache/dalli_client.rb: -------------------------------------------------------------------------------- 1 | module Cache::DalliClient 2 | def after_fork 3 | @metal.close 4 | end 5 | 6 | def _get(k) 7 | @metal.get k 8 | end 9 | 10 | def _get_multi(ks) 11 | @metal.get_multi ks 12 | end 13 | 14 | def _set(k, v, ttl) 15 | @metal.set k, v, ttl 16 | end 17 | 18 | def _delete(k) 19 | @metal.delete k 20 | end 21 | 22 | def _flush 23 | @metal.flush 24 | end 25 | 26 | # sux 27 | def _exist?(k) 28 | !@metal.get(k).nil? 29 | end 30 | 31 | def _stats 32 | @metal.stats 33 | end 34 | 35 | # native 36 | def fetch(k, ttl = nil, &blk) 37 | handle_fork 38 | @metal.fetch k, extract_ttl(ttl), &blk 39 | end 40 | 41 | def cas(k, ttl = nil, &blk) 42 | handle_fork 43 | @metal.cas k, extract_ttl(ttl), &blk 44 | end 45 | # -- 46 | end 47 | -------------------------------------------------------------------------------- /lib/cache/redis.rb: -------------------------------------------------------------------------------- 1 | module Cache::Redis 2 | def after_fork 3 | @metal.client.reconnect 4 | end 5 | 6 | def _get(k) 7 | if cached_v = @metal.get(k) and cached_v.is_a?(::String) 8 | ::Marshal.load cached_v 9 | end 10 | end 11 | 12 | def _get_multi(ks) 13 | ks.inject({}) do |memo, k| 14 | if v = _get(k) 15 | memo[k] = v 16 | end 17 | memo 18 | end 19 | end 20 | 21 | def _set(k, v, ttl) 22 | if ttl == 0 23 | @metal.set k, ::Marshal.dump(v) 24 | else 25 | @metal.setex k, ttl, ::Marshal.dump(v) 26 | end 27 | end 28 | 29 | def _delete(k) 30 | @metal.del k 31 | end 32 | 33 | def _flush 34 | @metal.flushdb 35 | end 36 | 37 | def _exist?(k) 38 | @metal.exists k 39 | end 40 | 41 | def _stats 42 | @metal.info 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /test/test_default_storage.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | class TestDefaultStorage < Test::Unit::TestCase 4 | def raw_client 5 | nil 6 | end 7 | 8 | def test_query 9 | assert_equal ActiveSupport::Cache::MemoryStore, @cache.metal.class 10 | end 11 | 12 | def test_default 13 | c = Cache.new 14 | assert_equal ActiveSupport::Cache::MemoryStore, c.metal.class 15 | end 16 | 17 | def test_self_reference 18 | c = Cache.new(Cache.new) 19 | assert_equal ActiveSupport::Cache::MemoryStore, c.metal.class 20 | end 21 | 22 | def test_wrap_with_default 23 | c = Cache.wrap(Cache.new) 24 | assert_equal ActiveSupport::Cache::MemoryStore, c.metal.class 25 | end 26 | 27 | def test_absurd_wrap 28 | c = Cache.new(Cache.wrap(Cache.new)) 29 | assert_equal ActiveSupport::Cache::MemoryStore, c.metal.class 30 | end 31 | 32 | include SharedTests 33 | end 34 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | 0.4.1 / 2014-12-11 2 | 3 | * Bug fixes 4 | 5 | * Properly require 'active_support' before any of its modules 6 | 7 | 0.4.0 / 2012-09-07 8 | 9 | * Breaking changes 10 | 11 | * You have to include activesupport in your Gemfile if you use the default cache store 12 | 13 | * Enhancements 14 | 15 | * Remove dependency on ActiveSupport (unless you want the default cache store) 16 | 17 | * Bug fixes 18 | 19 | * Properly handle redis forking 20 | 21 | 0.3.3 / 2012-04-17 22 | 23 | * Enhancements 24 | 25 | * Test on MRI 1.8, MRI 1.9, and JRuby 1.6.7+ 26 | 27 | * Bug fixes 28 | 29 | * Fix Redis support. Tests were silently failing, made them loud and then fixed 'em. 30 | 31 | 0.3.2 / 2012-04-11 32 | 33 | * Enhancements 34 | 35 | * Compatibility with Rails 3.2.x that uses ActiveSupport::Cache::FileStore 36 | 37 | 0.3.1 / 2012-04-10 38 | 39 | * Bug fixes 40 | 41 | * Compatibility with Rails 2.x that calls Rails.cache.logger= 42 | -------------------------------------------------------------------------------- /lib/cache/memcached.rb: -------------------------------------------------------------------------------- 1 | module Cache::Memcached 2 | def thread_metal 3 | ::Thread.current["#{@pid}/#{self.class.name}/#{object_id}/thread_metal"] ||= @metal.clone 4 | end 5 | 6 | def _get(k) 7 | thread_metal.get k 8 | rescue ::Memcached::NotFound 9 | # oh well 10 | end 11 | 12 | def _get_multi(ks) 13 | thread_metal.get ks 14 | end 15 | 16 | def _set(k, v, ttl) 17 | thread_metal.set k, v, ttl 18 | end 19 | 20 | def _delete(k) 21 | thread_metal.delete k 22 | rescue ::Memcached::NotFound 23 | end 24 | 25 | def _flush 26 | thread_metal.flush 27 | end 28 | 29 | def _exist?(k) 30 | thread_metal.get k 31 | true 32 | rescue ::Memcached::NotFound 33 | false 34 | end 35 | 36 | def _stats 37 | thread_metal.stats 38 | end 39 | 40 | # native 41 | def cas(k, ttl = nil, &blk) 42 | handle_fork 43 | thread_metal.cas k, extract_ttl(ttl), &blk 44 | rescue ::Memcached::NotFound 45 | end 46 | # -- 47 | end 48 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Seamus Abshere 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | Bundler::GemHelper.install_tasks 3 | 4 | require 'rake' 5 | require 'rake/testtask' 6 | Rake::TestTask.new(:test) do |test| 7 | test.libs << 'lib' << 'test' 8 | test.pattern = 'test/**/test_*.rb' 9 | test.verbose = true 10 | end 11 | 12 | task :default => :test 13 | 14 | # http://stackoverflow.com/questions/2138427/can-i-get-my-readme-textile-into-my-rdoc-with-proper-formatting 15 | desc "Generate RDoc" 16 | task :rdoc => ['doc:generate'] 17 | namespace :doc do 18 | project_root = File.expand_path(File.dirname(__FILE__)) 19 | doc_destination = File.join(project_root, 'rdoc') 20 | files = Dir.glob(File.join(project_root, 'lib', '**', '*.rb')) + [ File.join(project_root, 'README.md') ] 21 | begin 22 | require 'yard' 23 | require 'yard/rake/yardoc_task' 24 | 25 | YARD::Rake::YardocTask.new(:generate) do |yt| 26 | yt.files = files 27 | yt.options = ['--output-dir', doc_destination, '--readme', 'README.md'] 28 | end 29 | rescue LoadError 30 | desc "Generate YARD Documentation" 31 | task :generate do 32 | abort "Please install the YARD gem to generate rdoc." 33 | end 34 | end 35 | 36 | desc "Remove generated documenation" 37 | task :clean do 38 | rm_r doc_dir if File.exists?(doc_destination) 39 | end 40 | 41 | end 42 | -------------------------------------------------------------------------------- /cache.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require File.expand_path('../lib/cache/version', __FILE__) 3 | 4 | Gem::Specification.new do |s| 5 | s.name = "cache" 6 | s.version = Cache::VERSION 7 | s.platform = Gem::Platform::RUBY 8 | s.authors = ["Seamus Abshere","Christoph Grabo"] 9 | s.email = ["seamus@abshere.net","chris@dinarrr.com"] 10 | s.homepage = "https://github.com/seamusabshere/cache" 11 | s.summary = %q{A unified cache handling interface inspired by libraries like ActiveSupport::Cache::Store, Perl's Cache::Cache, CHI, etc.} 12 | s.description = %q{Wraps memcached, redis(-namespace), memcache-client, dalli and handles their weirdnesses, including forking} 13 | 14 | s.rubyforge_project = "cache" 15 | 16 | s.files = `git ls-files`.split("\n") 17 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 18 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 19 | s.require_paths = ["lib"] 20 | 21 | s.add_development_dependency 'activesupport', '>=2.3.11' # for default memory store 22 | s.add_development_dependency 'yard' 23 | s.add_development_dependency 'test-unit' 24 | s.add_development_dependency 'redis' 25 | s.add_development_dependency 'redis-namespace' 26 | s.add_development_dependency 'dalli' 27 | unless RUBY_PLATFORM == 'java' 28 | s.add_development_dependency 'memcached' 29 | end 30 | s.add_development_dependency 'memcache-client' 31 | s.add_development_dependency 'rack' # for ActiveSupport::Cache::FileStore of all things 32 | end 33 | 34 | -------------------------------------------------------------------------------- /test/test_dalli_storage.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | require 'dalli' 4 | 5 | class TestDalliStorage < Test::Unit::TestCase 6 | def raw_client 7 | Dalli::Client.new ['localhost:11211'] 8 | end 9 | 10 | include SharedTests 11 | 12 | def get_ring_object_id 13 | @cache.metal.instance_variable_get(:@ring).object_id 14 | end 15 | 16 | def test_treats_as_thread_safe 17 | # make sure ring is initialized 18 | @cache.get 'hi' 19 | 20 | # get the main thread's ring 21 | main_thread_ring_id = get_ring_object_id 22 | 23 | # sanity check that it's not changing every time 24 | @cache.get 'hi' 25 | assert_equal main_thread_ring_id, get_ring_object_id 26 | 27 | # create a new thread and get its ring 28 | new_thread_ring_id = Thread.new { @cache.get 'hi'; get_ring_object_id }.value 29 | 30 | # make sure the ring was reinitialized 31 | assert_equal main_thread_ring_id, new_thread_ring_id 32 | end 33 | 34 | def test_treats_as_not_fork_safe 35 | # make sure ring is initialized 36 | @cache.get 'hi' 37 | 38 | # get the main thread's ring 39 | parent_process_ring_id = get_ring_object_id 40 | 41 | # sanity check that it's not changing every time 42 | @cache.get 'hi' 43 | assert_equal parent_process_ring_id, get_ring_object_id 44 | 45 | # fork a new process 46 | pid = Kernel.fork do 47 | @cache.get 'hi' 48 | raise "Didn't split!" if parent_process_ring_id == get_ring_object_id 49 | end 50 | Process.wait pid 51 | 52 | # make sure it didn't raise 53 | assert $?.success? 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /test/test_dalli_store_storage.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | require 'dalli' 4 | require 'active_support/cache/dalli_store' 5 | 6 | class TestDalliStoreStorage < Test::Unit::TestCase 7 | def raw_client 8 | ActiveSupport::Cache::DalliStore.new ['localhost:11211'] 9 | end 10 | 11 | include SharedTests 12 | 13 | def get_ring_object_id 14 | hidden_dalli_client = @cache.metal.instance_variable_get :@data 15 | hidden_dalli_client.instance_variable_get(:@ring).object_id 16 | end 17 | 18 | def test_treats_as_thread_safe 19 | # make sure ring is initialized 20 | @cache.get 'hi' 21 | 22 | # get the main thread's ring 23 | main_thread_ring_id = get_ring_object_id 24 | 25 | # sanity check that it's not changing every time 26 | @cache.get 'hi' 27 | assert_equal main_thread_ring_id, get_ring_object_id 28 | 29 | # create a new thread and get its ring 30 | new_thread_ring_id = Thread.new { @cache.get 'hi'; get_ring_object_id }.value 31 | 32 | # make sure the ring was reinitialized 33 | assert_equal main_thread_ring_id, new_thread_ring_id 34 | end 35 | 36 | def test_treats_as_not_fork_safe 37 | # make sure ring is initialized 38 | @cache.get 'hi' 39 | 40 | # get the main thread's ring 41 | parent_process_ring_id = get_ring_object_id 42 | 43 | # sanity check that it's not changing every time 44 | @cache.get 'hi' 45 | assert_equal parent_process_ring_id, get_ring_object_id 46 | 47 | # fork a new process 48 | pid = Kernel.fork do 49 | @cache.get 'hi' 50 | raise "Didn't split!" if parent_process_ring_id == get_ring_object_id 51 | end 52 | Process.wait pid 53 | 54 | # make sure it didn't raise 55 | assert $?.success? 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /test/test_memcached_rails_storage.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | unless RUBY_PLATFORM == 'java' 4 | require 'memcached' 5 | 6 | class TestMemcachedRailsStorage < Test::Unit::TestCase 7 | def raw_client 8 | Memcached::Rails.new 'localhost:11211', :support_cas => true 9 | end 10 | 11 | include SharedTests 12 | 13 | def get_bare_id 14 | @cache.thread_metal.object_id 15 | end 16 | 17 | def test_treats_as_not_thread_safe 18 | # make sure bare client is initialized 19 | @cache.get 'hi' 20 | 21 | # get the main thread's bare client 22 | main_thread_bare_id = get_bare_id 23 | 24 | # sanity check that it's not changing every time 25 | @cache.get 'hi' 26 | assert_equal main_thread_bare_id, get_bare_id 27 | 28 | # create a new thread and get its bare client 29 | new_thread_bare_id = Thread.new { @cache.get 'hi'; get_bare_id }.value 30 | 31 | # make sure the bare client was reinitialized 32 | assert(main_thread_bare_id != new_thread_bare_id) 33 | end 34 | 35 | def test_treats_as_not_fork_safe 36 | # make sure bare client is initialized 37 | @cache.get 'hi' 38 | 39 | # get the main process's bare client 40 | parent_process_bare_id = get_bare_id 41 | 42 | # sanity check that it's not changing every time 43 | @cache.get 'hi' 44 | assert_equal parent_process_bare_id, get_bare_id 45 | 46 | # fork a new process 47 | pid = Kernel.fork do 48 | @cache.get 'hi' 49 | raise "Didn't split!" if parent_process_bare_id == get_bare_id 50 | end 51 | Process.wait pid 52 | 53 | # make sure it didn't raise 54 | assert $?.success? 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /test/test_memcache_storage.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | # the famous memcache-client 4 | require 'memcache' 5 | 6 | class TestMemcacheStorage < Test::Unit::TestCase 7 | def raw_client 8 | MemCache.new ['localhost:11211'] 9 | end 10 | 11 | include SharedTests 12 | 13 | def get_server_status_ids 14 | @cache.metal.instance_variable_get(:@servers).map { |s| s.status.object_id } 15 | end 16 | 17 | def test_treats_as_thread_safe 18 | # make sure servers are connected 19 | @cache.get 'hi' 20 | 21 | # get the object ids 22 | main_thread_server_status_ids = get_server_status_ids 23 | 24 | # sanity check that it's not changing every time 25 | @cache.get 'hi' 26 | assert_equal main_thread_server_status_ids, get_server_status_ids 27 | 28 | # create a new thread and get its server ids 29 | new_thread_server_status_ids = Thread.new { @cache.get 'hi'; get_server_status_ids }.value 30 | 31 | # make sure the server ids was reinitialized 32 | assert_equal main_thread_server_status_ids, new_thread_server_status_ids 33 | end 34 | 35 | def test_treats_as_not_fork_safe 36 | # make sure server ids is initialized 37 | @cache.get 'hi' 38 | 39 | # get the main thread's server ids 40 | parent_process_server_status_ids = get_server_status_ids 41 | 42 | # sanity check that it's not changing every time 43 | @cache.get 'hi' 44 | assert_equal parent_process_server_status_ids, get_server_status_ids 45 | 46 | # fork a new process 47 | pid = Kernel.fork do 48 | @cache.get 'hi' 49 | raise "Didn't split!" if parent_process_server_status_ids == get_server_status_ids 50 | end 51 | Process.wait pid 52 | 53 | # make sure it didn't raise 54 | assert $?.success? 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /test/test_memcached_storage.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | unless RUBY_PLATFORM == 'java' 4 | require 'memcached' 5 | 6 | class TestMemcachedStorage < Test::Unit::TestCase 7 | def raw_client 8 | Memcached.new 'localhost:11211', :support_cas => true 9 | end 10 | 11 | include SharedTests 12 | 13 | def get_bare_id 14 | @cache.thread_metal.object_id 15 | end 16 | 17 | def test_treats_as_not_thread_safe 18 | # make sure bare client is initialized 19 | @cache.get 'hi' 20 | 21 | # get the main thread's bare client 22 | main_thread_bare_id = get_bare_id 23 | 24 | # sanity check that it's not changing every time 25 | @cache.get 'hi' 26 | assert_equal main_thread_bare_id, get_bare_id 27 | 28 | # create a new thread and get its bare client 29 | new_thread_bare_id = Thread.new { @cache.get 'hi'; get_bare_id }.value 30 | 31 | # make sure the bare client was reinitialized 32 | assert(main_thread_bare_id != new_thread_bare_id) 33 | 34 | # make sure the main thread's client wasn't messed with 35 | assert_equal main_thread_bare_id, get_bare_id 36 | end 37 | 38 | def test_treats_as_not_fork_safe 39 | # make sure bare client is initialized 40 | @cache.get 'hi' 41 | 42 | # get the main process's bare client 43 | parent_process_bare_id = get_bare_id 44 | 45 | # sanity check that it's not changing every time 46 | @cache.get 'hi' 47 | assert_equal parent_process_bare_id, get_bare_id 48 | 49 | # fork a new process 50 | pid = Kernel.fork do 51 | @cache.get 'hi' 52 | raise "Didn't split!" if parent_process_bare_id == get_bare_id 53 | end 54 | Process.wait pid 55 | 56 | # make sure it didn't raise 57 | assert $?.success? 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /test/test_rails_cache_storage.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | require 'dalli' 4 | require 'active_support/cache' 5 | require 'active_support/cache/memory_store' 6 | 7 | class TestRailsCacheStorage < Test::Unit::TestCase 8 | def setup 9 | eval %{ 10 | module ::Rails 11 | def self.cache 12 | @cache || ActiveSupport::Cache::DalliStore.new(['localhost:11211']) 13 | end 14 | def self.cache=(foo) 15 | @cache = foo 16 | end 17 | end 18 | } 19 | end 20 | 21 | def teardown 22 | Object.send(:remove_const, :Rails) 23 | end 24 | 25 | def test_defaults_to_rails_cache 26 | assert_equal ActiveSupport::Cache::DalliStore, Cache.new.metal.class 27 | end 28 | 29 | def test_helpful_default 30 | eval %{ 31 | module ::Rails 32 | def self.cache 33 | @cache 34 | end 35 | end 36 | } 37 | Rails.cache = Cache.new 38 | assert_equal ActiveSupport::Cache::MemoryStore, Rails.cache.metal.class 39 | end 40 | 41 | def test_explicitly_set 42 | c = Cache.new(Rails.cache) 43 | assert_equal ActiveSupport::Cache::DalliStore, c.metal.class 44 | end 45 | 46 | # these behave strangely because they resolve the value of Rails.cache (e.g., ActiveSupport::Cache::DalliStore) before returning 47 | def test_silly_self_reference 48 | Rails.cache = Cache.new(Rails.cache) 49 | assert_equal ActiveSupport::Cache::DalliStore, Rails.cache.metal.class 50 | end 51 | 52 | def test_self_reference_twice 53 | Rails.cache = Cache.new(Cache.new) 54 | assert_equal ActiveSupport::Cache::DalliStore, Rails.cache.metal.class 55 | end 56 | 57 | def test_self_reference_with_wrap 58 | Rails.cache = Cache.wrap(Cache.new) 59 | assert_equal ActiveSupport::Cache::DalliStore, Rails.cache.metal.class 60 | end 61 | 62 | def test_self_reference_with_absurd_wrapping 63 | Rails.cache = Cache.new(Cache.wrap(Cache.new)) 64 | assert_equal ActiveSupport::Cache::DalliStore, Rails.cache.metal.class 65 | end 66 | #-- 67 | end 68 | -------------------------------------------------------------------------------- /test/test_redis_storage.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | require 'redis' 4 | require 'uri' 5 | 6 | class TestRedisStorage < Test::Unit::TestCase 7 | def raw_client 8 | Redis.new#(:host => uri.host, :port => uri.port, :password => uri.password) 9 | end 10 | 11 | include SharedTests 12 | 13 | # client DOT client 14 | def get_redis_client_connection_socket_id 15 | connection = @cache.metal.client.instance_variable_get :@connection 16 | sock = connection.instance_variable_get(:@sock) 17 | # $stderr.puts sock.inspect 18 | sock.object_id 19 | end 20 | 21 | def test_treats_as_thread_safe 22 | # make sure ring is initialized 23 | @cache.get 'hi' 24 | 25 | # get the main thread's ring 26 | main_thread_redis_client_connection_socket_id = get_redis_client_connection_socket_id 27 | 28 | # sanity check that it's not changing every time 29 | @cache.get 'hi' 30 | assert_equal main_thread_redis_client_connection_socket_id, get_redis_client_connection_socket_id 31 | 32 | # create a new thread and get its ring 33 | new_thread_redis_client_connection_socket_id = Thread.new { @cache.get 'hi'; get_redis_client_connection_socket_id }.value 34 | 35 | # make sure the ring was reinitialized 36 | assert_equal main_thread_redis_client_connection_socket_id, new_thread_redis_client_connection_socket_id 37 | end 38 | 39 | def test_treats_as_not_fork_safe 40 | # make sure ring is initialized 41 | @cache.get 'hi' 42 | 43 | # get the main thread's ring 44 | parent_process_redis_client_connection_socket_id = get_redis_client_connection_socket_id 45 | 46 | # sanity check that it's not changing every time 47 | @cache.get 'hi' 48 | assert_equal parent_process_redis_client_connection_socket_id, get_redis_client_connection_socket_id 49 | 50 | # fork a new process 51 | pid = Kernel.fork do 52 | @cache.get 'hi' 53 | raise "Didn't split!" if parent_process_redis_client_connection_socket_id == get_redis_client_connection_socket_id 54 | end 55 | Process.wait pid 56 | 57 | # make sure it didn't raise 58 | assert $?.success? 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /test/test_redis_namespace_storage.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | require 'redis' 4 | require 'redis-namespace' 5 | 6 | class TestRedisNamespaceStorage < Test::Unit::TestCase 7 | def raw_client 8 | r = Redis.new 9 | Redis::Namespace.new(:test_cache, :redis => r) 10 | end 11 | 12 | include SharedTests 13 | 14 | # client DOT client 15 | def get_redis_client_connection_socket_id 16 | connection = @cache.metal.client.instance_variable_get :@connection 17 | sock = connection.instance_variable_get(:@sock) 18 | # $stderr.puts sock.inspect 19 | sock.object_id 20 | end 21 | 22 | def test_treats_as_thread_safe 23 | # make sure ring is initialized 24 | @cache.get 'hi' 25 | 26 | # get the main thread's ring 27 | main_thread_redis_client_connection_socket_id = get_redis_client_connection_socket_id 28 | 29 | # sanity check that it's not changing every time 30 | @cache.get 'hi' 31 | assert_equal main_thread_redis_client_connection_socket_id, get_redis_client_connection_socket_id 32 | 33 | # create a new thread and get its ring 34 | new_thread_redis_client_connection_socket_id = Thread.new { @cache.get 'hi'; get_redis_client_connection_socket_id }.value 35 | 36 | # make sure the ring was reinitialized 37 | assert_equal main_thread_redis_client_connection_socket_id, new_thread_redis_client_connection_socket_id 38 | end 39 | 40 | def test_treats_as_not_fork_safe 41 | # make sure ring is initialized 42 | @cache.get 'hi' 43 | 44 | # get the main thread's ring 45 | parent_process_redis_client_connection_socket_id = get_redis_client_connection_socket_id 46 | 47 | # sanity check that it's not changing every time 48 | @cache.get 'hi' 49 | assert_equal parent_process_redis_client_connection_socket_id, get_redis_client_connection_socket_id 50 | 51 | # fork a new process 52 | pid = Kernel.fork do 53 | @cache.get 'hi' 54 | raise "Didn't split!" if parent_process_redis_client_connection_socket_id == get_redis_client_connection_socket_id 55 | end 56 | Process.wait pid 57 | 58 | # make sure it didn't raise 59 | assert $?.success? 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /test/shared_tests.rb: -------------------------------------------------------------------------------- 1 | module SharedTests 2 | def test_get 3 | assert_equal nil, @cache.get('hello') 4 | @cache.set 'hello', 'world' 5 | assert_equal 'world', @cache.get('hello') 6 | end 7 | 8 | def test_set 9 | assert_nothing_raised do 10 | @cache.set 'hello', 'world' 11 | end 12 | end 13 | 14 | def test_set_with_ttl 15 | @cache.set 'hello', 'world', 1 16 | assert_equal 'world', @cache.get('hello') 17 | sleep 2 18 | assert_equal nil, @cache.get('hello') 19 | end 20 | 21 | def test_set_with_zero_ttl_meaning_eternal 22 | @cache.set 'hello', 'world', 0 23 | assert_equal 'world', @cache.get('hello') 24 | sleep 1 25 | assert_equal 'world', @cache.get('hello') 26 | end 27 | 28 | def test_delete 29 | @cache.set 'hello', 'world' 30 | assert_equal 'world', @cache.get('hello') 31 | @cache.delete 'hello' 32 | assert_equal nil, @cache.get('hello') 33 | end 34 | 35 | def test_flush 36 | @cache.set 'hello', 'world' 37 | assert @cache.exist?('hello') 38 | @cache.flush 39 | assert !@cache.exist?('hello') 40 | end 41 | 42 | def test_exist 43 | assert !@cache.exist?('hello') 44 | @cache.set 'hello', 'world' 45 | assert @cache.exist?('hello') 46 | end 47 | 48 | def test_exist_key_with_nil_value 49 | assert !@cache.exist?('hello') 50 | @cache.set 'hello', nil 51 | assert @cache.exist?('hello') 52 | end 53 | 54 | def test_stats 55 | assert_nothing_raised do 56 | @cache.stats 57 | end 58 | end 59 | 60 | def test_fetch 61 | assert_equal nil, @cache.fetch('hello') 62 | assert_equal 'world', @cache.fetch('hello') { 'world' } 63 | end 64 | 65 | def test_fetch_with_false_boolean 66 | assert_equal nil, @cache.fetch('hello') 67 | assert_equal false, @cache.fetch('hello') { false } 68 | end 69 | 70 | def test_fetch_with_expires_in 71 | assert_equal 'world', @cache.fetch('hello', :expires_in => 5) { 'world' } 72 | end 73 | 74 | def test_fetch_with_expires_in_stringified 75 | assert_equal 'world', @cache.fetch('hello', 'expires_in' => 5) { 'world' } 76 | end 77 | 78 | def test_fetch_with_ignored_options 79 | assert_equal 'world', @cache.fetch('hello', :foo => 'bar') { 'world' } 80 | end 81 | 82 | def test_cas 83 | toggle = lambda do |current| 84 | current == 'on' ? 'off' : 'on' 85 | end 86 | 87 | @cache.set 'lights', 'on' 88 | assert_equal 'on', @cache.get('lights') 89 | @cache.cas 'lights', &toggle 90 | assert_equal 'off', @cache.get('lights') 91 | @cache.cas 'lights', &toggle 92 | assert_equal 'on', @cache.get('lights') 93 | @cache.cas 'lights', &toggle 94 | assert_equal 'off', @cache.get('lights') 95 | end 96 | 97 | def test_write 98 | @cache.write 'hello', 'world' 99 | assert_equal 'world', @cache.get('hello') 100 | end 101 | 102 | def test_write_with_expires_in 103 | @cache.write 'hello', 'world', :expires_in => 1 104 | assert_equal 'world', @cache.get('hello') 105 | sleep 2 106 | assert_equal nil, @cache.get('hello') 107 | end 108 | 109 | def test_write_with_ignored_options 110 | @cache.write 'hello', 'world', :foobar => 'bazboo' 111 | assert_equal 'world', @cache.get('hello') 112 | end 113 | 114 | def test_read 115 | @cache.set 'hello', 'world' 116 | assert_equal 'world', @cache.read('hello') 117 | end 118 | 119 | def test_increment 120 | assert !@cache.exist?('high-fives') 121 | assert_equal 1, @cache.increment('high-fives') 122 | assert_equal 1, @cache.get('high-fives') 123 | assert_equal 2, @cache.increment('high-fives') 124 | assert_equal 2, @cache.get('high-fives') 125 | end 126 | 127 | def test_decrement 128 | assert !@cache.exist?('high-fives') 129 | assert_equal -1, @cache.decrement('high-fives') 130 | assert_equal -1, @cache.get('high-fives') 131 | assert_equal -2, @cache.decrement('high-fives') 132 | assert_equal -2, @cache.get('high-fives') 133 | end 134 | 135 | def test_get_multi 136 | @cache.set 'hello', 'world' 137 | @cache.set 'privyet', 'mir' 138 | assert_equal({ 'hello' => 'world', 'privyet' => 'mir'}, @cache.get_multi('hello', 'privyet', 'yoyoyo')) 139 | end 140 | 141 | # https://github.com/fauna/memcached/pull/50 142 | def test_get_set_behavior 143 | @cache.flush 144 | @cache.get 'get_set' 145 | @cache.set 'get_set', 'go' 146 | assert_equal 'go', @cache.get('get_set') 147 | end 148 | end 149 | -------------------------------------------------------------------------------- /lib/cache.rb: -------------------------------------------------------------------------------- 1 | require 'cache/config' 2 | 3 | class Cache 4 | # Create a new Cache instance by wrapping a client of your choice. 5 | # 6 | # Defaults to an in-process memory store. 7 | # 8 | # Supported memcached clients: 9 | # * memcached[https://github.com/evan/memcached] (either a Memcached or a Memcached::Rails) 10 | # * dalli[https://github.com/mperham/dalli] (either a Dalli::Client or an ActiveSupport::Cache::DalliStore) 11 | # * memcache-client[https://github.com/mperham/memcache-client] (MemCache, the one commonly used by Rails) 12 | # 13 | # Supported Redis clients: 14 | # * redis[https://github.com/ezmobius/redis-rb] 15 | # 16 | # Example: 17 | # raw_client = Memcached.new('127.0.0.1:11211') 18 | # cache = Cache.wrap raw_client 19 | def self.wrap(metal = nil) 20 | new metal 21 | end 22 | 23 | attr_reader :config 24 | attr_reader :metal 25 | 26 | # For compatibility with Rails 2.x 27 | attr_accessor :logger 28 | 29 | def initialize(metal = nil) #:nodoc: 30 | @pid = ::Process.pid 31 | @config = Config.new 32 | @metal = if metal.is_a?(Cache) 33 | metal.metal 34 | elsif metal 35 | metal 36 | elsif defined?(::Rails) and ::Rails.respond_to?(:cache) and rails_cache = ::Rails.cache 37 | rails_cache 38 | else 39 | require 'active_support' 40 | require 'active_support/cache' 41 | require 'active_support/cache/memory_store' 42 | ::ActiveSupport::Cache::MemoryStore.new 43 | end 44 | metal_class = @metal.class.name.delete('::') # Memcached::Rails -> 'MemcachedRails' 45 | # non-ActiveSupport underscore per http://stackoverflow.com/questions/1509915/converting-camel-case-to-underscore-case-in-ruby 46 | require "cache/#{metal_class.gsub(/(.)([A-Z])/,'\1_\2').downcase}" 47 | extend Cache.const_get(metal_class) 48 | end 49 | 50 | # Get a value. 51 | # 52 | # Example: 53 | # cache.get 'hello' 54 | def get(k, ignored_options = nil) 55 | handle_fork 56 | _get k 57 | end 58 | 59 | alias :read :get 60 | 61 | # Get multiple cache entries. 62 | # 63 | # Example: 64 | # cache.get_multi 'hello', 'privyet' 65 | def get_multi(*ks) 66 | handle_fork 67 | _get_multi ks 68 | end 69 | 70 | # Store a value. Note that this will Marshal it. 71 | # 72 | # Example: 73 | # cache.set 'hello', 'world' 74 | # cache.set 'hello', 'world', 80 # seconds til it expires 75 | def set(k, v, ttl = nil, ignored_options = nil) 76 | handle_fork 77 | _set k, v, extract_ttl(ttl) 78 | end 79 | 80 | alias :write :set 81 | 82 | # Delete a value. 83 | # 84 | # Example: 85 | # cache.delete 'hello' 86 | def delete(k, ignored_options = nil) 87 | handle_fork 88 | _delete k 89 | end 90 | 91 | # Flush the cache. 92 | # 93 | # Example: 94 | # cache.flush 95 | def flush 96 | handle_fork 97 | _flush 98 | end 99 | 100 | alias :clear :flush 101 | 102 | # Check if something exists. 103 | # 104 | # Example: 105 | # cache.exist? 'hello' 106 | def exist?(k, ignored_options = nil) 107 | handle_fork 108 | _exist? k 109 | end 110 | 111 | # Increment a value. 112 | # 113 | # Example: 114 | # cache.increment 'high-fives' 115 | def increment(k, amount = 1, ignored_options = nil) 116 | handle_fork 117 | new_v = _get(k).to_i + amount 118 | _set k, new_v, 0 119 | new_v 120 | end 121 | 122 | # Decrement a value. 123 | # 124 | # Example: 125 | # cache.decrement 'high-fives' 126 | def decrement(k, amount = 1, ignored_options = nil) 127 | increment k, -amount 128 | end 129 | 130 | # Try to get a value and if it doesn't exist, set it to the result of the block. 131 | # 132 | # Accepts :expires_in for compatibility with Rails. 133 | # 134 | # Example: 135 | # cache.fetch 'hello' { 'world' } 136 | def fetch(k, ttl = nil, &blk) 137 | handle_fork 138 | if _exist? k 139 | _get k 140 | elsif blk 141 | v = blk.call 142 | _set k, v, extract_ttl(ttl) 143 | v 144 | end 145 | end 146 | 147 | # Get the current value (if any), pass it into a block, and set the result. 148 | # 149 | # Example: 150 | # cache.cas 'hello' { |current| 'world' } 151 | def cas(k, ttl = nil, &blk) 152 | handle_fork 153 | if blk and _exist?(k) 154 | old_v = _get k 155 | new_v = blk.call old_v 156 | _set k, new_v, extract_ttl(ttl) 157 | new_v 158 | end 159 | end 160 | 161 | alias :compare_and_swap :cas 162 | 163 | # Get stats. 164 | # 165 | # Example: 166 | # cache.stats 167 | def stats 168 | handle_fork 169 | _stats 170 | end 171 | 172 | private 173 | 174 | def handle_fork 175 | if ::Process.pid != @pid 176 | @pid = ::Process.pid 177 | after_fork 178 | end 179 | end 180 | 181 | def after_fork 182 | # nothing 183 | end 184 | 185 | def extract_ttl(ttl) 186 | case ttl 187 | when ::Hash 188 | ttl[:expires_in] || ttl['expires_in'] || ttl[:ttl] || ttl['ttl'] || config.default_ttl 189 | when ::NilClass 190 | config.default_ttl 191 | else 192 | ttl 193 | end.to_i 194 | end 195 | end 196 | -------------------------------------------------------------------------------- /benchmarks/afterrefactor.txt: -------------------------------------------------------------------------------- 1 | Darwin alzabo0 10.8.0 Darwin Kernel Version 10.8.0: Tue Jun 7 16:32:41 PDT 2011; root:xnu-1504.15.3~1/RELEASE_X86_64 x86_64 2 | ruby 1.9.3p125 (2012-02-16 revision 34643) [x86_64-darwin10.8.0] 3 | RUBYOPT=-ropenssl 4 | RUBY_VERSION=ruby-1.9.3-p125 5 | Ruby 1.9.3p125 6 | Loaded memcached 1.4.1 7 | Loaded memcache-client 1.8.5 8 | Loaded cache 0.2.7 9 | Loaded kgio 2.7.4 10 | Loaded dalli 2.0.2 11 | Loops is 20000 12 | Stack depth is 0 13 | Small value size is: 19 bytes 14 | Large value size is: 4189 bytes 15 | user system total real 16 | set: cache:dalli:bin 2.140000 0.840000 2.980000 ( 3.750850) 17 | set: cache:libm:bin 0.860000 0.630000 1.490000 ( 3.024450) 18 | set: dalli:bin 2.120000 0.840000 2.960000 ( 3.727571) 19 | set: libm:ascii 0.300000 0.530000 0.830000 ( 2.274182) 20 | set: libm:ascii:pipeline 0.110000 0.000000 0.110000 ( 0.111021) 21 | set: libm:ascii:udp 0.200000 0.290000 0.490000 ( 1.129215) 22 | set: libm:bin 0.200000 0.490000 0.690000 ( 1.929198) 23 | set: libm:bin:buffer 0.070000 0.170000 0.240000 ( 0.318657) 24 | set: mclient:ascii 4.870000 0.840000 5.710000 ( 5.791865) 25 | 26 | get: cache:dalli:bin 2.030000 0.910000 2.940000 ( 3.639529) 27 | get: cache:libm:bin 0.810000 0.680000 1.490000 ( 2.721883) 28 | get: dalli:bin 2.010000 0.910000 2.920000 ( 3.630398) 29 | get: libm:ascii 0.350000 0.500000 0.850000 ( 1.977741) 30 | get: libm:ascii:pipeline 0.360000 0.700000 1.060000 ( 2.278924) 31 | get: libm:ascii:udp 0.280000 0.300000 0.580000 ( 1.102862) 32 | get: libm:bin 0.260000 0.660000 0.920000 ( 1.985586) 33 | get: libm:bin:buffer 0.300000 0.890000 1.190000 ( 2.051324) 34 | get: mclient:ascii 5.710000 0.950000 6.660000 ( 6.724394) 35 | 36 | delete: cache:dalli:bin 1.820000 0.890000 2.710000 ( 3.373237) 37 | delete: cache:libm:bin 0.960000 0.590000 1.550000 ( 2.928492) 38 | delete: dalli:bin 1.790000 0.880000 2.670000 ( 3.338832) 39 | delete: libm:ascii 0.620000 0.580000 1.200000 ( 2.428661) 40 | delete: libm:ascii:pipeline 0.070000 0.000000 0.070000 ( 0.077325) 41 | delete: libm:ascii:udp 0.490000 0.360000 0.850000 ( 1.310359) 42 | delete: libm:bin 0.590000 0.560000 1.150000 ( 2.369949) 43 | delete: libm:bin:buffer 0.600000 0.830000 1.430000 ( 2.457385) 44 | delete: mclient:ascii 4.700000 0.820000 5.520000 ( 5.581853) 45 | 46 | get-missing: cache:dalli:bin 1.770000 0.880000 2.650000 ( 3.331386) 47 | get-missing: cache:libm:bin 0.970000 0.800000 1.770000 ( 2.860777) 48 | get-missing: dalli:bin 1.760000 0.880000 2.640000 ( 3.337003) 49 | get-missing: libm:ascii 0.660000 0.630000 1.290000 ( 2.495618) 50 | get-missing: libm:ascii:pipeline 0.670000 0.790000 1.460000 ( 2.523977) 51 | get-missing: libm:ascii:udp 0.540000 0.370000 0.910000 ( 1.356731) 52 | get-missing: libm:bin 0.630000 0.730000 1.360000 ( 2.530438) 53 | get-missing: libm:bin:buffer 0.650000 0.860000 1.510000 ( 2.535072) 54 | get-missing: mclient:ascii 4.840000 0.840000 5.680000 ( 5.757437) 55 | 56 | set-large: cache:dalli:bin 2.770000 0.930000 3.700000 ( 4.579959) 57 | set-large: cache:libm:bin 1.220000 0.600000 1.820000 ( 3.422516) 58 | set-large: dalli:bin 2.720000 0.930000 3.650000 ( 4.510348) 59 | set-large: libm:ascii 0.330000 0.530000 0.860000 ( 2.404037) 60 | set-large: libm:ascii:pipeline 0.160000 0.140000 0.300000 ( 0.304724) 61 | set-large: libm:ascii:udp 0.220000 0.330000 0.550000 ( 1.246952) 62 | set-large: libm:bin 0.230000 0.530000 0.760000 ( 2.118547) 63 | set-large: libm:bin:buffer 0.120000 0.250000 0.370000 ( 0.453106) 64 | set-large: mclient:ascii 5.190000 0.860000 6.050000 ( 6.136922) 65 | 66 | get-large: cache:dalli:bin 2.420000 1.020000 3.440000 ( 4.181324) 67 | get-large: cache:libm:bin 3.790000 0.900000 4.690000 ( 6.141065) 68 | get-large: dalli:bin 2.390000 1.020000 3.410000 ( 4.140461) 69 | get-large: libm:ascii 0.390000 0.640000 1.030000 ( 2.091129) 70 | get-large: libm:ascii:pipeline 0.400000 0.760000 1.160000 ( 2.050111) 71 | get-large: libm:ascii:udp 0.340000 0.440000 0.780000 ( 1.300560) 72 | get-large: libm:bin 0.330000 0.830000 1.160000 ( 2.316933) 73 | get-large: libm:bin:buffer 0.370000 1.090000 1.460000 ( 2.420931) 74 | get-large: mclient:ascii 6.800000 1.280000 8.080000 ( 8.175742) 75 | 76 | hash:default 0.190000 0.000000 0.190000 ( 0.194626) 77 | hash:md5 0.340000 0.000000 0.340000 ( 0.337795) 78 | hash:crc 0.210000 0.000000 0.210000 ( 0.214550) 79 | hash:fnv1_64 0.160000 0.000000 0.160000 ( 0.151460) 80 | hash:fnv1a_64 0.130000 0.000000 0.130000 ( 0.132338) 81 | hash:fnv1_32 0.130000 0.000000 0.130000 ( 0.131782) 82 | hash:fnv1a_32 0.130000 0.000000 0.130000 ( 0.132192) 83 | hash:hsieh 0.060000 0.000000 0.060000 ( 0.057399) 84 | hash:murmur 0.090000 0.000000 0.090000 ( 0.087435) 85 | hash:jenkins 0.100000 0.000000 0.100000 ( 0.098543) 86 | hash:none 0.060000 0.000000 0.060000 ( 0.057412) 87 | -------------------------------------------------------------------------------- /benchmarks/midrefactor.txt: -------------------------------------------------------------------------------- 1 | alzabo0:~/code/cache (master) $ ruby test/profile/benchmark.rb 2 | Darwin alzabo0 10.8.0 Darwin Kernel Version 10.8.0: Tue Jun 7 16:32:41 PDT 2011; root:xnu-1504.15.3~1/RELEASE_X86_64 x86_64 3 | ruby 1.9.3p125 (2012-02-16 revision 34643) [x86_64-darwin10.8.0] 4 | RUBYOPT=-ropenssl 5 | RUBY_VERSION=ruby-1.9.3-p125 6 | Ruby 1.9.3p125 7 | Loaded memcached 1.4.1 8 | Loaded memcache-client 1.8.5 9 | Loaded cache 0.2.6 10 | Loaded kgio 2.7.4 11 | Loaded dalli 2.0.2 12 | Loops is 20000 13 | Stack depth is 0 14 | Small value size is: 19 bytes 15 | Large value size is: 4189 bytes 16 | No matching processes belonging to you were found 17 | user system total real 18 | set: cache:dalli:bin 2.200000 0.830000 3.030000 ( 3.818512) 19 | set: cache:libm:bin 0.630000 0.550000 1.180000 ( 2.579593) 20 | set: dalli:bin 2.080000 0.830000 2.910000 ( 3.709548) 21 | set: libm:ascii 0.290000 0.520000 0.810000 ( 2.175071) 22 | set: libm:ascii:pipeline 0.100000 0.010000 0.110000 ( 0.105957) 23 | set: libm:ascii:udp 0.200000 0.310000 0.510000 ( 1.116366) 24 | set: libm:bin 0.190000 0.480000 0.670000 ( 1.779256) 25 | set: libm:bin:buffer 0.080000 0.160000 0.240000 ( 0.313709) 26 | set: mclient:ascii 4.310000 0.830000 5.140000 ( 5.211834) 27 | 28 | get: cache:dalli:bin 2.070000 0.900000 2.970000 ( 3.678416) 29 | get: cache:libm:bin 0.580000 0.680000 1.260000 ( 2.372081) 30 | get: dalli:bin 1.940000 0.900000 2.840000 ( 3.548862) 31 | get: libm:ascii 0.340000 0.490000 0.830000 ( 1.850806) 32 | get: libm:ascii:pipeline 0.350000 0.680000 1.030000 ( 2.187457) 33 | get: libm:ascii:udp 0.260000 0.330000 0.590000 ( 1.062001) 34 | get: libm:bin 0.260000 0.670000 0.930000 ( 1.963878) 35 | get: libm:bin:buffer 0.300000 0.930000 1.230000 ( 2.021582) 36 | get: mclient:ascii 5.120000 0.850000 5.970000 ( 6.057993) 37 | 38 | delete: cache:dalli:bin 1.900000 0.880000 2.780000 ( 3.450546) 39 | delete: cache:libm:bin 0.720000 0.670000 1.390000 ( 2.628000) 40 | delete: dalli:bin 1.730000 0.870000 2.600000 ( 3.278894) 41 | delete: libm:ascii 0.610000 0.580000 1.190000 ( 2.382143) 42 | delete: libm:ascii:pipeline 0.070000 0.000000 0.070000 ( 0.077245) 43 | delete: libm:ascii:udp 0.500000 0.380000 0.880000 ( 1.311811) 44 | delete: libm:bin 0.590000 0.570000 1.160000 ( 2.325502) 45 | delete: libm:bin:buffer 0.580000 0.810000 1.390000 ( 2.347620) 46 | delete: mclient:ascii 4.150000 0.810000 4.960000 ( 5.022691) 47 | 48 | get-missing: cache:dalli:bin 1.830000 0.870000 2.700000 ( 3.410744) 49 | get-missing: cache:libm:bin 0.730000 0.730000 1.460000 ( 2.553246) 50 | get-missing: dalli:bin 1.710000 0.870000 2.580000 ( 3.294497) 51 | get-missing: libm:ascii 0.670000 0.650000 1.320000 ( 2.522556) 52 | get-missing: libm:ascii:pipeline 0.650000 0.780000 1.430000 ( 2.408539) 53 | get-missing: libm:ascii:udp 0.530000 0.390000 0.920000 ( 1.347747) 54 | get-missing: libm:bin 0.640000 0.730000 1.370000 ( 2.514046) 55 | get-missing: libm:bin:buffer 0.650000 0.880000 1.530000 ( 2.530509) 56 | get-missing: mclient:ascii 4.230000 0.820000 5.050000 ( 5.119931) 57 | 58 | set-large: cache:dalli:bin 2.560000 0.880000 3.440000 ( 4.314613) 59 | set-large: cache:libm:bin 0.900000 0.650000 1.550000 ( 3.082473) 60 | set-large: dalli:bin 2.450000 0.870000 3.320000 ( 4.192634) 61 | set-large: libm:ascii 0.320000 0.520000 0.840000 ( 2.383214) 62 | set-large: libm:ascii:pipeline 0.160000 0.150000 0.310000 ( 0.313549) 63 | set-large: libm:ascii:udp 0.230000 0.360000 0.590000 ( 1.254392) 64 | set-large: libm:bin 0.230000 0.520000 0.750000 ( 2.106144) 65 | set-large: libm:bin:buffer 0.130000 0.250000 0.380000 ( 0.448009) 66 | set-large: mclient:ascii 4.540000 0.860000 5.400000 ( 5.492006) 67 | 68 | get-large: cache:dalli:bin 2.320000 0.980000 3.300000 ( 4.028261) 69 | get-large: cache:libm:bin 3.480000 0.850000 4.330000 ( 5.709700) 70 | get-large: dalli:bin 2.200000 0.980000 3.180000 ( 3.916877) 71 | get-large: libm:ascii 0.380000 0.640000 1.020000 ( 2.054989) 72 | get-large: libm:ascii:pipeline 0.380000 0.760000 1.140000 ( 1.999273) 73 | get-large: libm:ascii:udp 0.330000 0.460000 0.790000 ( 1.286472) 74 | get-large: libm:bin 0.330000 0.860000 1.190000 ( 2.282758) 75 | get-large: libm:bin:buffer 0.360000 1.090000 1.450000 ( 2.388201) 76 | get-large: mclient:ascii 5.870000 1.070000 6.940000 ( 7.044938) 77 | 78 | hash:default 0.190000 0.000000 0.190000 ( 0.198824) 79 | hash:md5 0.370000 0.000000 0.370000 ( 0.361487) 80 | hash:crc 0.210000 0.000000 0.210000 ( 0.219091) 81 | hash:fnv1_64 0.150000 0.000000 0.150000 ( 0.155696) 82 | hash:fnv1a_64 0.140000 0.000000 0.140000 ( 0.135878) 83 | hash:fnv1_32 0.140000 0.000000 0.140000 ( 0.138691) 84 | hash:fnv1a_32 0.130000 0.000000 0.130000 ( 0.136036) 85 | hash:hsieh 0.060000 0.000000 0.060000 ( 0.059796) 86 | hash:murmur 0.090000 0.000000 0.090000 ( 0.088340) 87 | hash:jenkins 0.100000 0.000000 0.100000 ( 0.102807) 88 | hash:none 0.050000 0.000000 0.050000 ( 0.059193) 89 | alzabo0:~/code/cache (master) $ 90 | -------------------------------------------------------------------------------- /benchmarks/v0.0.2.txt: -------------------------------------------------------------------------------- 1 | # sabshere 2/22/11 v0.0.2 2 | 3 | Darwin vidalia 9.8.0 Darwin Kernel Version 9.8.0: Wed Jul 15 16:55:01 PDT 2009; root:xnu-1228.15.4~1/RELEASE_I386 i386 4 | ruby 1.8.7 (2010-05-25 patchlevel 266) [i686-darwin9.8.0] 5 | RUBY_VERSION=ruby-1.8.7-head 6 | Ruby 1.8.7p266 7 | Loaded memcached 1.0.6 8 | Loaded remix-stash 1.1.3 9 | Loaded memcache-client 1.8.5 10 | Loaded cache 0.0.2 11 | Loaded kgio 2.3.2 12 | Loaded dalli 1.0.2 13 | Loops is 20000 14 | Stack depth is 0 15 | Small value size is: 13 bytes 16 | Large value size is: 4158 bytes 17 | No matching processes belonging to you were found 18 | user system total real 19 | set: cache:dalli:bin 6.340000 1.880000 8.220000 ( 10.948888) 20 | set: cache:libm:bin 5.730000 1.530000 7.260000 ( 10.640067) 21 | set: dalli:bin 5.580000 1.860000 7.440000 ( 10.092303) 22 | set: libm:ascii 0.840000 1.290000 2.130000 ( 5.445700) 23 | set: libm:ascii:pipeline 0.340000 0.020000 0.360000 ( 0.362669) 24 | set: libm:ascii:udp 0.710000 0.710000 1.420000 ( 3.642532) 25 | set: libm:bin 0.710000 1.320000 2.030000 ( 5.339189) 26 | set: libm:bin:buffer 0.300000 0.110000 0.410000 ( 1.107518) 27 | set: mclient:ascii 10.700000 3.670000 14.370000 ( 14.945212) 28 | set: stash:bin 3.710000 1.370000 5.080000 ( 8.162312) 29 | 30 | get: cache:dalli:bin 6.820000 2.080000 8.900000 ( 11.412364) 31 | get: cache:libm:bin 5.670000 1.490000 7.160000 ( 10.392625) 32 | get: dalli:bin 5.840000 2.090000 7.930000 ( 10.308551) 33 | get: libm:ascii 0.970000 1.310000 2.280000 ( 5.562719) 34 | get: libm:ascii:pipeline 1.060000 1.620000 2.680000 ( 5.881988) 35 | get: libm:ascii:udp 0.820000 0.720000 1.540000 ( 3.585481) 36 | get: libm:bin 0.810000 1.330000 2.140000 ( 5.310565) 37 | get: libm:bin:buffer 0.940000 1.630000 2.570000 ( 5.677996) 38 | get: mclient:ascii 12.680000 3.690000 16.370000 ( 16.930786) 39 | get: stash:bin 3.390000 1.370000 4.760000 ( 7.658067) 40 | 41 | delete: cache:dalli:bin 6.190000 2.080000 8.270000 ( 10.697357) 42 | delete: cache:libm:bin 6.080000 1.580000 7.660000 ( 10.938304) 43 | delete: dalli:bin 5.190000 2.080000 7.270000 ( 9.694417) 44 | delete: libm:ascii 1.790000 1.400000 3.190000 ( 6.452993) 45 | delete: libm:ascii:pipeline 0.550000 0.620000 1.170000 ( 1.226934) 46 | delete: libm:ascii:udp 1.620000 0.870000 2.490000 ( 4.440414) 47 | delete: libm:bin 1.720000 1.400000 3.120000 ( 6.322207) 48 | delete: libm:bin:buffer 0.460000 0.520000 0.980000 ( 1.651911) 49 | delete: mclient:ascii 10.610000 3.700000 14.310000 ( 14.815498) 50 | delete:stash:bin => #> 51 | 52 | get-missing: cache:dalli:bin 6.300000 2.110000 8.410000 ( 10.962403) 53 | get-missing: cache:libm:bin 7.090000 1.780000 8.870000 ( 12.435441) 54 | get-missing: dalli:bin 5.730000 2.260000 7.990000 ( 10.758698) 55 | get-missing: libm:ascii 2.070000 1.550000 3.620000 ( 7.211442) 56 | get-missing: libm:ascii:pipeline 2.190000 1.920000 4.110000 ( 7.549889) 57 | get-missing: libm:ascii:udp 1.820000 0.930000 2.750000 ( 4.887171) 58 | get-missing: libm:bin 1.990000 1.510000 3.500000 ( 6.837518) 59 | get-missing: libm:bin:buffer 2.050000 1.830000 3.880000 ( 7.134244) 60 | get-missing: mclient:ascii 11.160000 3.790000 14.950000 ( 15.550381) 61 | get-missing: stash:bin 3.240000 1.390000 4.630000 ( 7.602964) 62 | 63 | set-large: cache:dalli:bin 8.160000 2.010000 10.170000 ( 13.146422) 64 | set-large: cache:libm:bin 6.490000 1.610000 8.100000 ( 11.726796) 65 | set-large: dalli:bin 7.490000 2.030000 9.520000 ( 12.402519) 66 | set-large: libm:ascii 0.920000 1.400000 2.320000 ( 5.925636) 67 | set-large: libm:ascii:pipeline 0.630000 0.450000 1.080000 ( 1.246708) 68 | set-large: libm:ascii:udp 0.790000 0.820000 1.610000 ( 4.095793) 69 | set-large: libm:bin 0.810000 1.440000 2.250000 ( 5.770076) 70 | set-large: libm:bin:buffer 0.570000 0.640000 1.210000 ( 2.286722) 71 | set-large: mclient:ascii 11.940000 3.910000 15.850000 ( 17.592266) 72 | set-large: stash:bin 6.250000 1.510000 7.760000 ( 13.191809) 73 | 74 | get-large: cache:dalli:bin 8.320000 2.460000 10.780000 ( 13.958967) 75 | get-large: cache:libm:bin 10.740000 1.760000 12.500000 ( 16.610462) 76 | get-large: dalli:bin 7.270000 2.460000 9.730000 ( 12.867557) 77 | get-large: libm:ascii 1.590000 1.540000 3.130000 ( 7.126347) 78 | get-large: libm:ascii:pipeline 1.700000 1.900000 3.600000 ( 7.440423) 79 | get-large: libm:ascii:udp 1.420000 0.950000 2.370000 ( 4.683143) 80 | get-large: libm:bin 1.440000 1.580000 3.020000 ( 6.955872) 81 | get-large: libm:bin:buffer 1.550000 1.920000 3.470000 ( 7.199426) 82 | get-large: mclient:ascii 15.460000 4.560000 20.020000 ( 20.855362) 83 | get-large: stash:bin 3.610000 1.450000 5.060000 ( 8.103802) 84 | 85 | hash:jenkins 0.660000 0.010000 0.670000 ( 0.660554) 86 | hash:hsieh 0.310000 0.000000 0.310000 ( 0.323458) 87 | hash:default 0.620000 0.000000 0.620000 ( 0.616587) 88 | hash:fnv1_32 0.620000 0.000000 0.620000 ( 0.631255) 89 | hash:fnv1_64 1.280000 0.010000 1.290000 ( 1.291580) 90 | hash:none 0.320000 0.000000 0.320000 ( 0.324198) 91 | hash:md5 1.030000 0.000000 1.030000 ( 1.046663) 92 | hash:murmur 0.560000 0.010000 0.570000 ( 0.574408) 93 | hash:fnv1a_32 0.660000 0.000000 0.660000 ( 0.670788) 94 | hash:fnv1a_64 0.690000 0.000000 0.690000 ( 0.701477) 95 | hash:crc 0.640000 0.010000 0.650000 ( 0.646991) 96 | -------------------------------------------------------------------------------- /benchmarks/v0.1.2.txt: -------------------------------------------------------------------------------- 1 | vidalia:~/github/cache (master) $ ruby test/profile/benchmark.rb 2 | Darwin vidalia 9.8.0 Darwin Kernel Version 9.8.0: Wed Jul 15 16:55:01 PDT 2009; root:xnu-1228.15.4~1/RELEASE_I386 i386 3 | ruby 1.8.7 (2010-05-25 patchlevel 266) [i686-darwin9.8.0] 4 | RUBY_VERSION=ruby-1.8.7-head 5 | Ruby 1.8.7p266 6 | Loaded memcached 1.2 7 | Loaded remix-stash 1.1.3 8 | Loaded memcache-client 1.8.5 9 | Loaded cache 0.1.2 10 | Loaded kgio 2.3.2 11 | Loaded dalli 1.0.2 12 | Loops is 20000 13 | Stack depth is 0 14 | Small value size is: 13 bytes 15 | Large value size is: 4158 bytes 16 | No matching processes belonging to you were found 17 | user system total real 18 | set: cache:dalli:bin 5.720000 1.860000 7.580000 ( 10.301808) 19 | set: cache:libm:bin 1.250000 1.270000 2.520000 ( 5.892224) 20 | set: dalli:bin 5.430000 1.860000 7.290000 ( 9.966409) 21 | set: libm:ascii 0.760000 1.330000 2.090000 ( 5.348975) 22 | set: libm:ascii:pipeline 0.280000 0.010000 0.290000 ( 0.297230) 23 | set: libm:ascii:udp 0.650000 0.710000 1.360000 ( 3.593454) 24 | set: libm:bin 0.640000 1.360000 2.000000 ( 5.285160) 25 | set: libm:bin:buffer 0.270000 0.160000 0.430000 ( 1.234561) 26 | set: mclient:ascii 11.330000 3.780000 15.110000 ( 16.174666) 27 | set: stash:bin 3.420000 1.330000 4.750000 ( 8.016146) 28 | 29 | get: cache:dalli:bin 5.870000 1.950000 7.820000 ( 10.644558) 30 | get: cache:libm:bin 1.370000 1.200000 2.570000 ( 5.946502) 31 | get: dalli:bin 5.580000 2.070000 7.650000 ( 10.361689) 32 | get: libm:ascii 0.980000 1.300000 2.280000 ( 5.455222) 33 | get: libm:ascii:pipeline 1.030000 1.590000 2.620000 ( 5.875592) 34 | get: libm:ascii:udp 0.820000 0.730000 1.550000 ( 3.515632) 35 | get: libm:bin 0.830000 1.330000 2.160000 ( 5.381290) 36 | get: libm:bin:buffer 0.900000 1.630000 2.530000 ( 5.761412) 37 | get: mclient:ascii 13.630000 3.870000 17.500000 ( 17.912800) 38 | get: stash:bin 3.100000 1.340000 4.440000 ( 7.667182) 39 | 40 | delete: cache:dalli:bin 6.270000 2.390000 8.660000 ( 11.313843) 41 | delete: cache:libm:bin 2.190000 1.560000 3.750000 ( 7.459292) 42 | delete: dalli:bin 5.660000 2.340000 8.000000 ( 10.374507) 43 | delete: libm:ascii 1.850000 1.570000 3.420000 ( 6.922356) 44 | delete: libm:ascii:pipeline 0.230000 0.010000 0.240000 ( 0.242642) 45 | delete: libm:ascii:udp 1.690000 0.930000 2.620000 ( 4.749489) 46 | delete: libm:bin 1.780000 1.530000 3.310000 ( 6.768449) 47 | delete: libm:bin:buffer 1.890000 1.880000 3.770000 ( 7.149319) 48 | delete: mclient:ascii 11.940000 4.040000 15.980000 ( 16.423645) 49 | delete:stash:bin => #> 50 | 51 | get-missing: cache:dalli:bin 5.530000 2.140000 7.670000 ( 10.187561) 52 | get-missing: cache:libm:bin 2.200000 1.510000 3.710000 ( 6.953625) 53 | get-missing: dalli:bin 5.230000 2.160000 7.390000 ( 9.891539) 54 | get-missing: libm:ascii 1.960000 1.420000 3.380000 ( 6.459433) 55 | get-missing: libm:ascii:pipeline 2.080000 1.860000 3.940000 ( 6.984890) 56 | get-missing: libm:ascii:udp 1.730000 0.910000 2.640000 ( 4.582510) 57 | get-missing: libm:bin 1.950000 1.470000 3.420000 ( 6.854775) 58 | get-missing: libm:bin:buffer 2.060000 1.900000 3.960000 ( 7.180759) 59 | get-missing: mclient:ascii 12.350000 4.100000 16.450000 ( 16.986064) 60 | get-missing: stash:bin 3.110000 1.410000 4.520000 ( 7.908972) 61 | 62 | set-large: cache:dalli:bin 8.650000 2.110000 10.760000 ( 13.864690) 63 | set-large: cache:libm:bin 2.600000 1.450000 4.050000 ( 7.811509) 64 | set-large: dalli:bin 8.250000 2.100000 10.350000 ( 13.370233) 65 | set-large: libm:ascii 0.890000 1.460000 2.350000 ( 6.069504) 66 | set-large: libm:ascii:pipeline 0.570000 0.450000 1.020000 ( 1.232906) 67 | set-large: libm:ascii:udp 0.730000 0.850000 1.580000 ( 4.174216) 68 | set-large: libm:bin 0.760000 1.510000 2.270000 ( 5.966586) 69 | set-large: libm:bin:buffer 0.530000 0.700000 1.230000 ( 2.419153) 70 | set-large: mclient:ascii 13.540000 4.250000 17.790000 ( 18.447711) 71 | set-large: stash:bin 6.100000 1.500000 7.600000 ( 11.216811) 72 | 73 | get-large: cache:dalli:bin 8.000000 2.480000 10.480000 ( 13.450322) 74 | get-large: cache:libm:bin 6.820000 1.490000 8.310000 ( 12.331486) 75 | get-large: dalli:bin 7.670000 2.490000 10.160000 ( 13.148162) 76 | get-large: libm:ascii 1.740000 1.540000 3.280000 ( 7.256110) 77 | get-large: libm:ascii:pipeline 1.870000 1.960000 3.830000 ( 7.723493) 78 | get-large: libm:ascii:udp 1.560000 0.980000 2.540000 ( 4.823281) 79 | get-large: libm:bin 1.590000 1.590000 3.180000 ( 7.018376) 80 | get-large: libm:bin:buffer 1.730000 1.960000 3.690000 ( 7.365047) 81 | get-large: mclient:ascii 17.160000 4.890000 22.050000 ( 22.647077) 82 | get-large: stash:bin 3.290000 1.440000 4.730000 ( 7.950154) 83 | 84 | hash:jenkins 0.550000 0.000000 0.550000 ( 0.561738) 85 | hash:default 0.550000 0.000000 0.550000 ( 0.553209) 86 | hash:crc 0.660000 0.000000 0.660000 ( 0.657155) 87 | hash:fnv1_32 0.530000 0.010000 0.540000 ( 0.529303) 88 | hash:hsieh 0.280000 0.000000 0.280000 ( 0.281685) 89 | hash:fnv1_64 1.160000 0.000000 1.160000 ( 1.169524) 90 | hash:none 0.310000 0.000000 0.310000 ( 0.313095) 91 | hash:murmur 0.450000 0.000000 0.450000 ( 0.454460) 92 | hash:md5 0.950000 0.010000 0.960000 ( 0.949226) 93 | hash:fnv1a_64 0.590000 0.000000 0.590000 ( 0.590362) 94 | hash:fnv1a_32 0.590000 0.000000 0.590000 ( 0.599005) 95 | -------------------------------------------------------------------------------- /benchmarks/v0.2.1.txt: -------------------------------------------------------------------------------- 1 | vidalia:~/github/cache (master) $ ruby test/profile/benchmark.rb 2 | Darwin vidalia 9.8.0 Darwin Kernel Version 9.8.0: Wed Jul 15 16:55:01 PDT 2009; root:xnu-1228.15.4~1/RELEASE_I386 i386 3 | ruby 1.8.7 (2010-05-25 patchlevel 266) [i686-darwin9.8.0] 4 | RUBY_VERSION=ruby-1.8.7-head 5 | Ruby 1.8.7p266 6 | Loaded memcached 1.2.1 7 | Loaded remix-stash 1.1.3 8 | Loaded memcache-client 1.8.5 9 | Loaded cache 0.2.1 10 | Loaded kgio 2.3.2 11 | Loaded dalli 1.0.2 12 | Loops is 20000 13 | Stack depth is 0 14 | Small value size is: 13 bytes 15 | Large value size is: 4158 bytes 16 | No matching processes belonging to you were found 17 | user system total real 18 | set: cache:dalli:bin 5.720000 1.870000 7.590000 ( 10.245466) 19 | set: cache:libm:bin 1.320000 1.260000 2.580000 ( 5.921810) 20 | set: dalli:bin 5.360000 1.870000 7.230000 ( 9.871376) 21 | set: libm:ascii 0.770000 1.310000 2.080000 ( 5.365856) 22 | set: libm:ascii:pipeline 0.280000 0.020000 0.300000 ( 0.296948) 23 | set: libm:ascii:udp 0.630000 0.690000 1.320000 ( 3.624834) 24 | set: libm:bin 0.640000 1.380000 2.020000 ( 5.280843) 25 | set: libm:bin:buffer 0.280000 0.180000 0.460000 ( 1.215491) 26 | set: mclient:ascii 11.840000 3.800000 15.640000 ( 15.882581) 27 | set: stash:bin 3.450000 1.320000 4.770000 ( 7.903133) 28 | 29 | get: cache:dalli:bin 5.730000 2.040000 7.770000 ( 10.224065) 30 | get: cache:libm:bin 1.340000 1.220000 2.560000 ( 5.813579) 31 | get: dalli:bin 5.460000 2.060000 7.520000 ( 9.941512) 32 | get: libm:ascii 0.970000 1.300000 2.270000 ( 5.396642) 33 | get: libm:ascii:pipeline 1.030000 1.590000 2.620000 ( 5.773589) 34 | get: libm:ascii:udp 0.790000 0.720000 1.510000 ( 3.391872) 35 | get: libm:bin 0.820000 1.340000 2.160000 ( 5.363674) 36 | get: libm:bin:buffer 0.920000 1.690000 2.610000 ( 5.658819) 37 | get: mclient:ascii 14.410000 3.980000 18.390000 ( 18.660373) 38 | get: stash:bin 3.310000 1.420000 4.730000 ( 7.973290) 39 | 40 | delete: cache:dalli:bin 5.850000 2.250000 8.100000 ( 10.467114) 41 | delete: cache:libm:bin 2.090000 1.530000 3.620000 ( 7.063066) 42 | delete: dalli:bin 5.370000 2.250000 7.620000 ( 9.882647) 43 | delete: libm:ascii 1.780000 1.500000 3.280000 ( 6.717891) 44 | delete: libm:ascii:pipeline 0.220000 0.010000 0.230000 ( 0.240083) 45 | delete: libm:ascii:udp 1.570000 0.900000 2.470000 ( 4.588112) 46 | delete: libm:bin 1.740000 1.480000 3.220000 ( 6.629920) 47 | delete: libm:bin:buffer 1.860000 1.890000 3.750000 ( 6.899297) 48 | delete: mclient:ascii 12.090000 3.990000 16.080000 ( 16.328276) 49 | delete:stash:bin => #> 50 | 51 | get-missing: cache:dalli:bin 5.460000 2.160000 7.620000 ( 10.150363) 52 | get-missing: cache:libm:bin 2.230000 1.500000 3.730000 ( 6.989031) 53 | get-missing: dalli:bin 5.120000 2.160000 7.280000 ( 9.790094) 54 | get-missing: libm:ascii 1.950000 1.410000 3.360000 ( 6.530576) 55 | get-missing: libm:ascii:pipeline 2.050000 1.830000 3.880000 ( 6.787680) 56 | get-missing: libm:ascii:udp 1.760000 0.900000 2.660000 ( 4.657937) 57 | get-missing: libm:bin 1.940000 1.490000 3.430000 ( 6.782449) 58 | get-missing: libm:bin:buffer 2.060000 1.920000 3.980000 ( 7.178149) 59 | get-missing: mclient:ascii 12.630000 4.060000 16.690000 ( 16.921560) 60 | get-missing: stash:bin 3.060000 1.380000 4.440000 ( 7.676172) 61 | 62 | set-large: cache:dalli:bin 8.640000 2.120000 10.760000 ( 13.677315) 63 | set-large: cache:libm:bin 2.720000 1.440000 4.160000 ( 7.890247) 64 | set-large: dalli:bin 8.260000 2.120000 10.380000 ( 13.237021) 65 | set-large: libm:ascii 0.900000 1.460000 2.360000 ( 6.131867) 66 | set-large: libm:ascii:pipeline 0.580000 0.460000 1.040000 ( 1.293182) 67 | set-large: libm:ascii:udp 0.720000 0.840000 1.560000 ( 4.197623) 68 | set-large: libm:bin 0.770000 1.500000 2.270000 ( 5.938192) 69 | set-large: libm:bin:buffer 0.530000 0.710000 1.240000 ( 2.383338) 70 | set-large: mclient:ascii 13.790000 4.210000 18.000000 ( 18.322264) 71 | set-large: stash:bin 6.150000 1.440000 7.590000 ( 10.888669) 72 | 73 | get-large: cache:dalli:bin 8.080000 2.510000 10.590000 ( 13.538810) 74 | get-large: cache:libm:bin 6.870000 1.460000 8.330000 ( 12.279483) 75 | get-large: dalli:bin 7.630000 2.470000 10.100000 ( 13.012382) 76 | get-large: libm:ascii 1.730000 1.500000 3.230000 ( 7.078254) 77 | get-large: libm:ascii:pipeline 1.870000 1.930000 3.800000 ( 7.427236) 78 | get-large: libm:ascii:udp 1.580000 0.950000 2.530000 ( 4.793014) 79 | get-large: libm:bin 1.600000 1.560000 3.160000 ( 6.958339) 80 | get-large: libm:bin:buffer 1.760000 1.960000 3.720000 ( 7.333753) 81 | get-large: mclient:ascii 17.450000 4.840000 22.290000 ( 22.647341) 82 | get-large: stash:bin 3.350000 1.420000 4.770000 ( 8.034720) 83 | 84 | hash:jenkins 0.520000 0.000000 0.520000 ( 0.528327) 85 | hash:default 0.580000 0.000000 0.580000 ( 0.574388) 86 | hash:crc 0.620000 0.000000 0.620000 ( 0.623146) 87 | hash:fnv1_32 0.530000 0.000000 0.530000 ( 0.536423) 88 | hash:hsieh 0.320000 0.000000 0.320000 ( 0.316024) 89 | hash:fnv1_64 1.150000 0.000000 1.150000 ( 1.151304) 90 | hash:none 0.290000 0.000000 0.290000 ( 0.296099) 91 | hash:murmur 0.460000 0.000000 0.460000 ( 0.458312) 92 | hash:md5 0.990000 0.000000 0.990000 ( 0.995194) 93 | hash:fnv1a_64 0.590000 0.000000 0.590000 ( 0.584158) 94 | hash:fnv1a_32 0.580000 0.000000 0.580000 ( 0.593039) 95 | -------------------------------------------------------------------------------- /benchmarks/v0.2.2.txt: -------------------------------------------------------------------------------- 1 | vidalia:~/github/cache (master) $ ruby test/profile/benchmark.rb 2 | Darwin vidalia 9.8.0 Darwin Kernel Version 9.8.0: Wed Jul 15 16:55:01 PDT 2009; root:xnu-1228.15.4~1/RELEASE_I386 i386 3 | ruby 1.8.7 (2010-05-25 patchlevel 266) [i686-darwin9.8.0] 4 | RUBY_VERSION=ruby-1.8.7-head 5 | Ruby 1.8.7p266 6 | Loaded memcached 1.2.1 7 | Loaded remix-stash 1.1.3 8 | Loaded memcache-client 1.8.5 9 | Loaded cache 0.2.2 10 | Loaded kgio 2.3.2 11 | Loaded dalli 1.0.2 12 | Loops is 20000 13 | Stack depth is 0 14 | Small value size is: 13 bytes 15 | Large value size is: 4158 bytes 16 | No matching processes belonging to you were found 17 | user system total real 18 | set: cache:dalli:bin 5.710000 1.870000 7.580000 ( 10.210710) 19 | set: cache:libm:bin 1.320000 1.260000 2.580000 ( 5.913591) 20 | set: dalli:bin 5.350000 1.860000 7.210000 ( 9.860368) 21 | set: libm:ascii 0.760000 1.310000 2.070000 ( 5.369027) 22 | set: libm:ascii:pipeline 0.280000 0.020000 0.300000 ( 0.300872) 23 | set: libm:ascii:udp 0.640000 0.690000 1.330000 ( 3.618846) 24 | set: libm:bin 0.640000 1.370000 2.010000 ( 5.287203) 25 | set: libm:bin:buffer 0.320000 0.170000 0.490000 ( 1.238471) 26 | set: mclient:ascii 11.840000 3.820000 15.660000 ( 15.933338) 27 | set: stash:bin 3.420000 1.300000 4.720000 ( 7.871299) 28 | 29 | get: cache:dalli:bin 5.740000 2.050000 7.790000 ( 10.220809) 30 | get: cache:libm:bin 1.330000 1.260000 2.590000 ( 5.789277) 31 | get: dalli:bin 5.430000 2.050000 7.480000 ( 9.945485) 32 | get: libm:ascii 0.970000 1.290000 2.260000 ( 5.421878) 33 | get: libm:ascii:pipeline 1.030000 1.590000 2.620000 ( 5.728829) 34 | get: libm:ascii:udp 0.790000 0.730000 1.520000 ( 3.393461) 35 | get: libm:bin 0.830000 1.330000 2.160000 ( 5.362280) 36 | get: libm:bin:buffer 0.900000 1.640000 2.540000 ( 5.719478) 37 | get: mclient:ascii 14.010000 3.860000 17.870000 ( 18.125730) 38 | get: stash:bin 3.100000 1.320000 4.420000 ( 7.559659) 39 | 40 | delete: cache:dalli:bin 6.680000 2.580000 9.260000 ( 11.757354) 41 | delete: cache:libm:bin 2.220000 1.600000 3.820000 ( 7.429178) 42 | delete: dalli:bin 5.670000 2.380000 8.050000 ( 10.352835) 43 | delete: libm:ascii 1.850000 1.550000 3.400000 ( 6.950162) 44 | delete: libm:ascii:pipeline 0.220000 0.010000 0.230000 ( 0.235296) 45 | delete: libm:ascii:udp 1.630000 0.930000 2.560000 ( 4.708250) 46 | delete: libm:bin 1.790000 1.520000 3.310000 ( 6.789130) 47 | delete: libm:bin:buffer 1.910000 1.850000 3.760000 ( 7.065142) 48 | delete: mclient:ascii 12.110000 4.000000 16.110000 ( 16.356892) 49 | delete:stash:bin => #> 50 | 51 | get-missing: cache:dalli:bin 5.390000 2.140000 7.530000 ( 9.990644) 52 | get-missing: cache:libm:bin 2.200000 1.450000 3.650000 ( 6.940150) 53 | get-missing: dalli:bin 5.070000 2.130000 7.200000 ( 9.662024) 54 | get-missing: libm:ascii 1.940000 1.380000 3.320000 ( 6.361626) 55 | get-missing: libm:ascii:pipeline 2.030000 1.830000 3.860000 ( 6.720970) 56 | get-missing: libm:ascii:udp 1.740000 0.890000 2.630000 ( 4.607945) 57 | get-missing: libm:bin 1.920000 1.450000 3.370000 ( 6.779409) 58 | get-missing: libm:bin:buffer 2.030000 1.850000 3.880000 ( 7.157176) 59 | get-missing: mclient:ascii 12.590000 4.060000 16.650000 ( 16.909974) 60 | get-missing: stash:bin 3.100000 1.380000 4.480000 ( 7.754969) 61 | 62 | set-large: cache:dalli:bin 8.780000 2.160000 10.940000 ( 13.767983) 63 | set-large: cache:libm:bin 2.750000 1.460000 4.210000 ( 8.026319) 64 | set-large: dalli:bin 8.400000 2.160000 10.560000 ( 13.420624) 65 | set-large: libm:ascii 0.930000 1.480000 2.410000 ( 6.291813) 66 | set-large: libm:ascii:pipeline 0.580000 0.450000 1.030000 ( 1.230049) 67 | set-large: libm:ascii:udp 0.740000 0.870000 1.610000 ( 4.308647) 68 | set-large: libm:bin 0.790000 1.500000 2.290000 ( 6.057129) 69 | set-large: libm:bin:buffer 0.540000 0.710000 1.250000 ( 2.423327) 70 | set-large: mclient:ascii 13.950000 4.260000 18.210000 ( 18.586740) 71 | set-large: stash:bin 6.100000 1.440000 7.540000 ( 10.858524) 72 | 73 | get-large: cache:dalli:bin 8.080000 2.500000 10.580000 ( 13.529802) 74 | get-large: cache:libm:bin 6.880000 1.510000 8.390000 ( 12.255805) 75 | get-large: dalli:bin 7.670000 2.480000 10.150000 ( 13.091946) 76 | get-large: libm:ascii 1.780000 1.520000 3.300000 ( 7.155644) 77 | get-large: libm:ascii:pipeline 1.890000 1.900000 3.790000 ( 7.469286) 78 | get-large: libm:ascii:udp 1.570000 0.970000 2.540000 ( 4.810661) 79 | get-large: libm:bin 1.610000 1.560000 3.170000 ( 6.964619) 80 | get-large: libm:bin:buffer 1.750000 1.930000 3.680000 ( 7.370880) 81 | get-large: mclient:ascii 17.490000 4.840000 22.330000 ( 22.742459) 82 | get-large: stash:bin 3.310000 1.410000 4.720000 ( 8.020154) 83 | 84 | hash:jenkins 0.590000 0.000000 0.590000 ( 0.598982) 85 | hash:default 0.540000 0.000000 0.540000 ( 0.539465) 86 | hash:crc 0.660000 0.010000 0.670000 ( 0.659791) 87 | hash:fnv1_32 0.510000 0.000000 0.510000 ( 0.514745) 88 | hash:hsieh 0.290000 0.000000 0.290000 ( 0.289317) 89 | hash:fnv1_64 1.160000 0.000000 1.160000 ( 1.157847) 90 | hash:none 0.320000 0.000000 0.320000 ( 0.324526) 91 | hash:murmur 0.470000 0.000000 0.470000 ( 0.469895) 92 | hash:md5 1.000000 0.000000 1.000000 ( 1.000877) 93 | hash:fnv1a_64 0.540000 0.010000 0.550000 ( 0.542104) 94 | hash:fnv1a_32 0.560000 0.000000 0.560000 ( 0.567425) 95 | -------------------------------------------------------------------------------- /benchmarks/v0.0.3.txt: -------------------------------------------------------------------------------- 1 | # sabshere 2/22/11 v0.0.3 2 | 3 | vidalia:~/github/cache (master) $ ruby test/profile/benchmark.rb 4 | Darwin vidalia 9.8.0 Darwin Kernel Version 9.8.0: Wed Jul 15 16:55:01 PDT 2009; root:xnu-1228.15.4~1/RELEASE_I386 i386 5 | ruby 1.8.7 (2010-05-25 patchlevel 266) [i686-darwin9.8.0] 6 | RUBY_VERSION=ruby-1.8.7-head 7 | Ruby 1.8.7p266 8 | Loaded memcached 1.0.6 9 | Loaded remix-stash 1.1.3 10 | Loaded memcache-client 1.8.5 11 | Loaded cache 0.0.3 12 | Loaded kgio 2.3.2 13 | Loaded dalli 1.0.2 14 | Loops is 20000 15 | Stack depth is 0 16 | Small value size is: 13 bytes 17 | Large value size is: 4158 bytes 18 | No matching processes belonging to you were found 19 | user system total real 20 | set: cache:dalli:bin 6.020000 1.890000 7.910000 ( 10.568499) 21 | set: cache:libm:bin 1.460000 1.280000 2.740000 ( 6.112829) 22 | set: dalli:bin 5.640000 1.870000 7.510000 ( 10.215219) 23 | set: libm:ascii 0.820000 1.320000 2.140000 ( 5.446435) 24 | set: libm:ascii:pipeline 0.360000 0.010000 0.370000 ( 0.378485) 25 | set: libm:ascii:udp 0.700000 0.720000 1.420000 ( 3.659001) 26 | set: libm:bin 0.720000 1.340000 2.060000 ( 5.364179) 27 | set: libm:bin:buffer 0.300000 0.120000 0.420000 ( 1.105467) 28 | set: mclient:ascii 10.860000 3.820000 14.680000 ( 15.175516) 29 | set: stash:bin 3.700000 1.350000 5.050000 ( 8.229477) 30 | 31 | get: cache:dalli:bin 6.130000 2.070000 8.200000 ( 10.683650) 32 | get: cache:libm:bin 1.440000 1.230000 2.670000 ( 5.908044) 33 | get: dalli:bin 5.800000 2.080000 7.880000 ( 10.324650) 34 | get: libm:ascii 0.970000 1.320000 2.290000 ( 5.582769) 35 | get: libm:ascii:pipeline 1.050000 1.580000 2.630000 ( 5.956804) 36 | get: libm:ascii:udp 0.820000 0.720000 1.540000 ( 3.605324) 37 | get: libm:bin 0.820000 1.340000 2.160000 ( 5.329828) 38 | get: libm:bin:buffer 0.940000 1.630000 2.570000 ( 5.748327) 39 | get: mclient:ascii 12.900000 3.860000 16.760000 ( 17.249089) 40 | get: stash:bin 3.360000 1.320000 4.680000 ( 7.949412) 41 | 42 | delete: cache:dalli:bin 5.610000 2.100000 7.710000 ( 10.029324) 43 | delete: cache:libm:bin 2.030000 1.370000 3.400000 ( 6.651471) 44 | delete: dalli:bin 5.230000 2.110000 7.340000 ( 9.676535) 45 | delete: libm:ascii 1.790000 1.380000 3.170000 ( 6.550488) 46 | delete: libm:ascii:pipeline 0.570000 0.650000 1.220000 ( 1.346079) 47 | delete: libm:ascii:udp 1.650000 0.870000 2.520000 ( 4.527372) 48 | delete: libm:bin 1.730000 1.390000 3.120000 ( 6.469119) 49 | delete: libm:bin:buffer 0.480000 0.550000 1.030000 ( 1.773955) 50 | delete: mclient:ascii 10.890000 3.930000 14.820000 ( 15.286474) 51 | delete:stash:bin => #> 52 | 53 | get-missing: cache:dalli:bin 5.970000 2.260000 8.230000 ( 10.874274) 54 | get-missing: cache:libm:bin 2.470000 1.580000 4.050000 ( 7.471236) 55 | get-missing: dalli:bin 5.550000 2.230000 7.780000 ( 10.420783) 56 | get-missing: libm:ascii 2.080000 1.560000 3.640000 ( 7.186221) 57 | get-missing: libm:ascii:pipeline 2.160000 1.890000 4.050000 ( 7.482394) 58 | get-missing: libm:ascii:udp 1.850000 0.930000 2.780000 ( 4.956340) 59 | get-missing: libm:bin 2.030000 1.470000 3.500000 ( 7.022853) 60 | get-missing: libm:bin:buffer 2.090000 1.890000 3.980000 ( 7.314636) 61 | get-missing: mclient:ascii 11.500000 4.010000 15.510000 ( 16.029498) 62 | get-missing: stash:bin 3.280000 1.390000 4.670000 ( 7.834190) 63 | 64 | set-large: cache:dalli:bin 8.070000 2.080000 10.150000 ( 13.126027) 65 | set-large: cache:libm:bin 2.530000 1.410000 3.940000 ( 7.636479) 66 | set-large: dalli:bin 7.650000 2.070000 9.720000 ( 12.691443) 67 | set-large: libm:ascii 0.930000 1.420000 2.350000 ( 6.066637) 68 | set-large: libm:ascii:pipeline 0.640000 0.450000 1.090000 ( 1.279625) 69 | set-large: libm:ascii:udp 0.820000 0.860000 1.680000 ( 4.318411) 70 | set-large: libm:bin 0.810000 1.450000 2.260000 ( 5.870205) 71 | set-large: libm:bin:buffer 0.590000 0.660000 1.250000 ( 2.492524) 72 | set-large: mclient:ascii 12.290000 4.120000 16.410000 ( 17.042205) 73 | set-large: stash:bin 5.810000 1.430000 7.240000 ( 10.615813) 74 | 75 | get-large: cache:dalli:bin 7.680000 2.460000 10.140000 ( 13.286558) 76 | get-large: cache:libm:bin 6.530000 1.500000 8.030000 ( 11.980532) 77 | get-large: dalli:bin 7.240000 2.460000 9.700000 ( 12.743058) 78 | get-large: libm:ascii 1.610000 1.540000 3.150000 ( 7.086940) 79 | get-large: libm:ascii:pipeline 1.720000 1.890000 3.610000 ( 7.410838) 80 | get-large: libm:ascii:udp 1.420000 0.960000 2.380000 ( 4.688834) 81 | get-large: libm:bin 1.440000 1.590000 3.030000 ( 6.935013) 82 | get-large: libm:bin:buffer 1.600000 1.920000 3.520000 ( 7.278831) 83 | get-large: mclient:ascii 15.700000 4.800000 20.500000 ( 21.276235) 84 | get-large: stash:bin 3.580000 1.420000 5.000000 ( 8.259296) 85 | 86 | hash:hsieh 0.310000 0.000000 0.310000 ( 0.314801) 87 | hash:none 0.320000 0.000000 0.320000 ( 0.317238) 88 | hash:default 0.610000 0.000000 0.610000 ( 0.627191) 89 | hash:fnv1_64 1.270000 0.010000 1.280000 ( 1.274384) 90 | hash:md5 1.070000 0.000000 1.070000 ( 1.080698) 91 | hash:murmur 0.560000 0.000000 0.560000 ( 0.570058) 92 | hash:fnv1a_64 0.690000 0.000000 0.690000 ( 0.701543) 93 | hash:fnv1a_32 0.680000 0.010000 0.690000 ( 0.684682) 94 | hash:jenkins 0.660000 0.000000 0.660000 ( 0.668542) 95 | hash:crc 0.660000 0.000000 0.660000 ( 0.665331) 96 | hash:fnv1_32 0.630000 0.000000 0.630000 ( 0.636520) 97 | -------------------------------------------------------------------------------- /test/profile/benchmark.rb: -------------------------------------------------------------------------------- 1 | # sabshere 2/22/11 thanks to memcached 2 | HERE = File.dirname(__FILE__) 3 | $LOAD_PATH << "#{HERE}/../../lib/" 4 | UNIX_SOCKET_NAME = File.join(ENV['TMPDIR']||'/tmp','memcached') 5 | 6 | require 'cache' 7 | require 'benchmark' 8 | require 'rubygems' 9 | require 'ruby-debug' if ENV['DEBUG'] 10 | begin; require 'memory'; rescue LoadError; end 11 | 12 | puts `uname -a` 13 | puts `ruby -v` 14 | puts `env | egrep '^RUBY'` 15 | puts "Ruby #{RUBY_VERSION}p#{RUBY_PATCHLEVEL}" 16 | 17 | [ ["memcached", "memcached"], 18 | # ["remix-stash", "remix/stash"], 19 | # ["astro-remcached", "remcached"], # Clobbers the "Memcached" constant 20 | ["memcache-client", "memcache"], 21 | ["cache", "cache"], 22 | ["kgio", "kgio"], 23 | ["dalli","dalli"]].each do |gem_name, requirement| 24 | require requirement 25 | gem gem_name 26 | puts "Loaded #{gem_name} #{Gem.loaded_specs[gem_name].version.to_s rescue nil}" 27 | end 28 | 29 | # class Remix::Stash 30 | # # Remix::Stash API doesn't let you set servers 31 | # @@clusters = {:default => Remix::Stash::Cluster.new(['127.0.0.1:43042', '127.0.0.1:43043'])} 32 | # end 33 | 34 | class Dalli::ClientCompat < Dalli::Client 35 | def set(*args) 36 | super(*args[0..2]) 37 | end 38 | def get(*args) 39 | super(args.first) 40 | end 41 | def get_multi(*args) 42 | super(args.first) 43 | end 44 | def append(*args) 45 | super 46 | rescue Dalli::DalliError 47 | end 48 | def prepend(*args) 49 | super 50 | rescue Dalli::DalliError 51 | end 52 | end 53 | 54 | # class Cache::Compat < Cache 55 | # def set(*args) 56 | # super(*args[0..2]) 57 | # end 58 | # def get(*args) 59 | # super(args.first) 60 | # end 61 | # end 62 | 63 | class Bench 64 | 65 | def initialize(loops = nil, stack_depth = nil) 66 | @loops = (loops || 20000).to_i 67 | @stack_depth = (stack_depth || 0).to_i 68 | 69 | puts "Loops is #{@loops}" 70 | puts "Stack depth is #{@stack_depth}" 71 | 72 | @m_value = Marshal.dump( 73 | @small_value = ["testing"]) 74 | @m_large_value = Marshal.dump( 75 | @large_value = [{"test" => "1", "test2" => "2", Object.new => "3", 4 => 4, "test5" => 2**65}] * 2048) 76 | 77 | puts "Small value size is: #{@m_value.size} bytes" 78 | puts "Large value size is: #{@m_large_value.size} bytes" 79 | 80 | @keys = [ 81 | @k1 = "Short", 82 | @k2 = "Sym1-2-3::45" * 8, 83 | @k3 = "Long" * 40, 84 | @k4 = "Medium" * 8, 85 | @k5 = "Medium2" * 8, 86 | @k6 = "Long3" * 40] 87 | 88 | reset_servers 89 | reset_clients 90 | 91 | Benchmark.bm(36) do |x| 92 | @benchmark = x 93 | end 94 | end 95 | 96 | def run(level = @stack_depth) 97 | level > 0 ? run(level - 1) : run_without_recursion 98 | end 99 | 100 | private 101 | 102 | def reset_servers 103 | # Kill memcached 104 | system("killall -9 memcached") 105 | 106 | # Start memcached 107 | verbosity = (ENV['DEBUG'] ? "-vv" : "") 108 | log = "/tmp/memcached.log" 109 | memcached = ENV['MEMCACHED_COMMAND'] || 'memcached' 110 | system ">#{log}" 111 | 112 | # TCP memcached 113 | (43042..43046).each do |port| 114 | cmd = "#{memcached} #{verbosity} -U 0 -p #{port} >> #{log} 2>&1 &" 115 | raise "'#{cmd}' failed to start" unless system(cmd) 116 | end 117 | # UDP memcached 118 | (43052..43053).each do |port| 119 | cmd = "#{memcached} #{verbosity} -U #{port} -p 0 >> #{log} 2>&1 &" 120 | raise "'#{cmd}' failed to start" unless system(cmd) 121 | end 122 | # Domain socket memcached 123 | (0..1).each do |i| 124 | cmd = "#{memcached} -M -s #{UNIX_SOCKET_NAME}#{i} #{verbosity} >> #{log} 2>&1 &" 125 | raise "'#{cmd}' failed to start" unless system(cmd) 126 | end 127 | end 128 | 129 | def reset_clients 130 | @clients = { 131 | "cache:libm:bin" => Cache.wrap(Memcached.new(['127.0.0.1:43042', '127.0.0.1:43043'], :buffer_requests => false, :no_block => false, :namespace => "namespace", :binary_protocol => true)), 132 | "cache:dalli:bin" => Cache.wrap(Dalli::Client.new(['127.0.0.1:43042', '127.0.0.1:43043'], :marshal => false, :threadsafe => false)), 133 | "libm:ascii" => Memcached::Rails.new( 134 | ['127.0.0.1:43042', '127.0.0.1:43043'], 135 | :buffer_requests => false, :no_block => false, :namespace => "namespace"), 136 | "libm:ascii:pipeline" => Memcached::Rails.new( 137 | ['127.0.0.1:43042', '127.0.0.1:43043'], 138 | :no_block => true, :buffer_requests => true, :noreply => true, :namespace => "namespace"), 139 | "libm:ascii:udp" => Memcached::Rails.new( 140 | ["#{UNIX_SOCKET_NAME}0", "#{UNIX_SOCKET_NAME}1"], 141 | :buffer_requests => false, :no_block => false, :namespace => "namespace"), 142 | "libm:bin" => Memcached::Rails.new( 143 | ['127.0.0.1:43042', '127.0.0.1:43043'], 144 | :buffer_requests => false, :no_block => false, :namespace => "namespace", :binary_protocol => true), 145 | "libm:bin:buffer" => Memcached::Rails.new( 146 | ['127.0.0.1:43042', '127.0.0.1:43043'], 147 | :no_block => true, :buffer_requests => true, :namespace => "namespace", :binary_protocol => true), 148 | "mclient:ascii" => MemCache.new(['127.0.0.1:43042', '127.0.0.1:43043']), 149 | # "stash:bin" => Remix::Stash.new(:root), 150 | "dalli:bin" => Dalli::ClientCompat.new(['127.0.0.1:43042', '127.0.0.1:43043'], :marshal => false, :threadsafe => false)} 151 | end 152 | 153 | 154 | def benchmark_clients(test_name, clients = @clients) 155 | clients.keys.sort.each do |client_name| 156 | next if client_name == "stash" and test_name == "set-large" # Don't let stash break the world 157 | client = clients[client_name] 158 | begin 159 | yield client 160 | @benchmark.report("#{test_name}: #{client_name}") { @loops.times { yield client } } 161 | rescue Exception => e 162 | puts "#{test_name}:#{client_name} => #{e.inspect}" 163 | reset_clients 164 | end 165 | end 166 | puts 167 | end 168 | 169 | def benchmark_hashes(hashes, test_name) 170 | hashes.each do |hash_name, int| 171 | @m = Memcached::Rails.new(:hash => hash_name) 172 | @benchmark.report("#{test_name}:#{hash_name}") do 173 | (@loops * 5).times { yield int } 174 | end 175 | end 176 | end 177 | 178 | def run_without_recursion 179 | benchmark_clients("set") do |c| 180 | c.set @k1, @m_value, 0, true 181 | c.set @k2, @m_value, 0, true 182 | c.set @k3, @m_value, 0, true 183 | end 184 | 185 | benchmark_clients("get") do |c| 186 | c.get @k1, true 187 | c.get @k2, true 188 | c.get @k3, true 189 | end 190 | 191 | # benchmark_clients("get-multi") do |c| 192 | # c.get_multi @keys, true 193 | # end 194 | # 195 | # benchmark_clients("append") do |c| 196 | # c.append @k1, @m_value 197 | # c.append @k2, @m_value 198 | # c.append @k3, @m_value 199 | # end 200 | # 201 | # benchmark_clients("prepend") do |c| 202 | # c.prepend @k1, @m_value 203 | # c.prepend @k2, @m_value 204 | # c.prepend @k3, @m_value 205 | # end 206 | 207 | benchmark_clients("delete") do |c| 208 | c.delete @k1 209 | c.delete @k2 210 | c.delete @k3 211 | end 212 | 213 | benchmark_clients("get-missing") do |c| 214 | c.get @k1 215 | c.get @k2 216 | c.get @k3 217 | end 218 | 219 | # benchmark_clients("append-missing") do |c| 220 | # c.append @k1, @m_value 221 | # c.append @k2, @m_value 222 | # c.append @k3, @m_value 223 | # end 224 | # 225 | # benchmark_clients("prepend-missing") do |c| 226 | # c.prepend @k1, @m_value 227 | # c.prepend @k2, @m_value 228 | # c.prepend @k3, @m_value 229 | # end 230 | 231 | benchmark_clients("set-large") do |c| 232 | c.set @k1, @m_large_value, 0, true 233 | c.set @k2, @m_large_value, 0, true 234 | c.set @k3, @m_large_value, 0, true 235 | end 236 | 237 | benchmark_clients("get-large") do |c| 238 | c.get @k1, true 239 | c.get @k2, true 240 | c.get @k3, true 241 | end 242 | 243 | benchmark_hashes(Memcached::HASH_VALUES, "hash") do |i| 244 | Rlibmemcached.memcached_generate_hash_rvalue(@k1, i) 245 | Rlibmemcached.memcached_generate_hash_rvalue(@k2, i) 246 | Rlibmemcached.memcached_generate_hash_rvalue(@k3, i) 247 | Rlibmemcached.memcached_generate_hash_rvalue(@k4, i) 248 | Rlibmemcached.memcached_generate_hash_rvalue(@k5, i) 249 | Rlibmemcached.memcached_generate_hash_rvalue(@k6, i) 250 | end 251 | end 252 | end 253 | 254 | Bench.new(ENV["LOOPS"], ENV["STACK_DEPTH"]).run 255 | 256 | Process.memory.each do |key, value| 257 | puts "#{key}: #{value/1024.0}M" 258 | end if Process.respond_to? :memory 259 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ymmv 2 | 3 | Update August 2015: If you are looking for locking and caching methods, [lock_and_cache](https://github.com/seamusabshere/lock_and_cache) is a simpler alternative that is redis-only. 4 | 5 | # cache 6 | 7 | Wraps memcached, redis, memcache-client, dalli and handles their weirdnesses, including forking. 8 | 9 | Aims to let other libraries be cache-agnostic in return for a performance hit. 10 | 11 | ## Real world usage 12 | 13 | Used by [lock_method](https://github.com/seamusabshere/lock_method) and [cache_method](https://github.com/seamusabshere/cache_method) so that you can use them with memcached, redis, etc. 14 | 15 | In production use at [carbon.brighterplanet.com](http://carbon.brighterplanet.com) and [data.brighterplanet.com](http://data.brighterplanet.com). 16 | 17 | ## Quick example 18 | 19 | require 'memcached' # a really fast memcached client gem by Evan Weaver, one of the lead engineers at Twitter 20 | require 'cache' # this gem, which wraps the client to provide a standard interface 21 | 22 | client = Memcached.new('127.0.0.1:11211', :binary_protocol => true) 23 | @cache = Cache.wrap(client) 24 | 25 | # don't worry, even though it's memcached gem, this won't raise Memcached::NotFound 26 | @cache.get('hello') 27 | 28 | # fetch is not provided by the memcached gem, the wrapper adds it 29 | @cache.fetch('hello') { 'world' } 30 | 31 | # don't worry, the wrapper will automatically clone the Memcached object after forking (or threading for that matter) 32 | Kernel.fork { @cache.get('hello') } 33 | 34 | If you can't use the memcached gem (because you're on heroku, for example) then just wrap a dalli or a redis client. You still get exactly the same interface. 35 | 36 | ## Rationale 37 | 38 | I wanted a common interface to a bunch of great Ruby cache clients so I can develop gems ([lock_method](https://github.com/seamusabshere/lock_method), [cache_method](https://github.com/seamusabshere/cache_method)) that accept any of them. 39 | 40 | * I'm tired of rescuing from Memcached::NotFound 41 | * I'm tired of forgetting whether it's :expires_in or :ttl 42 | * I don't know why we ever started using read/write instead of get/set. 43 | * I don't like how you have to manually handle after_fork for Redis, Memcached, etc. 44 | * I don't know why Memcached::Rails doesn't act like a ActiveRecord::Cache::Store 45 | * Why are you asking me about :raw or whatever? Just marshal it 46 | 47 | ## Speed 48 | 49 | It's more than 50% slower than raw [Memcached](https://github.com/evan/memcached) and about the same as raw [Dalli](https://github.com/mperham/dalli) 50 | 51 | # raw dalli versus wrapped 52 | 53 | set: cache:dalli:bin 2.150000 0.840000 2.990000 ( 3.752008) <- Cache.wrap(Dalli::Client.new) 54 | set: dalli:bin 2.120000 0.830000 2.950000 ( 3.734024) <- Dalli::Client.new 55 | 56 | get: cache:dalli:bin 2.040000 0.910000 2.950000 ( 3.646148) 57 | get: dalli:bin 2.040000 0.900000 2.940000 ( 3.632840) 58 | 59 | delete: cache:dalli:bin 1.830000 0.880000 2.710000 ( 3.381917) 60 | delete: dalli:bin 1.790000 0.880000 2.670000 ( 3.327514) 61 | 62 | get-missing: cache:dalli:bin 1.780000 0.880000 2.660000 ( 3.344041) 63 | get-missing: dalli:bin 1.760000 0.880000 2.640000 ( 3.337539) 64 | 65 | set-large: cache:dalli:bin 2.750000 0.880000 3.630000 ( 4.474265) 66 | set-large: dalli:bin 2.720000 0.870000 3.590000 ( 4.436163) 67 | 68 | get-large: cache:dalli:bin 2.420000 0.990000 3.410000 ( 4.135326) 69 | get-large: dalli:bin 2.410000 0.990000 3.400000 ( 4.119832) 70 | 71 | # raw memcached versus wrapped 72 | 73 | set: cache:libm:bin 0.860000 0.640000 1.500000 ( 3.033145) <- Cache.wrap(Memcached.new(:binary_protocol => true)) 74 | set: libm:bin 0.200000 0.480000 0.680000 ( 1.907099) <- Memcached.new(:binary_protocol => true) 75 | 76 | get: cache:libm:bin 0.800000 0.680000 1.480000 ( 2.700458) 77 | get: libm:bin 0.260000 0.660000 0.920000 ( 1.974025) 78 | 79 | delete: cache:libm:bin 1.000000 0.600000 1.600000 ( 2.968057) 80 | delete: libm:bin 0.600000 0.560000 1.160000 ( 2.375070) 81 | 82 | get-missing: cache:libm:bin 0.980000 0.800000 1.780000 ( 2.850947) 83 | get-missing: libm:bin 0.640000 0.710000 1.350000 ( 2.520733) 84 | 85 | set-large: cache:libm:bin 1.220000 0.590000 1.810000 ( 3.404739) 86 | set-large: libm:bin 0.230000 0.520000 0.750000 ( 2.111738) 87 | 88 | get-large: cache:libm:bin 3.780000 0.870000 4.650000 ( 6.073208) 89 | get-large: libm:bin 0.340000 0.830000 1.170000 ( 2.304408) 90 | 91 | Thanks to https://github.com/evan/memcached/blob/master/test/profile/benchmark.rb 92 | 93 | So: hopefully it makes it easier to get started with caching and hit the low-hanging fruit. Then you can move on to a raw client! 94 | 95 | ## Features 96 | 97 | ### Forking/threading 98 | 99 | When you use a Cache object to wrap Memcached or Redis, you don't have to worry about forking or threading. 100 | 101 | For example, you don't have to set up unicorn or PhusionPassenger's after_fork. 102 | 103 | ### TTL 104 | 105 | 0 means don't expire. 106 | 107 | The default ttl is 60 seconds. 108 | 109 | ### Marshalling 110 | 111 | Everything gets marshalled. No option to turn it into "raw" mode. If you need that kind of control, please submit a patch or just use one of the other gems directly. 112 | 113 | ### Methods 114 | 115 | It will translate these methods to whatever Redis, Memcached, etc. client you're using: 116 | 117 | @cache.get 'hello' 118 | @cache.set 'hello', 'world', 5.minutes 119 | @cache.delete 'hello' 120 | @cache.flush 121 | @cache.exist? 'hello' 122 | @cache.reset 123 | @cache.fetch 'hello' { 'world' } 124 | @cache.cas 'hello' { |current| 'world' } 125 | @cache.increment 'high-fives' 126 | @cache.decrement 'high-fives' 127 | @cache.get_multi 'hello', 'privyet', 'hallo' 128 | 129 | Also provided for Rails compatibility: 130 | 131 | @cache.write 'hello', 'world', :expires_in => 5.minutes 132 | @cache.read 'hello' 133 | @cache.clear 134 | @cache.compare_and_swap 135 | @cache.read_multi 'hello', 'privyet', 'hallo' 136 | 137 | ## Supported clients 138 | 139 | Supported memcached clients: 140 | 141 | * [memcached](https://github.com/fauna/memcached) (native C extensions, super fast!) 142 | * [dalli](https://github.com/mperham/dalli) (pure ruby, recommended if you're on heroku) 143 | * [memcache-client](https://github.com/mperham/memcache-client) (not recommended. the one that comes with Rails.) 144 | 145 | Supported Redis clients: 146 | 147 | * [redis](https://github.com/ezmobius/redis-rb) 148 | 149 | ## How you might use it 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 174 | 182 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 |
 Super-fast memcachedPure Ruby memcached (works on Heroku)Redis
Rails
config.cache_store = Cache.wrap(Memcached.new)
config.cache_store = Cache.wrap(Dalli::Client.new)
config.cache_store = Cache.wrap(Redis.new)
Your own library 167 |
168 | # Accept any client, let Cache take care of it
169 | def cache=(raw_client)
170 |   @cache = Cache.wrap(raw_client)
171 | end
172 | 
173 |
175 |
176 | # Accept any client, let Cache take care of it
177 | def cache=(raw_client)
178 |   @cache = Cache.wrap(raw_client)
179 | end
180 | 
181 |
183 |
184 | # Accept any client, let Cache take care of it
185 | def cache=(raw_client)
186 |   @cache = Cache.wrap(raw_client)
187 | end
188 | 
189 |
CacheMethod (already uses Cache internally)
CacheMethod.config.storage = Memcached.new
CacheMethod.config.storage = Dalli::Client.new
CacheMethod.config.storage = Redis.new
LockMethod (already uses Cache internally)
LockMethod.config.storage = Memcached.new
LockMethod.config.storage = Dalli::Client.new
LockMethod.config.storage = Redis.new
204 | 205 | ## Other examples 206 | 207 | It defaults to an in-process memory store: 208 | 209 | @cache = Cache.new 210 | @cache.set 'hello', 'world' 211 | @cache.get 'hello' 212 | 213 | You can specify a more useful cache client: 214 | 215 | require 'redis' # the redis key-value store 216 | require 'cache' # this gem, which provides a standard interface 217 | raw_client = Redis.new 218 | @cache = Cache.wrap(raw_client) 219 | 220 | or 221 | 222 | require 'dalli' # the dalli memcached client used by heroku 223 | require 'cache' # this gem, which provides a standard interface 224 | raw_client = Dalli::Client.new 225 | @cache = Cache.wrap(raw_client) 226 | 227 | Or you could piggyback off the default rails cache: 228 | 229 | @cache = Cache.wrap(Rails.cache) 230 | 231 | ## Copyright 232 | 233 | Copyright 2011 Seamus Abshere 234 | --------------------------------------------------------------------------------