tokyo_store 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
File without changes
File without changes
@@ -0,0 +1,70 @@
1
+ require 'tokyocabinet'
2
+ module Rack
3
+ module Session
4
+ class Cabinet < Abstract::ID
5
+ include TokyoCabinet
6
+ attr_reader :mutex, :pool
7
+ DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :cabinet_file => "/tmp/session.tch"
8
+
9
+ def initialize(app, options = {})
10
+ super
11
+ @mutex = Mutex.new
12
+ @db = options[:cabinet_file] || @default_options[:cabinet_file]
13
+ tokyo_connect
14
+ end
15
+
16
+ private
17
+ def tokyo_connect
18
+ @pool = HDB.new
19
+ unless @pool.open(@db, HDB::OREADER | HDB::OWRITER | HDB::OCREAT)
20
+ warn "Can't open db file '#{@db}', #{@pool.errmsg}."
21
+ end
22
+ end
23
+
24
+ def get_session(env, sid)
25
+ session = Marshal.load(@pool.get(sid)) rescue session if sid && session = @pool.get(sid)
26
+ @mutex.lock if env['rack.multithread']
27
+ unless sid && session
28
+ env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil?
29
+ session = {}
30
+ sid = generate_sid
31
+ ret = @pool.put(sid, Marshal.dump(session))
32
+ raise "Session collision on '#{sid.inspect}'" unless ret
33
+ end
34
+ return [sid, session]
35
+ rescue Rufus::Tokyo::TokyoError => e
36
+ return [nil, {}]
37
+ ensure
38
+ @mutex.unlock if env['rack.multithread']
39
+ end
40
+
41
+ def set_session(env, sid, new_session, options)
42
+ @mutex.lock if env['rack.multithread']
43
+ session = Marshal.load(session) rescue session if session = @pool.get(sid)
44
+ if options[:renew] || options[:drop]
45
+ @pool.out(sid)
46
+ return false if options[:drop]
47
+ sid = generate_sid
48
+ @pool.put(sid, "")
49
+ end
50
+ @pool.put(sid, options && options[:raw] ? new_session : Marshal.dump(new_session))
51
+ return sid
52
+ rescue Rufus::Tokyo::TokyoError => e
53
+ warn "#{self} is unable to find server, error: #{e}"
54
+ warn $!.inspect
55
+ ensure
56
+ @mutex.unlock if env['rack.multithread']
57
+ end
58
+
59
+ def generate_sid
60
+ loop do
61
+ sid = super
62
+ break sid unless @pool.get(sid)
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+
70
+ end
@@ -0,0 +1,81 @@
1
+ require 'rufus/tokyo/tyrant'
2
+
3
+ module Rack
4
+ module Session
5
+ class RufusTyrant < Abstract::ID
6
+ attr_reader :mutex, :pool
7
+ DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :tyrant_server => "localhost:1978"
8
+
9
+ def initialize(app, options = {})
10
+ super
11
+ @mutex = Mutex.new
12
+ @host, @port = *(options[:tyrant_server] || @default_options[:tyrant_server]).split(":") # @default_options) #options[:cache] ||
13
+ # connecting & closing on each get and put
14
+ # not sure if this is the best option, but otherwise it'll keep
15
+ # opening connections until tyrant freezes... =/
16
+ tokyo_connect
17
+ end
18
+
19
+ private
20
+ def tokyo_connect
21
+ begin
22
+ @pool ||= Rufus::Tokyo::Tyrant.new(@host, @port.to_i)
23
+ rescue Rufus::Tokyo::TokyoError => e
24
+ warn "Can't connect to Tyrant #{e}"
25
+ end
26
+ end
27
+
28
+ def get_session(env, sid)
29
+ # tokyo_connect
30
+ session = Marshal.load(@pool[sid]) rescue session if sid && session = @pool[sid]
31
+ @mutex.lock if env['rack.multithread']
32
+ unless sid && session
33
+ env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil?
34
+ session = {}
35
+ sid = generate_sid
36
+ ret = @pool[sid] = Marshal.dump(session)
37
+ raise "Session collision on '#{sid.inspect}'" unless ret
38
+ end
39
+ session.instance_variable_set('@old', {}.merge(session))
40
+ return [sid, session]
41
+ rescue Rufus::Tokyo::TokyoError => e
42
+ return [nil, {}]
43
+ ensure
44
+ @mutex.unlock if env['rack.multithread']
45
+ # @pool.close
46
+ end
47
+
48
+ def set_session(env, sid, new_session, options)
49
+ # tokyo_connect
50
+ @mutex.lock if env['rack.multithread']
51
+ session = Marshal.load(session) rescue session if session = @pool[sid]
52
+ if options[:renew] || options[:drop]
53
+ @pool.delete sid
54
+ return false if options[:drop]
55
+ sid = generate_sid
56
+ @pool[sid] = ""
57
+ end
58
+ old_session = new_session.instance_variable_get('@old') || {}
59
+ session = new_session
60
+ @pool[sid] = options && options[:raw] ? session : Marshal.dump(session)
61
+ return sid
62
+ rescue Rufus::Tokyo::TokyoError => e
63
+ warn "#{self} is unable to find server, error: #{e}"
64
+ warn $!.inspect
65
+ ensure
66
+ @mutex.unlock if env['rack.multithread']
67
+ # @pool.close
68
+ end
69
+
70
+ def generate_sid
71
+ loop do
72
+ sid = super
73
+ break sid unless @pool[sid]
74
+ end
75
+ end
76
+
77
+ end
78
+
79
+ end
80
+
81
+ end
@@ -0,0 +1,70 @@
1
+ require 'tokyotyrant'
2
+ module Rack
3
+ module Session
4
+ class Tyrant < Abstract::ID
5
+ include TokyoTyrant
6
+ attr_reader :mutex, :pool
7
+ DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :tyrant_server => "localhost:1978"
8
+
9
+ def initialize(app, options = {})
10
+ super
11
+ @mutex = Mutex.new
12
+ @host, @port = *(options[:tyrant_server] || @default_options[:tyrant_server]).split(":")
13
+ tokyo_connect
14
+ end
15
+
16
+ private
17
+ def tokyo_connect
18
+ @pool = RDB.new
19
+ unless @pool.open(@host, @port.to_i)
20
+ warn "Can't connect to Tyrant #{@host}:#{@port}"
21
+ end
22
+ end
23
+
24
+ def get_session(env, sid)
25
+ session = Marshal.load(@pool.get(sid)) rescue session if sid && session = @pool.get(sid)
26
+ @mutex.lock if env['rack.multithread']
27
+ unless sid && session
28
+ env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil?
29
+ session = {}
30
+ sid = generate_sid
31
+ ret = @pool.put(sid, Marshal.dump(session))
32
+ raise "Session collision on '#{sid.inspect}'" unless ret
33
+ end
34
+ return [sid, session]
35
+ rescue Rufus::Tokyo::TokyoError => e
36
+ return [nil, {}]
37
+ ensure
38
+ @mutex.unlock if env['rack.multithread']
39
+ end
40
+
41
+ def set_session(env, sid, new_session, options)
42
+ @mutex.lock if env['rack.multithread']
43
+ session = Marshal.load(session) rescue session if session = @pool.get(sid)
44
+ if options[:renew] || options[:drop]
45
+ @pool.delete sid
46
+ return false if options[:drop]
47
+ sid = generate_sid
48
+ @pool.put(sid, "")
49
+ end
50
+ @pool.put(sid, options && options[:raw] ? new_session : Marshal.dump(new_session))
51
+ return sid
52
+ rescue Rufus::Tokyo::TokyoError => e
53
+ warn "#{self} is unable to find server, error: #{e}"
54
+ warn $!.inspect
55
+ ensure
56
+ @mutex.unlock if env['rack.multithread']
57
+ end
58
+
59
+ def generate_sid
60
+ loop do
61
+ sid = super
62
+ break sid unless @pool.get(sid)
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+
70
+ end
@@ -0,0 +1,16 @@
1
+
2
+ # Rack Session
3
+ if defined?(Rack::Session)
4
+ require "rack/session/abstract/id"
5
+ require "rack/session/tyrant"
6
+ #require "rack/session/cabinet"
7
+ #require "rack/session/rufus_tyrant"
8
+ end
9
+
10
+ # # Cache store
11
+ # if defined?(Sinatra)
12
+ # require "cache/sinatra/tokyo_store"
13
+ # elsif defined?(Rails)
14
+ # require "cache/rails/tokyo_store"
15
+ # end
16
+
@@ -0,0 +1,256 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe "TokyoStore" do
4
+ it "should store fragment cache" do
5
+ Rufus::Tokyo::Tyrant.should_receive(:new).and_return(@mock_tyrant = mock("Tyrant"))
6
+ store = ActiveSupport::Cache.lookup_store :tokyo_store, "data.tch"
7
+ store.should be_kind_of ActiveSupport::Cache::TokyoStore
8
+ end
9
+
10
+ it "should fail" do
11
+ tokyo = Rufus::Tokyo::Tyrant.new('localhost', 1978)
12
+ Rufus::Tokyo::Tyrant.should_not_receive(:new)
13
+ store = ActiveSupport::Cache.lookup_store :tokyo_store, tokyo
14
+ store.should be_kind_of ActiveSupport::Cache::TokyoStore
15
+ end
16
+
17
+ describe "Similar" do
18
+
19
+ before(:each) do
20
+ @cache = ActiveSupport::Cache::TokyoStore.new 'localhost:1978'
21
+ @cache.clear
22
+ end
23
+
24
+ it "should return true on success" do
25
+ @cache.write('foo', 'bar').should be_true
26
+ end
27
+
28
+ it "should read and write strings" do
29
+ @cache.write('foo', 'bar')
30
+ @cache.read('foo').should eql('bar')
31
+ end
32
+
33
+ it "should read and write hash" do
34
+ @cache.write('foo', {:a => "b"})
35
+ @cache.read('foo').should eql({:a => "b"})
36
+ end
37
+
38
+ it "should write integers" do
39
+ @cache.write('foo', 1)
40
+ @cache.read('foo').should eql(1)
41
+ end
42
+
43
+ it "should write nil" do
44
+ @cache.write('foo', nil)
45
+ @cache.read('foo').should eql(nil)
46
+ end
47
+
48
+ it "should have a cache miss block" do
49
+ @cache.write('foo', 'bar')
50
+ @cache.fetch('foo') { 'baz' }.should eql('bar')
51
+ end
52
+
53
+ it "should have a cache miss block" do
54
+ @cache.fetch('foo') { 'baz' }.should eql('baz')
55
+ end
56
+
57
+ it "should have a forced cache miss block" do
58
+ @cache.fetch('foo', :force => true).should be_nil
59
+ end
60
+
61
+ it "should read and write hash" do
62
+ @cache.write('foo', {:a => "b", :c => "d"})
63
+ @cache.read('foo').should eql({:a => "b", :c => "d"})
64
+ end
65
+
66
+ it "should read and write array" do
67
+ @cache.write('foo', [1,2,3])
68
+ @cache.read('foo').should eql([1,2,3])
69
+ end
70
+
71
+ it "should read and write obj" do
72
+ obj = City.new; obj.name = "Acapulco"; obj.pop = 717766
73
+ @cache.write('foo', obj)
74
+ @cache.read('foo').should be_instance_of City
75
+ @cache.read('foo').name.should eql("Acapulco")
76
+ end
77
+
78
+ it "should read multiples" do
79
+ @cache.write('a', 1)
80
+ @cache.write('b', 2)
81
+ @cache.read_multi('a','b').should eql({ 'a' => 1, 'b' => 2})
82
+ end
83
+
84
+ it "should clear all" do
85
+ @cache.write("erase_me", 1).should be_true
86
+ @cache.delete("erase_me")
87
+ @cache.exist?("erase_me").should be_false
88
+ end
89
+
90
+ it "should check if exists" do
91
+ @cache.exist?("new_one").should be_false
92
+ @cache.write("new_one", 1)
93
+ @cache.exist?("new_one").should be_true
94
+ end
95
+
96
+ it "should increment value" do
97
+ @cache.write('val', 1, :raw => true)
98
+ @cache.read("val", :raw => true).to_i.should eql 1
99
+ @cache.increment('val')
100
+ @cache.read("val", :raw => true).to_i.should eql 2
101
+ @cache.increment('val')
102
+ @cache.read("val", :raw => true).to_i.should eql 3
103
+ end
104
+
105
+ it "should decrement value" do
106
+ @cache.write('val', 3, :raw => true)
107
+ @cache.read("val", :raw => true).to_i.should eql 3
108
+ @cache.decrement('val')
109
+ @cache.read("val", :raw => true).to_i.should eql 2
110
+ @cache.decrement('val')
111
+ @cache.read("val", :raw => true).to_i.should eql 1
112
+ end
113
+
114
+ it "should clear all" do
115
+ @cache.increment("val")
116
+ @cache.exist?("val", :raw => true).should be_true
117
+ @cache.clear
118
+ @cache.exist?("val").should be_false
119
+ end
120
+
121
+ it "should show some stats" do
122
+ @cache.stats.should be_instance_of Hash #== hash_including({ :type => "hash"})
123
+ end
124
+
125
+ it "store objects should be immutable" do
126
+ @cache.with_local_cache do
127
+ @cache.write('foo', 'bar')
128
+ @cache.read('foo').gsub!(/.*/, 'baz')# }.should raise_error(ActiveSupport::FrozenObjectError)
129
+ @cache.read('foo').should == 'bar'
130
+ end
131
+ end
132
+
133
+ it "stored objects should not be frozen" do
134
+ pending "It's on the rails tests..."
135
+ @cache.with_local_cache do
136
+ @cache.write('foo', 'bar')
137
+ end
138
+ @cache.with_local_cache do
139
+ @cache.read('foo').should_not be_frozen
140
+ end
141
+ end
142
+
143
+ it "should delete matched" do
144
+ ["val", "value", "vall", "val/1", "vla", "xla", "xla/1"].each do |v|
145
+ @cache.write(v, 1)
146
+ end
147
+
148
+ @cache.delete_matched('val')
149
+ ["val", "value", "vall", "val/1"].each { |v| @cache.read(v).should be_nil }
150
+ ["vla", "xla", "xla/1"].each { |v| @cache.read(v).should eql(1) }
151
+ end
152
+
153
+ end
154
+
155
+ describe "backed store" do
156
+ before(:each) do
157
+ @cache = ActiveSupport::Cache.lookup_store(:tokyo_store)
158
+ @data = @cache.instance_variable_get(:@data)
159
+ @cache.clear
160
+ end
161
+
162
+ it "local_writes_are_persistent_on_the_remote_cache" do
163
+ @cache.with_local_cache do
164
+ @cache.write('foo', 'bar')
165
+ end
166
+
167
+ @cache.read('foo').should eql('bar')
168
+ end
169
+
170
+ it "test_clear_also_clears_local_cache" do
171
+ @cache.with_local_cache do
172
+ @cache.write('foo', 'bar')
173
+ @cache.clear
174
+ @cache.read('foo').should be_nil
175
+ end
176
+ end
177
+
178
+ it "test_local_cache_of_read_and_write" do
179
+ @cache.with_local_cache do
180
+ @cache.write('foo', 'bar')
181
+ @data.clear # Clear remote cache
182
+ @cache.read('foo').should eql('bar')
183
+ end
184
+ end
185
+
186
+ it "test_local_cache_should_read_and_write_integer" do
187
+ @cache.with_local_cache do
188
+ @cache.write('foo', 1)
189
+ @cache.read('foo').should eql(1)
190
+ end
191
+ end
192
+
193
+ it "test_local_cache_of_delete" do
194
+ @cache.with_local_cache do
195
+ @cache.write('foo', 'bar')
196
+ @cache.delete('foo')
197
+ @data.clear # Clear remote cache
198
+ @cache.read('foo').should be_nil
199
+ end
200
+ end
201
+
202
+ it "test_local_cache_of_exist" do
203
+ @cache.with_local_cache do
204
+ @cache.write('foo', 'bar')
205
+ @cache.instance_variable_set(:@data, nil)
206
+ @data.clear # Clear remote cache
207
+ @cache.exist?('foo').should be_true
208
+ end
209
+ end
210
+
211
+ it "test_local_cache_of_increment" do
212
+ @cache.with_local_cache do
213
+ @cache.write('foo', 1, :raw => true)
214
+ @cache.increment('foo')
215
+ @data.clear # Clear remote cache
216
+ @cache.read('foo', :raw => true).to_i.should eql(2)
217
+ end
218
+ end
219
+
220
+ it "test_local_cache_of_decrement" do
221
+ @cache.with_local_cache do
222
+ @cache.write('foo', 1, :raw => true)
223
+ @cache.decrement('foo')
224
+ @data.clear # Clear remote cache
225
+ @cache.read('foo', :raw => true).to_i.should be_zero
226
+ end
227
+ end
228
+
229
+ it "test_exist_with_nulls_cached_locally" do
230
+ @cache.with_local_cache do
231
+ @cache.write('foo', 'bar')
232
+ @cache.delete('foo')
233
+ @cache.exist?('foo').should be_false
234
+ end
235
+ end
236
+
237
+ it "test_multi_get" do
238
+ @cache.with_local_cache do
239
+ @cache.write('foo', 1)
240
+ @cache.write('goo', 2)
241
+ @cache.read_multi('foo', 'goo').should eql({'foo' => 1, 'goo' => 2})
242
+ end
243
+ end
244
+
245
+ it "test_middleware" do
246
+ app = lambda { |env|
247
+ result = @cache.write('foo', 'bar')
248
+ @cache.read('foo').should eql('bar') # make sure 'foo' was written
249
+ }
250
+ app = @cache.middleware.new(app)
251
+ app.call({})
252
+ end
253
+
254
+ end
255
+
256
+ end