Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

add tyrant pure ruby and cabinet C

  • Loading branch information...
commit 42077ef0af95f568c86d5e80cb7cd456cf7454a6 1 parent 09b7bc1
@nofxx authored
View
14 benchmark/session.rb
@@ -26,6 +26,13 @@
}
bar = "_" * 45
+puts "\n#{bar} SET"
+Benchmark.bmbm do |b|
+ TEST.each_pair do |n,s|
+ b.report(n) { T.times { Rack::MockRequest.new(s).get("/") }}
+ end
+end
+
puts "\n#{bar} GET"
Benchmark.bmbm do |b|
TEST.each_pair do |n,s|
@@ -37,13 +44,6 @@
end
end
-puts "\n#{bar} SET"
-Benchmark.bmbm do |b|
- TEST.each_pair do |n,s|
- b.report(n) { T.times { Rack::MockRequest.new(s).get("/") }}
- end
-end
-
puts "\n#{bar} EXIST"
Benchmark.bmbm do |b|
TEST.each_pair do |n,s|
View
77 lib/rack/session/cabinet.rb
@@ -0,0 +1,77 @@
+require 'tokyocabinet'
+module Rack
+ module Session
+ class Cabinet < Abstract::ID
+ include TokyoCabinet
+ attr_reader :mutex, :pool
+ DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :cabinet_file => "session_cabinet.tch"
+
+ def initialize(app, options = {})
+ super
+ @mutex = Mutex.new
+ @db = options[:cabinet_file] || @default_options[:cabinet_file]
+ tokyo_connect
+ end
+
+ private
+ def tokyo_connect
+ @pool = HDB.new
+ unless @pool.open(@db, HDB::OWRITER | HDB::OCREAT)
+ warn "Can't open db file #{@db}"
+ end
+ end
+
+ def get_session(env, sid)
+ # tokyo_connect
+ session = Marshal.load(@pool.get(sid)) rescue session if sid && session = @pool.get(sid)
+ @mutex.lock if env['rack.multithread']
+ unless sid && session
+ env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil?
+ session = {}
+ sid = generate_sid
+ ret = @pool.put(sid, Marshal.dump(session))
+ raise "Session collision on '#{sid.inspect}'" unless ret
+ end
+ #session.instance_variable_put('@old', {}.merge(session))
+ return [sid, session]
+ rescue Rufus::Tokyo::TokyoError => e
+ return [nil, {}]
+ ensure
+ @mutex.unlock if env['rack.multithread']
+ # @pool.close
+ end
+
+ def set_session(env, sid, new_session, options)
+ # tokyo_connect
+ @mutex.lock if env['rack.multithread']
+ session = Marshal.load(session) rescue session if session = @pool.get(sid)
+ if options[:renew] || options[:drop]
+ @pool.out(sid)
+ return false if options[:drop]
+ sid = generate_sid
+ @pool.put(sid, "")
+ end
+ #old_session = new_session.instance_variable_get('@old') || {}
+ session = new_session
+ @pool.put(sid, options && options[:raw] ? session : Marshal.dump(session))
+ return sid
+ rescue Rufus::Tokyo::TokyoError => e
+ warn "#{self} is unable to find server, error: #{e}"
+ warn $!.inspect
+ ensure
+ @mutex.unlock if env['rack.multithread']
+ # @pool.close
+ end
+
+ def generate_sid
+ loop do
+ sid = super
+ break sid unless @pool.get(sid)
+ end
+ end
+
+ end
+
+ end
+
+end
View
29 lib/rack/session/tokyo.rb
@@ -12,21 +12,24 @@ def initialize(app, options = {})
# not sure if this is the best option, but otherwise it'll keep
# opening connections until tyrant freezes... =/
# tokyo_connect
+ # tokyo_connect
+ p @pool
+ @pool ||= Rufus::Tokyo::Tyrant.new(@host, @port.to_i)
end
private
- def tokyo_connect
- begin
- @pool = Rufus::Tokyo::Tyrant.new(@host, @port.to_i)
- rescue Rufus::Tokyo::TokyoError => e
- warn "Can't connect to Tyrant #{e}"
- end
- end
+ # def tokyo_connect
+ # begin
+
+ # rescue Rufus::Tokyo::TokyoError => e
+ # warn "Can't connect to Tyrant #{e}"
+ # end
+ # end
def get_session(env, sid)
- tokyo_connect
- @mutex.lock if env['rack.multithread']
+ # tokyo_connect
session = Marshal.load(@pool[sid]) rescue session if sid && session = @pool[sid]
+ @mutex.lock if env['rack.multithread']
unless sid && session
env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil?
session = {}
@@ -37,14 +40,14 @@ def get_session(env, sid)
session.instance_variable_set('@old', {}.merge(session))
return [sid, session]
rescue Rufus::Tokyo::TokyoError => e
- session = {}
+ return [nil, {}]
ensure
@mutex.unlock if env['rack.multithread']
- @pool.close
+ # @pool.close
end
def set_session(env, sid, new_session, options)
- tokyo_connect
+ # tokyo_connect
@mutex.lock if env['rack.multithread']
session = Marshal.load(session) rescue session if session = @pool[sid]
if options[:renew] || options[:drop]
@@ -62,7 +65,7 @@ def set_session(env, sid, new_session, options)
warn $!.inspect
ensure
@mutex.unlock if env['rack.multithread']
- @pool.close
+ # @pool.close
end
def generate_sid
View
77 lib/rack/session/tyrant.rb
@@ -0,0 +1,77 @@
+require 'tokyotyrant'
+module Rack
+ module Session
+ class Tyrant < Abstract::ID
+ include TokyoTyrant
+ attr_reader :mutex, :pool
+ DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :tyrant_server => "localhost:1978"
+
+ def initialize(app, options = {})
+ super
+ @mutex = Mutex.new
+ @host, @port = *(options[:tyrant_server] || @default_options[:tyrant_server]).split(":") # @default_options) #options[:cache] ||
+ tokyo_connect
+ end
+
+ private
+ def tokyo_connect
+ @pool = RDB.new
+ unless @pool.open(@host, @port.to_i)
+ warn "Can't connect to Tyrant #{e}"
+ end
+ end
+
+ def get_session(env, sid)
+ # tokyo_connect
+ session = Marshal.load(@pool.get(sid)) rescue session if sid && session = @pool.get(sid)
+ @mutex.lock if env['rack.multithread']
+ unless sid && session
+ env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil?
+ session = {}
+ sid = generate_sid
+ ret = @pool.put(sid, Marshal.dump(session))
+ raise "Session collision on '#{sid.inspect}'" unless ret
+ end
+ #session.instance_variable_put('@old', {}.merge(session))
+ return [sid, session]
+ rescue Rufus::Tokyo::TokyoError => e
+ return [nil, {}]
+ ensure
+ @mutex.unlock if env['rack.multithread']
+ # @pool.close
+ end
+
+ def set_session(env, sid, new_session, options)
+ # tokyo_connect
+ @mutex.lock if env['rack.multithread']
+ session = Marshal.load(session) rescue session if session = @pool.get(sid)
+ if options[:renew] || options[:drop]
+ @pool.delete sid
+ return false if options[:drop]
+ sid = generate_sid
+ @pool.put(sid, "")
+ end
+ #old_session = new_session.instance_variable_get('@old') || {}
+ session = new_session
+ @pool.put(sid, options && options[:raw] ? session : Marshal.dump(session))
+ return sid
+ rescue Rufus::Tokyo::TokyoError => e
+ warn "#{self} is unable to find server, error: #{e}"
+ warn $!.inspect
+ ensure
+ @mutex.unlock if env['rack.multithread']
+ # @pool.close
+ end
+
+ def generate_sid
+ loop do
+ sid = super
+ break sid unless @pool.get(sid)
+ end
+ end
+
+ end
+
+ end
+
+end
View
249 spec/rack/session/cabinet_spec.rb
@@ -0,0 +1,249 @@
+# Code from http://github.com/jodosha/redis-store
+require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
+
+module Rack
+ module Session
+ # class Tokyo
+ # attr_reader :
+ # end
+ describe "Rack::Session::Cabinet" do
+ before(:each) do
+ @session_key = Rack::Session::Cabinet::DEFAULT_OPTIONS[:key]
+ @session_match = /#{@session_key}=[0-9a-fA-F]+;/
+ @incrementor = lambda do |env|
+ env["rack.session"]["counter"] ||= 0
+ env["rack.session"]["counter"] += 1
+ Rack::Response.new(env["rack.session"].inspect).to_a
+ end
+ @drop_session = proc do |env|
+ env['rack.session.options'][:drop] = true
+ @incrementor.call(env)
+ end
+ @renew_session = proc do |env|
+ env['rack.session.options'][:renew] = true
+ @incrementor.call(env)
+ end
+ @defer_session = proc do |env|
+ env['rack.session.options'][:defer] = true
+ @incrementor.call(env)
+ end
+ end
+
+ it "should specify connection params" do
+ pool = Rack::Session::Cabinet.new(@incrementor, :cabinet_file => "some.tch").pool
+ # pool.should be_kind_of(Rufus::Tokyo::Cabinet)
+ pool.host.should eql("127.0.0.1")
+ pool.port.should eql(1978)
+
+ # pool = Rack::Session::Tokyo.new(@incrementor, :tokyo_server => ["localhost:6379", "localhost:6380"]).pool
+ # pool.should be_kind_of(DistributedMarshaledTokyo)
+ end
+
+ it "should raise tokyo error on connect" do
+ lambda{ Rack::Session::Tokyo.new(@incrementor, :cabinet_file => "session.tch").pool }.
+ should_not raise_error(Rufus::Tokyo::TokyoError)
+ end
+
+
+ it "creates a new cookie" do
+ pool = Rack::Session::Cabinet.new(@incrementor)
+ res = Rack::MockRequest.new(pool).get("/")
+ res["Set-Cookie"].should match(/#{@session_key}=/)
+ res.body.should eql('{"counter"=>1}')
+ end
+
+ it "determines session from a cookie" do
+ pool = Rack::Session::Cabinet.new(@incrementor)
+ req = Rack::MockRequest.new(pool)
+ res = req.get("/")
+ cookie = res["Set-Cookie"]
+ req.get("/", "HTTP_COOKIE" => cookie).body.should eql('{"counter"=>2}')
+ req.get("/", "HTTP_COOKIE" => cookie).body.should eql('{"counter"=>3}')
+ end
+
+ it "survives nonexistant cookies" do
+ bad_cookie = "rack.session=blsarghfasel"
+ pool = Rack::Session::Cabinet.new(@incrementor)
+ res = Rack::MockRequest.new(pool).get("/", "HTTP_COOKIE" => bad_cookie)
+ res.body.should eql('{"counter"=>1}')
+ cookie = res["Set-Cookie"][@session_match]
+ cookie.should_not match(/#{bad_cookie}/)
+ end
+
+ # Expire isn't supported by cabinet. Implement in ruby?
+ # it "should maintain freshness" do
+ # pool = Rack::Session::Cabinet.new(@incrementor, :expire_after => 3)
+ # res = Rack::MockRequest.new(pool).get('/')
+ # res.body.should include('"counter"=>1')
+ # cookie = res["Set-Cookie"]
+ # res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie)
+ # res["Set-Cookie"].should == cookie
+ # res.body.should include('"counter"=>2')
+ # puts 'Sleeping to expire session' if $DEBUG
+ # sleep 4
+ # res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie)
+ # res["Set-Cookie"].should_not == cookie
+ # res.body.should include('"counter"=>1')
+ # end
+
+ it "deletes cookies with :drop option" do
+ pool = Rack::Session::Cabinet.new(@incrementor)
+ req = Rack::MockRequest.new(pool)
+ drop = Rack::Utils::Context.new(pool, @drop_session)
+ dreq = Rack::MockRequest.new(drop)
+
+ res0 = req.get("/")
+ session = (cookie = res0["Set-Cookie"])[@session_match]
+ res0.body.should == '{"counter"=>1}'
+
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
+ res1["Set-Cookie"][@session_match].should == session
+ res1.body.should == '{"counter"=>2}'
+
+ res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
+ res2["Set-Cookie"].should be_nil
+ res2.body.should == '{"counter"=>3}'
+
+ res3 = req.get("/", "HTTP_COOKIE" => cookie)
+ res3["Set-Cookie"][@session_match].should_not == session
+ res3.body.should == '{"counter"=>1}'
+ end
+
+ it "provides new session id with :renew option" do
+ pool = Rack::Session::Cabinet.new(@incrementor)
+ req = Rack::MockRequest.new(pool)
+ renew = Rack::Utils::Context.new(pool, @renew_session)
+ rreq = Rack::MockRequest.new(renew)
+
+ res0 = req.get("/")
+ session = (cookie = res0["Set-Cookie"])[@session_match]
+ res0.body.should == '{"counter"=>1}'
+
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
+ res1["Set-Cookie"][@session_match].should == session
+ res1.body.should == '{"counter"=>2}'
+
+ res2 = rreq.get("/", "HTTP_COOKIE" => cookie)
+ new_cookie = res2["Set-Cookie"]
+ new_session = new_cookie[@session_match]
+ new_session.should_not == session
+ res2.body.should == '{"counter"=>3}'
+
+ res3 = req.get("/", "HTTP_COOKIE" => new_cookie)
+ res3["Set-Cookie"][@session_match].should == new_session
+ res3.body.should == '{"counter"=>4}'
+ end
+
+ specify "omits cookie with :defer option" do
+ pool = Rack::Session::Cabinet.new(@incrementor)
+ req = Rack::MockRequest.new(pool)
+ defer = Rack::Utils::Context.new(pool, @defer_session)
+ dreq = Rack::MockRequest.new(defer)
+
+ res0 = req.get("/")
+ session = (cookie = res0["Set-Cookie"])[@session_match]
+ res0.body.should == '{"counter"=>1}'
+
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
+ res1["Set-Cookie"][@session_match].should == session
+ res1.body.should == '{"counter"=>2}'
+
+ res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
+ res2["Set-Cookie"].should be_nil
+ res2.body.should == '{"counter"=>3}'
+
+ res3 = req.get("/", "HTTP_COOKIE" => cookie)
+ res3["Set-Cookie"][@session_match].should == session
+ res3.body.should == '{"counter"=>4}'
+ end
+
+ # anyone know how to do this better?
+ specify "multithread: should cleanly merge sessions" do
+ next unless $DEBUG
+ warn 'Running multithread test for Session::Cabinet'
+ pool = Rack::Session::Cabinet.new(@incrementor)
+ req = Rack::MockRequest.new(pool)
+
+ res = req.get('/')
+ res.body.should == '{"counter"=>1}'
+ cookie = res["Set-Cookie"]
+ sess_id = cookie[/#{pool.key}=([^,;]+)/,1]
+
+ delta_incrementor = lambda do |env|
+ # emulate disconjoinment of threading
+ env['rack.session'] = env['rack.session'].dup
+ Thread.stop
+ env['rack.session'][(Time.now.usec*rand).to_i] = true
+ @incrementor.call(env)
+ end
+ tses = Rack::Utils::Context.new pool, delta_incrementor
+ treq = Rack::MockRequest.new(tses)
+ tnum = rand(7).to_i+5
+ r = Array.new(tnum) do
+ Thread.new(treq) do |run|
+ run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
+ end
+ end.reverse.map{|t| t.run.join.value }
+ r.each do |res|
+ res['Set-Cookie'].should == cookie
+ res.body.should include('"counter"=>2')
+ end
+
+ session = pool.pool.get(sess_id)
+ session.size.should == tnum+1 # counter
+ session['counter'].should == 2 # meeeh
+
+ tnum = rand(7).to_i+5
+ r = Array.new(tnum) do |i|
+ delta_time = proc do |env|
+ env['rack.session'][i] = Time.now
+ Thread.stop
+ env['rack.session'] = env['rack.session'].dup
+ env['rack.session'][i] -= Time.now
+ @incrementor.call(env)
+ end
+ app = Rack::Utils::Context.new pool, time_delta
+ req = Rack::MockRequest.new app
+ Thread.new(req) do |run|
+ run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
+ end
+ end.reverse.map{|t| t.run.join.value }
+ r.each do |res|
+ res['Set-Cookie'].should == cookie
+ res.body.should include('"counter"=>3')
+ end
+
+ session = pool.pool.get(sess_id)
+ session.size.should == tnum+1
+ session['counter'].should == 3
+
+ drop_counter = proc do |env|
+ env['rack.session'].delete 'counter'
+ env['rack.session']['foo'] = 'bar'
+ [200, {'Content-Type'=>'text/plain'}, env['rack.session'].inspect]
+ end
+ tses = Rack::Utils::Context.new pool, drop_counter
+ treq = Rack::MockRequest.new(tses)
+ tnum = rand(7).to_i+5
+ r = Array.new(tnum) do
+ Thread.new(treq) do |run|
+ run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
+ end
+ end.reverse.map{|t| t.run.join.value }
+ r.each do |res|
+ res['Set-Cookie'].should == cookie
+ res.body.should include('"foo"=>"bar"')
+ end
+
+ session = pool.pool.get(sess_id)
+ session.size.should == r.size+1
+ session['counter'].should be_nil
+ session['foo'].should == 'bar'
+ end
+
+ after(:all) do
+ Rack::Session::Cabinet.new(@incrementor).pool.clear
+ end
+ end
+ end
+end
View
249 spec/rack/session/tyrant_spec.rb
@@ -0,0 +1,249 @@
+# Code from http://github.com/jodosha/redis-store
+require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
+
+module Rack
+ module Session
+ # class Tokyo
+ # attr_reader :
+ # end
+ describe "Rack::Session::Tyrant" do
+ before(:each) do
+ @session_key = Rack::Session::Tyrant::DEFAULT_OPTIONS[:key]
+ @session_match = /#{@session_key}=[0-9a-fA-F]+;/
+ @incrementor = lambda do |env|
+ env["rack.session"]["counter"] ||= 0
+ env["rack.session"]["counter"] += 1
+ Rack::Response.new(env["rack.session"].inspect).to_a
+ end
+ @drop_session = proc do |env|
+ env['rack.session.options'][:drop] = true
+ @incrementor.call(env)
+ end
+ @renew_session = proc do |env|
+ env['rack.session.options'][:renew] = true
+ @incrementor.call(env)
+ end
+ @defer_session = proc do |env|
+ env['rack.session.options'][:defer] = true
+ @incrementor.call(env)
+ end
+ end
+
+ it "should specify connection params" do
+ pool = Rack::Session::Tyrant.new(@incrementor, :tyrant_server => "127.0.0.1:1978").pool
+ # pool.should be_kind_of(Rufus::Tokyo::Tyrant)
+ pool.host.should eql("127.0.0.1")
+ pool.port.should eql(1978)
+
+ # pool = Rack::Session::Tokyo.new(@incrementor, :tokyo_server => ["localhost:6379", "localhost:6380"]).pool
+ # pool.should be_kind_of(DistributedMarshaledTokyo)
+ end
+
+ it "should raise tokyo error on connect" do
+ lambda{ Rack::Session::Tokyo.new(@incrementor, :tyrant_server => "localhost:6380").pool }.
+ should_not raise_error(Rufus::Tokyo::TokyoError)
+ end
+
+
+ it "creates a new cookie" do
+ pool = Rack::Session::Tyrant.new(@incrementor)
+ res = Rack::MockRequest.new(pool).get("/")
+ res["Set-Cookie"].should match(/#{@session_key}=/)
+ res.body.should eql('{"counter"=>1}')
+ end
+
+ it "determines session from a cookie" do
+ pool = Rack::Session::Tyrant.new(@incrementor)
+ req = Rack::MockRequest.new(pool)
+ res = req.get("/")
+ cookie = res["Set-Cookie"]
+ req.get("/", "HTTP_COOKIE" => cookie).body.should eql('{"counter"=>2}')
+ req.get("/", "HTTP_COOKIE" => cookie).body.should eql('{"counter"=>3}')
+ end
+
+ it "survives nonexistant cookies" do
+ bad_cookie = "rack.session=blsarghfasel"
+ pool = Rack::Session::Tyrant.new(@incrementor)
+ res = Rack::MockRequest.new(pool).get("/", "HTTP_COOKIE" => bad_cookie)
+ res.body.should eql('{"counter"=>1}')
+ cookie = res["Set-Cookie"][@session_match]
+ cookie.should_not match(/#{bad_cookie}/)
+ end
+
+ # Expire isn't supported by cabinet. Implement in ruby?
+ # it "should maintain freshness" do
+ # pool = Rack::Session::Tyrant.new(@incrementor, :expire_after => 3)
+ # res = Rack::MockRequest.new(pool).get('/')
+ # res.body.should include('"counter"=>1')
+ # cookie = res["Set-Cookie"]
+ # res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie)
+ # res["Set-Cookie"].should == cookie
+ # res.body.should include('"counter"=>2')
+ # puts 'Sleeping to expire session' if $DEBUG
+ # sleep 4
+ # res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie)
+ # res["Set-Cookie"].should_not == cookie
+ # res.body.should include('"counter"=>1')
+ # end
+
+ it "deletes cookies with :drop option" do
+ pool = Rack::Session::Tyrant.new(@incrementor)
+ req = Rack::MockRequest.new(pool)
+ drop = Rack::Utils::Context.new(pool, @drop_session)
+ dreq = Rack::MockRequest.new(drop)
+
+ res0 = req.get("/")
+ session = (cookie = res0["Set-Cookie"])[@session_match]
+ res0.body.should == '{"counter"=>1}'
+
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
+ res1["Set-Cookie"][@session_match].should == session
+ res1.body.should == '{"counter"=>2}'
+
+ res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
+ res2["Set-Cookie"].should be_nil
+ res2.body.should == '{"counter"=>3}'
+
+ res3 = req.get("/", "HTTP_COOKIE" => cookie)
+ res3["Set-Cookie"][@session_match].should_not == session
+ res3.body.should == '{"counter"=>1}'
+ end
+
+ it "provides new session id with :renew option" do
+ pool = Rack::Session::Tyrant.new(@incrementor)
+ req = Rack::MockRequest.new(pool)
+ renew = Rack::Utils::Context.new(pool, @renew_session)
+ rreq = Rack::MockRequest.new(renew)
+
+ res0 = req.get("/")
+ session = (cookie = res0["Set-Cookie"])[@session_match]
+ res0.body.should == '{"counter"=>1}'
+
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
+ res1["Set-Cookie"][@session_match].should == session
+ res1.body.should == '{"counter"=>2}'
+
+ res2 = rreq.get("/", "HTTP_COOKIE" => cookie)
+ new_cookie = res2["Set-Cookie"]
+ new_session = new_cookie[@session_match]
+ new_session.should_not == session
+ res2.body.should == '{"counter"=>3}'
+
+ res3 = req.get("/", "HTTP_COOKIE" => new_cookie)
+ res3["Set-Cookie"][@session_match].should == new_session
+ res3.body.should == '{"counter"=>4}'
+ end
+
+ specify "omits cookie with :defer option" do
+ pool = Rack::Session::Tyrant.new(@incrementor)
+ req = Rack::MockRequest.new(pool)
+ defer = Rack::Utils::Context.new(pool, @defer_session)
+ dreq = Rack::MockRequest.new(defer)
+
+ res0 = req.get("/")
+ session = (cookie = res0["Set-Cookie"])[@session_match]
+ res0.body.should == '{"counter"=>1}'
+
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
+ res1["Set-Cookie"][@session_match].should == session
+ res1.body.should == '{"counter"=>2}'
+
+ res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
+ res2["Set-Cookie"].should be_nil
+ res2.body.should == '{"counter"=>3}'
+
+ res3 = req.get("/", "HTTP_COOKIE" => cookie)
+ res3["Set-Cookie"][@session_match].should == session
+ res3.body.should == '{"counter"=>4}'
+ end
+
+ # anyone know how to do this better?
+ specify "multithread: should cleanly merge sessions" do
+ next unless $DEBUG
+ warn 'Running multithread test for Session::Tyrant'
+ pool = Rack::Session::Tyrant.new(@incrementor)
+ req = Rack::MockRequest.new(pool)
+
+ res = req.get('/')
+ res.body.should == '{"counter"=>1}'
+ cookie = res["Set-Cookie"]
+ sess_id = cookie[/#{pool.key}=([^,;]+)/,1]
+
+ delta_incrementor = lambda do |env|
+ # emulate disconjoinment of threading
+ env['rack.session'] = env['rack.session'].dup
+ Thread.stop
+ env['rack.session'][(Time.now.usec*rand).to_i] = true
+ @incrementor.call(env)
+ end
+ tses = Rack::Utils::Context.new pool, delta_incrementor
+ treq = Rack::MockRequest.new(tses)
+ tnum = rand(7).to_i+5
+ r = Array.new(tnum) do
+ Thread.new(treq) do |run|
+ run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
+ end
+ end.reverse.map{|t| t.run.join.value }
+ r.each do |res|
+ res['Set-Cookie'].should == cookie
+ res.body.should include('"counter"=>2')
+ end
+
+ session = pool.pool.get(sess_id)
+ session.size.should == tnum+1 # counter
+ session['counter'].should == 2 # meeeh
+
+ tnum = rand(7).to_i+5
+ r = Array.new(tnum) do |i|
+ delta_time = proc do |env|
+ env['rack.session'][i] = Time.now
+ Thread.stop
+ env['rack.session'] = env['rack.session'].dup
+ env['rack.session'][i] -= Time.now
+ @incrementor.call(env)
+ end
+ app = Rack::Utils::Context.new pool, time_delta
+ req = Rack::MockRequest.new app
+ Thread.new(req) do |run|
+ run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
+ end
+ end.reverse.map{|t| t.run.join.value }
+ r.each do |res|
+ res['Set-Cookie'].should == cookie
+ res.body.should include('"counter"=>3')
+ end
+
+ session = pool.pool.get(sess_id)
+ session.size.should == tnum+1
+ session['counter'].should == 3
+
+ drop_counter = proc do |env|
+ env['rack.session'].delete 'counter'
+ env['rack.session']['foo'] = 'bar'
+ [200, {'Content-Type'=>'text/plain'}, env['rack.session'].inspect]
+ end
+ tses = Rack::Utils::Context.new pool, drop_counter
+ treq = Rack::MockRequest.new(tses)
+ tnum = rand(7).to_i+5
+ r = Array.new(tnum) do
+ Thread.new(treq) do |run|
+ run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
+ end
+ end.reverse.map{|t| t.run.join.value }
+ r.each do |res|
+ res['Set-Cookie'].should == cookie
+ res.body.should include('"foo"=>"bar"')
+ end
+
+ session = pool.pool.get(sess_id)
+ session.size.should == r.size+1
+ session['counter'].should be_nil
+ session['foo'].should == 'bar'
+ end
+
+ after(:all) do
+ Rack::Session::Tyrant.new(@incrementor).pool.clear
+ end
+ end
+ end
+end
View
2  spec/spec_helper.rb
@@ -2,6 +2,8 @@
%W{rubygems spec rack/cache activesupport }.each { |l| require l }
require 'tokyo_store'
require 'rack/session/tokyo'
+require 'rack/session/tyrant'
+require 'rack/session/cabinet'
require 'cache/tokyo_store'
#require 'rack/cache/tokyo'
View
8 tokyo_store.gemspec
@@ -6,7 +6,7 @@ Gem::Specification.new do |s|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Marcos Piccinini"]
- s.date = %q{2009-07-18}
+ s.date = %q{2009-07-19}
s.email = %q{x@nofxx.com}
s.extra_rdoc_files = [
"LICENSE",
@@ -24,11 +24,15 @@ Gem::Specification.new do |s|
"lib/cache/tokyo_store.rb",
"lib/rack/cache/tokyo_entitystore.rb",
"lib/rack/cache/tokyo_metastore.rb",
+ "lib/rack/session/cabinet.rb",
"lib/rack/session/tokyo.rb",
+ "lib/rack/session/tyrant.rb",
"lib/tokyo_store.rb",
"spec/cache/tokyo_store_spec.rb",
"spec/rack/cache/tokyo_spec.rb",
+ "spec/rack/session/cabinet_spec.rb",
"spec/rack/session/tokyo_spec.rb",
+ "spec/rack/session/tyrant_spec.rb",
"spec/spec.opts",
"spec/spec_helper.rb",
"spec/tokyo_store_spec.rb",
@@ -42,6 +46,8 @@ Gem::Specification.new do |s|
s.test_files = [
"spec/rack/cache/tokyo_spec.rb",
"spec/rack/session/tokyo_spec.rb",
+ "spec/rack/session/cabinet_spec.rb",
+ "spec/rack/session/tyrant_spec.rb",
"spec/tokyo_store_spec.rb",
"spec/cache/tokyo_store_spec.rb",
"spec/spec_helper.rb"
Please sign in to comment.
Something went wrong with that request. Please try again.