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.
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