tokyo_store 0.3.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/.document +5 -0
- data/.gitignore +6 -0
- data/LICENSE +20 -0
- data/README.rdoc +51 -0
- data/Rakefile +44 -0
- data/VERSION +1 -0
- data/benchmark/cache.rb +134 -0
- data/benchmark/session.rb +75 -0
- data/lib/cache/tokyo_store.rb +139 -0
- data/lib/rack/cache/tokyo_entitystore.rb +0 -0
- data/lib/rack/cache/tokyo_metastore.rb +0 -0
- data/lib/rack/session/cabinet.rb +70 -0
- data/lib/rack/session/rufus_tyrant.rb +81 -0
- data/lib/rack/session/tyrant.rb +70 -0
- data/lib/tokyo_store.rb +16 -0
- data/spec/cache/tokyo_store_spec.rb +256 -0
- data/spec/rack/cache/tokyo_spec.rb +0 -0
- data/spec/rack/session/cabinet_spec.rb +242 -0
- data/spec/rack/session/rufus_tyrant_spec.rb +246 -0
- data/spec/rack/session/tyrant_spec.rb +249 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/tokyo_store_spec.rb +43 -0
- data/tokyo_store.gemspec +65 -0
- metadata +85 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Marcos Piccinini
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
= TokyoStore
|
2
|
+
|
3
|
+
Rack based Tokyo stored (FAST!) Rails session store.
|
4
|
+
|
5
|
+
Code:: http://github.com/nofxx/tokyo_store
|
6
|
+
Demo:: http://github.com/nofxx/tokyo_webapps under rails/tokyo_store
|
7
|
+
|
8
|
+
|
9
|
+
== Require
|
10
|
+
|
11
|
+
Tokyo Cabinet and/or Tyrant:: http://tokyocabinet.sourceforge.net
|
12
|
+
|
13
|
+
Choose an adapter:
|
14
|
+
|
15
|
+
Tyrant - Pure Ruby:: http://tokyocabinet.sourceforge.net/tyrantrubydoc
|
16
|
+
Cabinet - C Bindings:: http://tokyocabinet.sourceforge.net/rubydoc
|
17
|
+
RufusTyrant - Rufus FFI:: http://github.com/jmettraux/rufus-tokyo
|
18
|
+
|
19
|
+
|
20
|
+
== Install
|
21
|
+
|
22
|
+
gem install nofxx-tokyo_store
|
23
|
+
|
24
|
+
Rails (enviroment.rb)
|
25
|
+
|
26
|
+
config.gem 'nofxx-tokyo_store', :lib => 'tokyo_store'
|
27
|
+
ActionController::Base.session_store = Rack::Session::<ADAPTER (Tyrant, Cabinet or RufusTyrant)>
|
28
|
+
|
29
|
+
|
30
|
+
== Tyrant
|
31
|
+
|
32
|
+
Start the server if you are using a Tyrant based adapter:
|
33
|
+
|
34
|
+
ttserver data.tch
|
35
|
+
|
36
|
+
Some rake tasks: http://gist.github.com/147413
|
37
|
+
|
38
|
+
|
39
|
+
Have fun!
|
40
|
+
|
41
|
+
|
42
|
+
== Thanks
|
43
|
+
|
44
|
+
Mikio Hirabayashi:: tokyo products
|
45
|
+
John Mettraux:: rufus-tokyo
|
46
|
+
Luca Guidi:: redis-store
|
47
|
+
|
48
|
+
|
49
|
+
== Copyright
|
50
|
+
|
51
|
+
Copyright (c) 2009 Marcos Piccinini. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "tokyo_store"
|
8
|
+
gem.summary = "Tokyo Tyrant rails session store"
|
9
|
+
gem.email = "x@nofxx.com"
|
10
|
+
gem.homepage = "http://github.com/nofxx/tokyo_store"
|
11
|
+
gem.authors = ["Marcos Piccinini"]
|
12
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
13
|
+
end
|
14
|
+
|
15
|
+
rescue LoadError
|
16
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'spec/rake/spectask'
|
20
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
21
|
+
spec.libs << 'lib' << 'spec'
|
22
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
23
|
+
end
|
24
|
+
|
25
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
26
|
+
spec.libs << 'lib' << 'spec'
|
27
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
28
|
+
spec.rcov = true
|
29
|
+
end
|
30
|
+
task :default => :spec
|
31
|
+
|
32
|
+
require 'rake/rdoctask'
|
33
|
+
Rake::RDocTask.new do |rdoc|
|
34
|
+
if File.exist?('VERSION')
|
35
|
+
version = File.read('VERSION').chomp
|
36
|
+
else
|
37
|
+
version = ""
|
38
|
+
end
|
39
|
+
|
40
|
+
rdoc.rdoc_dir = 'rdoc'
|
41
|
+
rdoc.title = "foo #{version}"
|
42
|
+
rdoc.rdoc_files.include('README*')
|
43
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
44
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.3.0
|
data/benchmark/cache.rb
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
#
|
2
|
+
# Tokyo Store
|
3
|
+
#
|
4
|
+
# Benchmark vs MemCacheStore on memcached and tyrant
|
5
|
+
#
|
6
|
+
$: << File.join(File.dirname(__FILE__), "/../lib")
|
7
|
+
require 'rubygems'
|
8
|
+
require 'active_support'
|
9
|
+
require 'cache/tokyo_store'
|
10
|
+
require 'redis-store'
|
11
|
+
require 'cache/rails/redis_store'
|
12
|
+
|
13
|
+
P = "x" * 100
|
14
|
+
M = P * 10
|
15
|
+
G = M * 10
|
16
|
+
A = [P, M, G]
|
17
|
+
X = { :small => P, :medium => M, :big => G }
|
18
|
+
class User; attr_accessor :name, :info; end
|
19
|
+
u = User.new; u.name = P; u.info = G
|
20
|
+
O = u
|
21
|
+
T = ARGV[0].to_i || 10000
|
22
|
+
|
23
|
+
#TODO: Cabinet & memcached C bindings
|
24
|
+
TEST = {
|
25
|
+
"TokyoStore" => ActiveSupport::Cache.lookup_store(:tokyo_store, "localhost:1978"),
|
26
|
+
"MemTokyo" => ActiveSupport::Cache.lookup_store(:mem_cache_store, "localhost:1978"),
|
27
|
+
"RedisStore" => ActiveSupport::Cache.lookup_store(:redis_store, "localhost:6379"),
|
28
|
+
"MemCache" => ActiveSupport::Cache.lookup_store(:mem_cache_store, "localhost:11211"),
|
29
|
+
}
|
30
|
+
|
31
|
+
puts " Write"
|
32
|
+
puts "----------"
|
33
|
+
Benchmark.bmbm do |b|
|
34
|
+
TEST.each_pair do |k,s|
|
35
|
+
b.report("#{k} P") { T.times { |i| s.write i.to_s, P }}
|
36
|
+
b.report("#{k} M") { T.times { |i| s.write i.to_s, M }}
|
37
|
+
b.report("#{k} G") { T.times { |i| s.write i.to_s, G }}
|
38
|
+
b.report("#{k} Obj") { T.times { |i| s.write i.to_s, O }}
|
39
|
+
b.report("#{k} Hash") { T.times { |i| s.write i.to_s, X }}
|
40
|
+
b.report("#{k} Array") { T.times { |i| s.write i.to_s, A }}
|
41
|
+
b.report("#{k} Delete") { T.times { |i| s.delete i.to_s }}
|
42
|
+
b.report("#{k} +") { T.times { |i| s.increment i.to_s }}
|
43
|
+
b.report("#{k} -") { T.times { |i| s.decrement i.to_s }}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
puts " Read"
|
48
|
+
puts "----------"
|
49
|
+
TEST.each { |p| 10_000.times { |i| p[1].write i.to_s, G }}
|
50
|
+
#TODO: implement read with diff data.
|
51
|
+
Benchmark.bmbm do |b|
|
52
|
+
TEST.each_pair do |k,s|
|
53
|
+
k = s.class.to_s.split("::")[-1]
|
54
|
+
b.report("#{k} Seq") { T.times { |i| s.read rand(i).to_s }}
|
55
|
+
b.report("#{k} Rand") { T.times { |i| s.read rand(i).to_s }}
|
56
|
+
b.report("#{k} Exist") { T.times { |i| s.exist? i.to_s }}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
puts
|
61
|
+
thr = []
|
62
|
+
Benchmark.bmbm do |b|
|
63
|
+
TEST.each_pair do |k,s|
|
64
|
+
b.report("#{k} TW") { (T/2).times { |j| thr << Thread.new { (T/2).times { |i| s.write "#{j}-#{i}", X }}}; thr.each { |t| t.join }; thr = [] }
|
65
|
+
b.report("#{k} TR") { (T/2).times { |j| thr << Thread.new { (T/2).times { |i| s.read "#{j}-#{i}" }}}; thr.each { |t| t.join }; thr = [] }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
__END__
|
70
|
+
|
71
|
+
*NOTE: Redis and Memcache support native expiration, Tokyo doesn't.
|
72
|
+
Wondering the impact of this feature written in ruby...
|
73
|
+
|
74
|
+
Core 2 Duo 8500 - 3.16Ghz
|
75
|
+
------------------------------------------------------------
|
76
|
+
Write
|
77
|
+
-------------
|
78
|
+
TokyoStore# P 0.140000 0.090000 0.230000 ( 0.367587)
|
79
|
+
RedisStore# P 0.340000 0.090000 0.430000 ( 0.542441)
|
80
|
+
MemCacheD # P 0.570000 0.160000 0.730000 ( 0.829167)
|
81
|
+
MemCacheT # P 0.560000 0.170000 0.730000 ( 0.897084)
|
82
|
+
|
83
|
+
TokyoStore# M 0.170000 0.100000 0.270000 ( 0.448071)
|
84
|
+
RedisStore# M 0.360000 0.120000 0.480000 ( 0.641083)
|
85
|
+
MemCacheD # M 0.610000 0.140000 0.750000 ( 0.878559)
|
86
|
+
MemCacheT # M 0.630000 0.140000 0.770000 ( 0.951748)
|
87
|
+
|
88
|
+
TokyoStore# G 0.410000 0.090000 0.500000 ( 0.976746)
|
89
|
+
RedisStore# G 0.930000 0.170000 1.100000 ( 1.572024)
|
90
|
+
MemCacheD # G 1.000000 0.200000 1.200000 ( 1.429635)
|
91
|
+
MemCacheT # G 1.060000 0.170000 1.230000 ( 1.558731)
|
92
|
+
|
93
|
+
TokyoStore# D 0.220000 0.170000 0.390000 ( 0.707877)
|
94
|
+
RedisStore# D 0.240000 0.110000 0.350000 ( 0.474265)
|
95
|
+
MemCacheD # D 0.560000 0.110000 0.670000 ( 0.775865)
|
96
|
+
MemCacheT # D 0.560000 0.140000 0.700000 ( 0.860964)
|
97
|
+
|
98
|
+
TokyoStore# + 0.230000 0.190000 0.420000 ( 0.654690)
|
99
|
+
RedisStore# + 0.200000 0.120000 0.320000 ( 0.494333)
|
100
|
+
MemCacheD # + 0.570000 0.130000 0.700000 ( 0.801407)
|
101
|
+
MemCacheT # + 0.540000 0.190000 0.730000 ( 0.937882)
|
102
|
+
|
103
|
+
TokyoStore# - 0.200000 0.210000 0.410000 ( 0.669899)
|
104
|
+
RedisStore# - 0.240000 0.120000 0.360000 ( 0.511279)
|
105
|
+
MemCacheD # - 0.600000 0.100000 0.700000 ( 0.778212)
|
106
|
+
MemCacheT # - 0.600000 0.140000 0.740000 ( 0.983345)
|
107
|
+
|
108
|
+
Read
|
109
|
+
--------------
|
110
|
+
TokyoStore# Seq 0.440000 0.150000 0.590000 ( 0.858501)
|
111
|
+
RedisStore# Seq 0.660000 0.130000 0.790000 ( 1.039310)
|
112
|
+
MemCacheD # Seq 0.830000 0.220000 1.050000 ( 1.135630)
|
113
|
+
MemCacheT # Seq 1.210000 0.190000 1.400000 ( 1.754004)
|
114
|
+
|
115
|
+
TokyoStore# Rand 0.490000 0.200000 0.690000 ( 1.003466)
|
116
|
+
RedisStore# Rand 0.580000 0.210000 0.790000 ( 1.877162)
|
117
|
+
MemCacheD # Rand 0.870000 0.170000 1.040000 ( 1.135382)
|
118
|
+
MemCacheT # Rand 1.410000 0.230000 1.640000 ( 1.971372)
|
119
|
+
|
120
|
+
TokyoStore# Exist 0.460000 0.140000 0.600000 ( 0.845920)
|
121
|
+
RedisStore# Exist 0.550000 0.250000 0.800000 ( 1.658580)
|
122
|
+
MemCacheD # Exist 0.810000 0.220000 1.030000 ( 1.138310)
|
123
|
+
MemCacheT # Exist 1.150000 0.250000 1.400000 ( 1.760770)
|
124
|
+
|
125
|
+
|
126
|
+
Tokyo # W 0.510000 0.150000 0.660000 ( 1.023739)
|
127
|
+
Redis # W 0.540000 0.140000 0.680000 ( 1.032881)
|
128
|
+
MemCa # W 2.530000 0.210000 2.740000 ( 3.072881)
|
129
|
+
MemTo # W 2.600000 0.280000 2.880000 ( 3.344125)
|
130
|
+
|
131
|
+
Tokyo # R 0.680000 0.210000 0.890000 ( 2.547828)
|
132
|
+
Redis # R 0.710000 0.220000 0.930000 ( 2.599269)
|
133
|
+
MemCa # R 2.190000 0.240000 2.430000 ( 2.977408)
|
134
|
+
MemTo # R 3.210000 0.330000 3.540000 ( 4.298882)
|
@@ -0,0 +1,75 @@
|
|
1
|
+
#
|
2
|
+
# Tokyo Store
|
3
|
+
#
|
4
|
+
# Session Benchmark vs Redis Store
|
5
|
+
#
|
6
|
+
# TODO: memcache
|
7
|
+
#
|
8
|
+
$: << File.join(File.dirname(__FILE__), "/../lib")
|
9
|
+
require 'rubygems'
|
10
|
+
require 'benchmark'
|
11
|
+
require 'rack'
|
12
|
+
require 'tokyo_store'
|
13
|
+
require 'redis-store'
|
14
|
+
#S = [Rack::Session::Tokyo::DEFAULT_OPTIONS[:key], { :test => "foo"}]
|
15
|
+
T = ARGV[0].to_i || 10000
|
16
|
+
|
17
|
+
rack = lambda do |env|
|
18
|
+
env["rack.session"]["counter"] ||= 0
|
19
|
+
env["rack.session"]["counter"] += 1
|
20
|
+
Rack::Response.new(env["rack.session"].inspect).to_a
|
21
|
+
end
|
22
|
+
|
23
|
+
TEST = {
|
24
|
+
"Tokyo" => Rack::Session::Tokyo.new(rack),
|
25
|
+
"Redis" => Rack::Session::Redis.new(rack),
|
26
|
+
}
|
27
|
+
bar = "_" * 45
|
28
|
+
|
29
|
+
puts "\n#{bar} SET"
|
30
|
+
Benchmark.bmbm do |b|
|
31
|
+
TEST.each_pair do |n,s|
|
32
|
+
b.report(n) { T.times { Rack::MockRequest.new(s).get("/") }}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
puts "\n#{bar} GET"
|
37
|
+
Benchmark.bmbm do |b|
|
38
|
+
TEST.each_pair do |n,s|
|
39
|
+
b.report(n) do T.times do
|
40
|
+
req = Rack::MockRequest.new(s)
|
41
|
+
cookie = req.get("/")["Set-Cookie"]
|
42
|
+
req.get("/", "HTTP_COOKIE" => cookie)
|
43
|
+
end end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
puts "\n#{bar} EXIST"
|
48
|
+
Benchmark.bmbm do |b|
|
49
|
+
TEST.each_pair do |n,s|
|
50
|
+
b.report(n) do T.times do
|
51
|
+
Rack::MockRequest.new(s).get("/", "HTTP_COOKIE" => "rack.session=badbadcookie")
|
52
|
+
end end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
__END__
|
58
|
+
|
59
|
+
*Note: Tokyo uses FFI adapter, while redis a pure ruby.
|
60
|
+
Both comparable to memcache pr too. Check the cache benchmark.
|
61
|
+
|
62
|
+
_____________________________________________ GET
|
63
|
+
Tokyo 6.310000 1.030000 7.340000 ( 8.511138)
|
64
|
+
Redis 7.300000 1.010000 8.310000 ( 9.325441)
|
65
|
+
------------------------------- total: 15.650000sec
|
66
|
+
|
67
|
+
_____________________________________________ SET
|
68
|
+
Tokyo 3.340000 0.540000 3.880000 ( 4.562920)
|
69
|
+
Redis 3.960000 0.540000 4.500000 ( 5.030627)
|
70
|
+
-------------------------------- total: 8.380000sec
|
71
|
+
|
72
|
+
_____________________________________________ EXIST
|
73
|
+
Tokyo 4.090000 0.650000 4.740000 ( 5.537898)
|
74
|
+
Redis 4.700000 0.700000 5.400000 ( 6.061131)
|
75
|
+
------------------------------- total: 10.140000sec
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'rufus/tokyo/tyrant'
|
2
|
+
# require 'tokyocabinet'
|
3
|
+
module ActiveSupport
|
4
|
+
module Cache
|
5
|
+
|
6
|
+
# A cache store implementation which stores data in Tokyo Cabinet
|
7
|
+
#
|
8
|
+
# Special features:
|
9
|
+
# - Substring match
|
10
|
+
# - Clustering and load balancing. TODO
|
11
|
+
# - Time-based expiry support. TODO (Lua)
|
12
|
+
# - Per-request in memory cache for all communication with the Tokyo server(s).
|
13
|
+
class TokyoStore < Store
|
14
|
+
|
15
|
+
def self.build_tokyo(*store)
|
16
|
+
store = store.flatten
|
17
|
+
options = store.extract_options!
|
18
|
+
#TODO: multiple instances
|
19
|
+
store = store.empty? ? ["localhost", 1978] : store[0].split(":")
|
20
|
+
|
21
|
+
#TODO: Auto choice between tyrant ffi x tyrant pure ruby x cabinet C
|
22
|
+
# Tyrant FFI
|
23
|
+
Rufus::Tokyo::Tyrant.new(store[0], store[1].to_i)
|
24
|
+
|
25
|
+
# Cabinet C
|
26
|
+
#hdb = HDB.new
|
27
|
+
# if !hdb.open(store[0], HDB::OWRITER | HDB::OCREAT)
|
28
|
+
# ecode = hdb.ecode
|
29
|
+
# STDERR.printf("open error: %s\n", hdb.errmsg(ecode))
|
30
|
+
# end
|
31
|
+
# hdb
|
32
|
+
end
|
33
|
+
|
34
|
+
# Creates a new TokyoStore object, with the given tyrant server
|
35
|
+
# addresses. Each address is either a host name, or a host-with-port string
|
36
|
+
# in the form of "host_name:port". For example:
|
37
|
+
#
|
38
|
+
# ActiveSupport::Cache::TokyoStore.new("localhost", "server-downstairs.localnetwork:8229")
|
39
|
+
#
|
40
|
+
# If no addresses are specified, then TokyoStore will connect to
|
41
|
+
# localhost port 1978 (the default tyrant port).
|
42
|
+
def initialize(*store)
|
43
|
+
if store.first.respond_to?(:get)
|
44
|
+
@data = store.first
|
45
|
+
else
|
46
|
+
@data = self.class.build_tokyo(*store)
|
47
|
+
end
|
48
|
+
|
49
|
+
extend Strategy::LocalCache
|
50
|
+
end
|
51
|
+
|
52
|
+
# Reads multiple keys from the cache.
|
53
|
+
def read_multi(*keys)
|
54
|
+
#keys.inject({ }){ |h,k| h.merge({ k => read(k)}) }
|
55
|
+
@data.lget(keys).inject({ }) { |h, k| h.merge({ k[0] => Marshal.load(k[1])})} #
|
56
|
+
end
|
57
|
+
|
58
|
+
def read(key, options = nil) # :nodoc:
|
59
|
+
# TODO: benchmark [key] vs .get(key)
|
60
|
+
super
|
61
|
+
return nil unless val = @data[key]
|
62
|
+
val = Marshal.load(val) unless raw?(options)
|
63
|
+
val
|
64
|
+
# if str = @data.get(key)
|
65
|
+
# Marshal.load str
|
66
|
+
# else
|
67
|
+
# STDERR.printf("get error: %s\n", @data.errmsg(@data.ecode))
|
68
|
+
# end
|
69
|
+
# logger.error("TokyoError (#{e}): #{e.message}")
|
70
|
+
# nil
|
71
|
+
end
|
72
|
+
|
73
|
+
# Writes a value to the cache.
|
74
|
+
#
|
75
|
+
# Possible options:
|
76
|
+
# - +:unless_exist+ - set to true if you don't want to update the cache
|
77
|
+
# if the key is already set.
|
78
|
+
def write(key, value, options = nil)
|
79
|
+
super
|
80
|
+
method = options && options[:unless_exist] ? :add : :set
|
81
|
+
# will break the connection if you send it an integer
|
82
|
+
# in raw mode, so we convert it to a string to be sure it continues working.
|
83
|
+
value = raw?(options) ? value.to_s : Marshal.dump(value) # if value.instance_of? Hash
|
84
|
+
|
85
|
+
@data[key] = value
|
86
|
+
###response = @data.put(key, value) || STDERR.printf("get error: %s\n", @data.errmsg(@data.ecode))#, expires_in(options), raw?(options))
|
87
|
+
# logger.error("TokyoError (#{e}): #{e.message}")
|
88
|
+
# false
|
89
|
+
end
|
90
|
+
|
91
|
+
def delete(key, options = nil) # :nodoc:
|
92
|
+
super
|
93
|
+
@data.delete(key) #= nil #, expires_in(options))
|
94
|
+
end
|
95
|
+
|
96
|
+
def exist?(key, options = nil) # :nodoc:
|
97
|
+
# Local cache is checked first?
|
98
|
+
!read(key, options).nil?
|
99
|
+
end
|
100
|
+
|
101
|
+
def increment(key, amount = 1) # :nodoc:
|
102
|
+
#NATIVE breaks...rufus integer prob?
|
103
|
+
# @data.incr(key, amount)
|
104
|
+
@data[key] = (@data[key].to_i + amount).to_s
|
105
|
+
end
|
106
|
+
|
107
|
+
def decrement(key, amount = 1) # :nodoc:
|
108
|
+
# @data.incr(key, -amount)
|
109
|
+
increment(key, -amount)
|
110
|
+
end
|
111
|
+
|
112
|
+
def delete_matched(matcher, options = nil) # :nodoc:
|
113
|
+
super
|
114
|
+
@data.delete_keys_with_prefix(matcher)
|
115
|
+
end
|
116
|
+
|
117
|
+
def clear
|
118
|
+
@data.clear
|
119
|
+
end
|
120
|
+
|
121
|
+
def stats
|
122
|
+
@data.stat
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
#TODO
|
127
|
+
# def expires_in(options)
|
128
|
+
# (options && options[:expires_in]) || 0
|
129
|
+
# end
|
130
|
+
|
131
|
+
def raw?(options)
|
132
|
+
options && options[:raw]
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
end
|