mongocached 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Benchmark.md +54 -0
- data/Gemfile +25 -0
- data/HISTORY.md +1 -0
- data/README.md +14 -0
- data/Rakefile +17 -0
- data/lib/mongocached.rb +203 -0
- metadata +104 -0
data/Benchmark.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
### Ruby 1.9.3p194
|
2
|
+
|
3
|
+
mo = mongocached / me = memcached
|
4
|
+
|
5
|
+
#### small string * 100000
|
6
|
+
<pre>
|
7
|
+
user system total real
|
8
|
+
mo set 5.060000 0.470000 5.530000 ( 5.538689)
|
9
|
+
me set 1.400000 1.310000 2.710000 ( 4.065908)
|
10
|
+
mo get 10.130000 1.110000 11.240000 ( 12.415902)
|
11
|
+
me get 1.360000 1.340000 2.700000 ( 3.768052)
|
12
|
+
</pre>
|
13
|
+
|
14
|
+
|
15
|
+
#### large hash * 100000
|
16
|
+
<pre>
|
17
|
+
user system total real
|
18
|
+
mo set 39.010000 1.170000 40.180000 ( 40.314892)
|
19
|
+
me set 15.510000 2.340000 17.850000 ( 20.471940)
|
20
|
+
mo get 39.740000 3.350000 43.090000 ( 47.473688)
|
21
|
+
me get 14.950000 1.420000 16.370000 ( 17.187145)
|
22
|
+
</pre>
|
23
|
+
|
24
|
+
#### large hash * 100000
|
25
|
+
<pre>
|
26
|
+
user system total real
|
27
|
+
mo set 40.650000 1.180000 41.830000 ( 42.026839)
|
28
|
+
me set 16.590000 2.060000 18.650000 ( 20.909275)
|
29
|
+
mo get 49.400000 5.650000 55.050000 ( 63.141785)
|
30
|
+
me get 11.760000 1.810000 13.570000 ( 14.814693)
|
31
|
+
</pre>
|
32
|
+
|
33
|
+
### Ruby 1.9.2p318
|
34
|
+
|
35
|
+
mo = mongocached / me = memcached
|
36
|
+
|
37
|
+
#### small string * 100000
|
38
|
+
<pre>
|
39
|
+
user system total real
|
40
|
+
mo set 4.360000 0.470000 4.830000 ( 4.849431)
|
41
|
+
me set 1.950000 1.840000 3.790000 ( 5.901345)
|
42
|
+
mo get 9.580000 1.130000 10.710000 ( 12.000498)
|
43
|
+
me get 1.300000 1.390000 2.690000 ( 3.707633)
|
44
|
+
</pre>
|
45
|
+
|
46
|
+
|
47
|
+
#### large hash * 100000
|
48
|
+
<pre>
|
49
|
+
user system total real
|
50
|
+
mo set 39.770000 1.130000 40.900000 ( 41.075236)
|
51
|
+
me set 15.880000 2.440000 18.320000 ( 20.905582)
|
52
|
+
mo get 38.660000 3.590000 42.250000 ( 46.763608)
|
53
|
+
me get 11.900000 1.450000 13.350000 ( 14.215215)
|
54
|
+
</pre>
|
data/Gemfile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
source :rubygems
|
2
|
+
|
3
|
+
gem 'mongo'
|
4
|
+
gem 'bson_ext'
|
5
|
+
|
6
|
+
group :development, :test do
|
7
|
+
gem 'rake'
|
8
|
+
end
|
9
|
+
|
10
|
+
group :development do
|
11
|
+
gem 'rdoc'
|
12
|
+
end
|
13
|
+
|
14
|
+
group :benchmark do
|
15
|
+
# for benchmark only
|
16
|
+
# requires sudo apt-get install libsasl2-dev
|
17
|
+
gem 'memcached'
|
18
|
+
end
|
19
|
+
|
20
|
+
group :test do
|
21
|
+
gem 'rspec'
|
22
|
+
gem 'simplecov', :require => false
|
23
|
+
end
|
24
|
+
|
25
|
+
|
data/HISTORY.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
|
data/README.md
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# Mongocached
|
2
|
+
|
3
|
+
Cache using mongo, like memcached.
|
4
|
+
|
5
|
+
## Why?
|
6
|
+
|
7
|
+
Because disk space is cheaper.
|
8
|
+
|
9
|
+
## Links
|
10
|
+
|
11
|
+
#### [API Docs](http://rubyops.github.com/mongocached/doc/)
|
12
|
+
#### [Coverage](http://rubyops.github.com/mongocached/coverage/)
|
13
|
+
#### [Benchmarks](https://github.com/rubyops/mongocached/blob/master/Benchmark.md)
|
14
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
task :default => :spec
|
7
|
+
|
8
|
+
|
9
|
+
task :benchmark do
|
10
|
+
load './spec/benchmarks.rb'
|
11
|
+
end
|
12
|
+
|
13
|
+
task :package do
|
14
|
+
puts %x{ gem build diskcached.gemspec && (mkdir ./pkg; mv *.gem ./pkg/ ) }
|
15
|
+
end
|
16
|
+
|
17
|
+
# vim: syntax=ruby
|
data/lib/mongocached.rb
ADDED
@@ -0,0 +1,203 @@
|
|
1
|
+
require 'mongo'
|
2
|
+
# @author Joshua P. Mervine <joshua@mervine.net>
|
3
|
+
class Mongocached
|
4
|
+
# version for gem
|
5
|
+
VERSION = '1.0.0'
|
6
|
+
|
7
|
+
@cleanup_last = nil
|
8
|
+
@ensure_indexes = false
|
9
|
+
|
10
|
+
# initialize object
|
11
|
+
def initialize options={}
|
12
|
+
@options = defaults.merge! options
|
13
|
+
|
14
|
+
unless @options[:lifetime].nil?
|
15
|
+
@cleanup_last = Time.now
|
16
|
+
@options[:cleanup_life] = ( @options[:lifetime] < 1800 ? @options[:lifetime] : 1800 )
|
17
|
+
else
|
18
|
+
@options[:cleanup_auto] = false
|
19
|
+
end
|
20
|
+
|
21
|
+
@last = nil
|
22
|
+
|
23
|
+
flush_expired!
|
24
|
+
end
|
25
|
+
|
26
|
+
def defaults
|
27
|
+
{
|
28
|
+
#cache_id_prefix: nil,
|
29
|
+
lifetime: 3600,
|
30
|
+
automatic_serialization: true,
|
31
|
+
host: 'localhost',
|
32
|
+
port: '27017',
|
33
|
+
dbname: 'mongocached',
|
34
|
+
collection: 'cache',
|
35
|
+
cleanup_auto: true,
|
36
|
+
cleanup_life: 1800,
|
37
|
+
config: {}
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
# expire cache
|
42
|
+
def delete id
|
43
|
+
@last = nil
|
44
|
+
collection.remove(_id: id)
|
45
|
+
end
|
46
|
+
alias :remove :delete
|
47
|
+
|
48
|
+
# delete all caches
|
49
|
+
def flush
|
50
|
+
collection.drop
|
51
|
+
end
|
52
|
+
alias :clean :flush
|
53
|
+
|
54
|
+
# flush expired caches if cleanup hasn't been run recently
|
55
|
+
def flush_expired
|
56
|
+
if !@cleanup_last.nil? && !@options[:cleanup_life].nil? && (@cleanup_last+@options[:cleanup_life]) < Time.now
|
57
|
+
flush_expired!
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# flush expired caches, ingoring when garbage collection was last run
|
62
|
+
# update indexes
|
63
|
+
def flush_expired!
|
64
|
+
# commenting out forking until I'm sure it's necessary
|
65
|
+
# and won't cause zombie processes
|
66
|
+
#gcpid = Process.fork do
|
67
|
+
collection.remove(expires: {'$lt' => Time.now})
|
68
|
+
unless @ensure_indexes
|
69
|
+
collection.ensure_index([[:tags, 1]])
|
70
|
+
collection.ensure_index([[:expires, -1]])
|
71
|
+
end
|
72
|
+
@cleanup_last = Time.now
|
73
|
+
#end
|
74
|
+
#Process.detach(gcpid)
|
75
|
+
end
|
76
|
+
alias :clean_expired :flush_expired!
|
77
|
+
|
78
|
+
# create or read cache
|
79
|
+
# - creates cache if it doesn't exist
|
80
|
+
# - reads cache if it exists
|
81
|
+
def save id, tags = [], ttl = @options[:lifetime]
|
82
|
+
begin
|
83
|
+
doc = collection.find_one(_id: id, expires: { '$gt' => Time.now })
|
84
|
+
return deserialize(doc['data']) unless doc.nil?
|
85
|
+
|
86
|
+
data = Proc.new { yield }.call
|
87
|
+
collection.save({
|
88
|
+
_id: id,
|
89
|
+
data: serialize(data),
|
90
|
+
tags: tags,
|
91
|
+
expires: calc_expires(ttl)
|
92
|
+
})
|
93
|
+
return data
|
94
|
+
|
95
|
+
rescue LocalJumpError
|
96
|
+
# when nothing is passed to yield and it's called
|
97
|
+
return nil
|
98
|
+
end
|
99
|
+
end
|
100
|
+
alias :cache :save
|
101
|
+
|
102
|
+
# set cache
|
103
|
+
# - creates cache if it doesn't exist
|
104
|
+
# - updates cache if it does exist
|
105
|
+
def set id, data, tags = [], ttl = @options[:lifetime]
|
106
|
+
collection.save({
|
107
|
+
_id: id,
|
108
|
+
data: serialize(data),
|
109
|
+
tags: tags,
|
110
|
+
expires: calc_expires(ttl)
|
111
|
+
})
|
112
|
+
flush_expired if @options[:cleanup_auto]
|
113
|
+
true
|
114
|
+
end
|
115
|
+
alias :replace :set # for memcached compatability
|
116
|
+
|
117
|
+
# add cache
|
118
|
+
# - creates cache if it doesn't exist
|
119
|
+
# - return false if it does
|
120
|
+
def add id, data, tags = [], ttl = @options[:lifetime]
|
121
|
+
begin
|
122
|
+
collection.insert({
|
123
|
+
_id: id,
|
124
|
+
data: serialize(data),
|
125
|
+
tags: tags,
|
126
|
+
expires: calc_expires(ttl)
|
127
|
+
}, safe: true)
|
128
|
+
rescue Mongo::OperationFailure
|
129
|
+
flush_expired if @options[:cleanup_auto]
|
130
|
+
return false
|
131
|
+
end
|
132
|
+
flush_expired if @options[:cleanup_auto]
|
133
|
+
return true
|
134
|
+
end
|
135
|
+
|
136
|
+
# get cache
|
137
|
+
# - reads cache if it exists and isn't expired or raises Diskcache::NotFound
|
138
|
+
# - if passed an Array returns only items which exist and aren't expired, it raises Diskcache::NotFound if none are available
|
139
|
+
def get id
|
140
|
+
flush_expired if @options[:cleanup_auto]
|
141
|
+
if id.is_a? Array
|
142
|
+
# TODO: more mongo'y way to do this, perhaps a map/reduce?
|
143
|
+
# STORE.find({ _id: {'$in' => ['no1', 'no2', 'no3']}})
|
144
|
+
hash = {}
|
145
|
+
id.each do |i|
|
146
|
+
doc = collection.find_one(_id: i)
|
147
|
+
if !doc.nil? && doc['expires'] > Time.now
|
148
|
+
hash[i] = deserialize(doc['data'])
|
149
|
+
end
|
150
|
+
end
|
151
|
+
return hash unless hash.empty?
|
152
|
+
else
|
153
|
+
doc = collection.find_one(_id: id)
|
154
|
+
if !doc.nil? && doc['expires'] > Time.now
|
155
|
+
return deserialize(doc['data'])
|
156
|
+
end
|
157
|
+
end
|
158
|
+
raise Mongocached::NotFound
|
159
|
+
end
|
160
|
+
alias :load :get
|
161
|
+
|
162
|
+
private
|
163
|
+
def serialize data
|
164
|
+
if @options[:automatic_serialization]
|
165
|
+
Marshal::dump(data)
|
166
|
+
else
|
167
|
+
data
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def deserialize data
|
172
|
+
if @options[:automatic_serialization]
|
173
|
+
Marshal::load(data)
|
174
|
+
else
|
175
|
+
data
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def calc_expires ttl = @options[:lifetime]
|
180
|
+
return nil if ttl.nil?
|
181
|
+
Time.now+ttl
|
182
|
+
end
|
183
|
+
|
184
|
+
def collection
|
185
|
+
@collection ||= db[@options[:collection]]
|
186
|
+
end
|
187
|
+
|
188
|
+
def db
|
189
|
+
@db ||= connection[@options[:dbname]]
|
190
|
+
end
|
191
|
+
|
192
|
+
def connection
|
193
|
+
@connection ||= connect
|
194
|
+
end
|
195
|
+
|
196
|
+
def connect
|
197
|
+
::Mongo::Connection.new(@options[:host], @options[:port], @options[:config])
|
198
|
+
end
|
199
|
+
|
200
|
+
class NotFound < Exception
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mongocached
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Joshua Mervine
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-07-13 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: simplecov
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rdoc
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: Simple mongodb based cache for things like Sinatra and Rails which is
|
63
|
+
implemented much like Memcached in hopes that in some cases they're interchangeable.
|
64
|
+
It's based off 'diskcached'. This gem should be considered 'alpha'!
|
65
|
+
email:
|
66
|
+
- joshua@mervine.net
|
67
|
+
executables: []
|
68
|
+
extensions: []
|
69
|
+
extra_rdoc_files: []
|
70
|
+
files:
|
71
|
+
- lib/mongocached.rb
|
72
|
+
- README.md
|
73
|
+
- HISTORY.md
|
74
|
+
- Benchmark.md
|
75
|
+
- Gemfile
|
76
|
+
- Rakefile
|
77
|
+
homepage: http://mongocached.rubyops.net/
|
78
|
+
licenses: []
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options: []
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ! '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
segments:
|
90
|
+
- 0
|
91
|
+
hash: -4487074619354158587
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
none: false
|
94
|
+
requirements:
|
95
|
+
- - ! '>='
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: 1.3.6
|
98
|
+
requirements: []
|
99
|
+
rubyforge_project:
|
100
|
+
rubygems_version: 1.8.24
|
101
|
+
signing_key:
|
102
|
+
specification_version: 3
|
103
|
+
summary: Simple disk cache
|
104
|
+
test_files: []
|