diskcached 1.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +3 -4
  3. data/README.md +135 -1
  4. data/Rakefile +18 -0
  5. data/lib/diskcached.rb +42 -33
  6. metadata +13 -21
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2d3ce84b679e0539fce33f13ae46242030555375
4
+ data.tar.gz: 8d122fd674accaae92f1cbb3fc5ed9d9f024a34a
5
+ SHA512:
6
+ metadata.gz: 3e4f49916e1134d3187f21434663ac638da6ed24bca20764bd0d553eee0af2eed666b42e84fb21be17393d9ef2d10a6093d7f81b0c422082e694c4277f6b37ab
7
+ data.tar.gz: 377874d6837768046cc7a2909a73659c2dc47d9211e6de678c6345db3210d844ff6742b450b164a1cf8633b1c8a96fdf1b08b25539e92f8b084b32dffdd4cacd
data/Gemfile CHANGED
@@ -1,11 +1,12 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
 
3
3
  group :development, :test do
4
4
  gem 'rake'
5
5
  end
6
6
 
7
7
  group :development do
8
- gem 'rdoc'
8
+ gem 'yard'
9
+ gem 'redcarpet'
9
10
  end
10
11
 
11
12
  group :benchmark do
@@ -18,5 +19,3 @@ group :test do
18
19
  gem 'rspec'
19
20
  gem 'simplecov', :require => false
20
21
  end
21
-
22
-
data/README.md CHANGED
@@ -1,3 +1,137 @@
1
1
  # Diskcached
2
2
 
3
- See: http://rubyops.net/2012/07/06/diskcached_simple_disk_cacheing_for_ruby
3
+ > Simple disk cache for things like Sinatra which is implemented much like Memcached in hopes that in some cases they're interchangeable.
4
+
5
+ ### [Documentation](http://rubyops.github.com/diskcached/doc/Diskcached.html) | [Coverage](http://rubyops.github.com/diskcached/coverage/index.html#_AllFiles) | [Benchmarks](https://github.com/rubyops/diskcached/wiki/Benchmark-Output)
6
+
7
+ ## Introduction
8
+
9
+ I created Diskcached as a simple cacheing layer for things like html fragments and database calls. I thought about using [memcached](http://memcached.org/), but as the app I was working on was running on a single server, it seemed overkill. Additionally, I looked at using [rack-cache](http://rtomayko.github.com/rack-cache/), but I felt it was a bit more complex then I was looking for. So Diskcached was born (although it was origiionally released as "simple\_disk\_cache" -- for about 12 hours).
10
+
11
+ * To the comment: "I'm not clear how memcached on a single server is overkill."
12
+ > 1. In some cases -- e.g. Dreamhost shared hosting and Heroku (I believe) -- it is difficult, if not impossible to install memcached. This is for those situations.
13
+ > 1. In all cases, disk space is cheaper than memory. For example, when I used [myhosting.com](http://myhosting.com), which charges $1 per 20G of disk storage and $1 per 512MB of memory. So in my case, I use Diskcached instead of memcached and my memory foot print is ~300MB. While at the moment, I could very easily handle running memcached without running out of memory, using disk based cacheing allows me to scale much further before having to upgrade my hosting package. Additionally, if you [check out my blogs performance metrics](https://github.com/jmervine/ditty/wiki/Performance), you'll see that Diskcached brought me from ~140ms render times, to ~1ms render times, allowing me to scale even further.
14
+
15
+ * To the comment: "If you need memcache...then use it."
16
+ > * I totally agree!
17
+
18
+ ## Installation
19
+
20
+ :::shell
21
+ gem install diskcached
22
+
23
+ Or with [Bundler](http://mervine.net/tag/bundler):
24
+
25
+ :::ruby
26
+ source :rubygems
27
+ gem 'diskcached'
28
+
29
+ ## Basic Usage
30
+
31
+ ### Block Style
32
+
33
+ :::ruby
34
+ require 'diskcached'
35
+ @diskcache = Diskcached.new
36
+
37
+ result = @diskcache.cache('expensive_code') do
38
+ # some expensive code
39
+ end
40
+
41
+ puts result
42
+
43
+ The above will create the cache if it doesn't exist and cache the result of block and return it. If the cache exists and isn't expired, it will read from the cache and returnwhat's stored. This allows you to passively wrap code in a cache block and not worry about checking to see if it's valid or expired.
44
+
45
+ Also worth noting, it will return `nil` if something goes wrong.
46
+
47
+ ### Memcached Style
48
+
49
+ Using Diskcached like this should allow for a "drag and drop" replacement of Memcached, should you so decide.
50
+
51
+ :::ruby
52
+ require 'diskcached'
53
+ @diskcache = Diskcached.new
54
+
55
+ begin
56
+ result = @diskcache.get('expensive_code')
57
+ rescue # Diskcached::NotFound # prevents easy replacement, but is safer.
58
+ result = run_expensive_code
59
+ @diskcache.set('expensive_code', result)
60
+ end
61
+
62
+ puts result
63
+
64
+ It's important to note that Diskcached is quite a bit simpler then Memcached and in some ways more forgiving. If Memcached compatability is really important, refer to Memcached docs as well as Diskcached docs when implementing your code.
65
+
66
+ ## Benchmarks
67
+
68
+ ### Comments
69
+
70
+ Diskcached wasn't designed to be a faster solution, just a simpler
71
+ one when compaired to Memcached. However, from these benchmarks,
72
+ it holds up will and even should provide slightly faster reads.
73
+
74
+ ##### [Moved to 'Benchmark Output'](https://github.com/rubyops/diskcached/wiki/Benchmark-Output)
75
+
76
+ ## Sinatra Application 'httperf' results.
77
+
78
+ On a development machine (Unicorn w/ 1 worker) I ran a series of [httperf](http://www.hpl.hp.com/research/linux/httperf/) tests to see how Diskcached ran in real world situations. You can [checkout the full output from multiple examples here](https://gist.github.com/3062334), but there's a taste...
79
+
80
+ Using the endpoint [http://mervine.net/](http://mervine.net/) on my dev server and hitting it 100,000 times --
81
+
82
+ ### Code Example from Test
83
+
84
+ :::ruby
85
+ 15 configure do
86
+ ...
87
+ 44 $diskcache = Diskcached.new(File.join(settings.root, 'cache'))
88
+ 45 $diskcache.flush # ensure caches are empty on startup
89
+ 46 end
90
+ ...
91
+ 58 before do
92
+ ...
93
+ 61 @cache_key = cache_sha(request.path_info)
94
+ 62 end
95
+ ...
96
+ 231 get "/" do
97
+ 232 begin
98
+ 233 raise Diskcached::NotFound if authorized?
99
+ 234 content = $diskcache.get(@cache_key)
100
+ 235 logger.debug("reading index from cache") unless authorized?
101
+ 236 rescue Diskcached::NotFound
102
+ 237 logger.debug("storing index to cache") unless authorized?
103
+ 238 content = haml(:index, :layout => choose_layout)
104
+ 239 $diskcache.set(@cache_key, content) unless authorized?
105
+ 240 end
106
+ 241 content
107
+ 242 end
108
+
109
+ ### Test Results
110
+
111
+ :::shell
112
+ httperf --client=0/1 --server=localhost --port=9001 --uri=/ --send-buffer=4096 --recv-buffer=16384 --num-conns=100000 --num-calls=1
113
+ httperf: warning: open file limit > FD_SETSIZE; limiting max. # of open files to FD_SETS
114
+
115
+ Maximum connect burst length: 1
116
+
117
+ Total: connections 100000 requests 100000 replies 100000 test-duration 744.646 s
118
+
119
+ Connection rate: 134.3 conn/s (7.4 ms/conn, <=1 concurrent connections)
120
+ Connection time [ms]: min 1.9 avg 7.4 max 398.8 median 4.5 stddev 10.5
121
+ Connection time [ms]: connect 0.1
122
+ Connection length [replies/conn]: 1.000
123
+
124
+ Request rate: 134.3 req/s (7.4 ms/req)
125
+ Request size [B]: 62.0
126
+
127
+ Reply rate [replies/s]: min 116.6 avg 134.3 max 147.2 stddev 6.1 (148 samples)
128
+ Reply time [ms]: response 6.9 transfer 0.5
129
+ Reply size [B]: header 216.0 content 105088.0 footer 0.0 (total 105304.0)
130
+ Reply status: 1xx=0 2xx=100000 3xx=0 4xx=0 5xx=0
131
+
132
+ CPU time [s]: user 287.88 system 115.60 (user 38.7% system 15.5% total 54.2%)
133
+ Net I/O: 13818.2 KB/s (113.2*10^6 bps)
134
+
135
+ Errors: total 0 client-timo 0 socket-timo 0 connrefused 0 connreset 0
136
+ Errors: fd-unavail 0 addrunavail 0 ftab-full 0 other 0
137
+
data/Rakefile CHANGED
@@ -2,9 +2,27 @@
2
2
 
3
3
  require 'rubygems'
4
4
  require 'rspec/core/rake_task'
5
+ require './lib/diskcached'
5
6
  RSpec::Core::RakeTask.new(:spec)
6
7
  task :default => :spec
7
8
 
9
+ desc "generate and update gh-pages"
10
+ task :pages do
11
+ system(" set -x; bundle exec rspec ") or abort
12
+ system(" set -x; bundle exec yardoc --protected ./lib/**/*.rb ") or abort
13
+ system(" set -x; rm -rf /tmp/doc /tmp/coverage ") or abort
14
+ system(" set -x; mv -v ./doc /tmp ") or abort
15
+ system(" set -x; mv -v ./coverage /tmp ") or abort
16
+ system(" set -x; git checkout gh-pages ") or abort
17
+ system(" set -x; rm -rf ./doc ./coverage ") or abort
18
+ system(" set -x; mv -v /tmp/doc . ") or abort
19
+ system(" set -x; mv -v /tmp/coverage . ") or abort
20
+ system(" set -x; git add . ") or abort
21
+ system(" set -x; git commit --all -m 'updating doc and coverage' ") or abort
22
+ system(" set -x; git checkout master ") or abort
23
+ puts "don't forget to run: git push origin gh-pages"
24
+ end
25
+
8
26
 
9
27
  task :benchmark do
10
28
  load './spec/benchmarks.rb'
@@ -1,14 +1,14 @@
1
1
  # @author Joshua P. Mervine <joshua@mervine.net>
2
2
  class Diskcached
3
3
  # version for gem
4
- VERSION = '1.1.0'
4
+ VERSION = '1.1.1'
5
5
 
6
6
  # disk location for cache store
7
7
  attr_reader :store
8
-
8
+
9
9
  # cache timeout
10
10
  attr_reader :timeout
11
-
11
+
12
12
  # time of last #garbage_collect
13
13
  attr_reader :gc_last
14
14
 
@@ -26,17 +26,25 @@ class Diskcached
26
26
  def initialize store="/tmp/cache", timeout=600, autogc=true
27
27
  @store = store
28
28
  @timeout = timeout
29
- if timeout.nil?
30
- @gc_last, @gc_time = nil
31
- @gc_auto = false
32
- else
33
- @gc_last = Time.now
34
- @gc_auto = autogc
35
- @gc_time = (timeout < 600 ? timeout : 600)
29
+
30
+ @gc_last, @gc_time = nil
31
+
32
+ # true or false, this will be ignored if @gc_last and @gc_time
33
+ # are nil
34
+ @gc_auto = autogc
35
+
36
+ unless timeout.nil?
37
+ send(:timeout=, timeout)
36
38
  end
39
+
37
40
  ensure_store_directory
38
41
  end
39
42
 
43
+ def timeout=(t)
44
+ @gc_last = Time.now
45
+ @gc_time = t
46
+ end
47
+
40
48
  # return true if cache with 'key' is expired
41
49
  def expired? key
42
50
  return false if timeout.nil?
@@ -52,14 +60,14 @@ class Diskcached
52
60
  # expire (delete) all caches in #store directory
53
61
  def flush
54
62
  Dir[ File.join( store, '*.cache' ) ].each do |file|
55
- File.delete(file)
63
+ File.delete(file)
56
64
  end
57
65
  end
58
66
 
59
67
  # flush expired caches if garbage collection
60
68
  # hasn't been run recently
61
- def flush_expired
62
- if gc_last && gc_time && gc_last+gc_time <= Time.now
69
+ def flush_expired
70
+ if gc_last && gc_time && gc_last+gc_time <= Time.now
63
71
  flush_expired!
64
72
  end
65
73
  end
@@ -67,9 +75,9 @@ class Diskcached
67
75
  # flash expired caches, ingoring when garbage
68
76
  # collection was last run
69
77
  def flush_expired!
70
- Dir[ File.join( store, "*.cache" ) ].each do |f|
78
+ Dir[ File.join( store, "*.cache" ) ].each do |f|
71
79
  if (File.mtime(f)+timeout) <= Time.now
72
- File.delete(f)
80
+ File.delete(f)
73
81
  end
74
82
  end
75
83
  @gc_last = Time.now
@@ -108,28 +116,29 @@ class Diskcached
108
116
  alias :replace :set # for memcached compatability
109
117
 
110
118
  # get cache with 'key'
111
- # - reads cache if it exists and isn't expired
119
+ # - reads cache if it exists and isn't expired
112
120
  # or raises Diskcache::NotFound
113
- # - if 'key' is an Array returns only keys
121
+ # - if 'key' is an Array returns only keys
114
122
  # which exist and aren't expired, it raises
115
123
  # Diskcache::NotFound if none are available
116
124
  def get key
117
- begin
118
- if key.is_a? Array
119
- hash = {}
120
- key.each do |k|
121
- hash[k] = Marshal::load(read_cache_file(k)) unless expired?(k)
122
- end
123
- flush_expired if gc_auto
124
- return hash unless hash.empty?
125
- else
126
- flush_expired if gc_auto
127
- return Marshal::load(read_cache_file(key)) unless expired?(key)
125
+ if key.is_a? Array
126
+ ret = {}
127
+ key.each do |k|
128
+ ret[k] = Marshal::load(read_cache_file(k)) unless expired?(k)
128
129
  end
129
- raise Diskcached::NotFound
130
- rescue
131
- raise Diskcached::NotFound
130
+
131
+ raise if ret.empty?
132
+ return ret
132
133
  end
134
+
135
+ raise if expired?(key)
136
+
137
+ return Marshal::load(read_cache_file(key))
138
+ rescue
139
+ raise Diskcached::NotFound
140
+ ensure
141
+ flush_expired if gc_auto
133
142
  end
134
143
 
135
144
  # returns path to cache file with 'key'
@@ -151,12 +160,12 @@ class Diskcached
151
160
  def read_cache_file key
152
161
  f = File.open( cache_file(key), "r" )
153
162
  f.flock(File::LOCK_SH)
154
- out = f.read
163
+ out = f.read
155
164
  f.close
156
165
  return out
157
166
  end
158
167
 
159
- # returns mtime of cache file or nil if
168
+ # returns mtime of cache file or nil if
160
169
  # file doesn't exist
161
170
  def read_cache_mtime key
162
171
  return nil unless File.exists?(cache_file(key))
metadata CHANGED
@@ -1,62 +1,55 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: diskcached
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
5
- prerelease:
4
+ version: 1.1.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Joshua Mervine
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-07-10 00:00:00.000000000 Z
11
+ date: 2015-04-16 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: rspec
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: simplecov
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: '0'
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - '>='
44
39
  - !ruby/object:Gem::Version
45
40
  version: '0'
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: rdoc
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - '>='
52
46
  - !ruby/object:Gem::Version
53
47
  version: '0'
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - '>='
60
53
  - !ruby/object:Gem::Version
61
54
  version: '0'
62
55
  description: Simple disk cache for things like Sinatra which is implemented much like
@@ -75,26 +68,25 @@ files:
75
68
  - Rakefile
76
69
  homepage: http://diskcached.rubyops.net/
77
70
  licenses: []
71
+ metadata: {}
78
72
  post_install_message:
79
73
  rdoc_options: []
80
74
  require_paths:
81
75
  - lib
82
76
  required_ruby_version: !ruby/object:Gem::Requirement
83
- none: false
84
77
  requirements:
85
- - - ! '>='
78
+ - - '>='
86
79
  - !ruby/object:Gem::Version
87
80
  version: '0'
88
81
  required_rubygems_version: !ruby/object:Gem::Requirement
89
- none: false
90
82
  requirements:
91
- - - ! '>='
83
+ - - '>='
92
84
  - !ruby/object:Gem::Version
93
85
  version: 1.3.6
94
86
  requirements: []
95
87
  rubyforge_project:
96
- rubygems_version: 1.8.24
88
+ rubygems_version: 2.0.14
97
89
  signing_key:
98
- specification_version: 3
90
+ specification_version: 4
99
91
  summary: Simple disk cache
100
92
  test_files: []