Created
August 9, 2009 02:47
-
-
Save jpr5/164587 to your computer and use it in GitHub Desktop.
Sinatra / DM Scavenger Thread issue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The problem is due to ::Extlib::Pooling.scavenger lazy-creating a thread to reap the pool. Call it within the main context (i.e. DO initialization during ::DataMapper.setup) and the hang is avoided. | |
For a simple solution, see: http://github.com/jpr5/dm-core/commit/3379acf9ad12a154eabb9fcd3148a09f6fbc1a55 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
== Sinatra/0.10.1 has taken the stage on 4000 for development with backup from Mongrel | |
Sun, 09 Aug 2009 03:47:26 GMT ~ debug ~ (0.000587) SELECT "id", "session_id", "data", "updated_at" FROM "sessions" WHERE ("session_id" = 'true') ORDER BY "id" LIMIT 1 | |
Sun, 09 Aug 2009 03:47:26 GMT ~ debug ~ (0.000054) SELECT "id", "session_id", "data", "updated_at" FROM "sessions" WHERE ("session_id" = 'true') ORDER BY "id" LIMIT 1 | |
127.0.0.1 - - [08/Aug/2009 20:47:26] "GET /flows HTTP/1.1" 200 155 0.0080 | |
127.0.0.1 - - [08/Aug/2009 20:47:27] "GET /favicon.ico HTTP/1.1" 200 32 0.0014 | |
^CSat Aug 08 20:47:27 -0700 2009: Reaping 1 threads for slow workers because of 'shutdown' | |
Waiting for 1 requests to finish, could take 60.0 seconds. | |
== Sinatra has ended his set (crowd applauds) | |
^C | |
== Sinatra has ended his set (crowd applauds) | |
/Library/Ruby/Gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:341:in `graceful_shutdown': Mongrel::StopServer (Mongrel::StopServer) | |
from /Library/Ruby/Gems/1.8/gems/rack-1.0.0/lib/rack/handler/mongrel.rb:34:in `join' | |
from /Library/Ruby/Gems/1.8/gems/rack-1.0.0/lib/rack/handler/mongrel.rb:34:in `run' | |
from ./lib/../vendor/sinatra-0.10.1/lib/sinatra/base.rb:888:in `run!' | |
from app.rb:124 | |
$ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'rubygems' | |
require 'rack' | |
require 'sinatra/base' | |
require 'dm-core' | |
DataMapper::Logger.new(STDOUT, :debug) | |
DataMapper.setup(:default, "sqlite3:foo.db") | |
# This will create the pooling scavenger thread used by DO inside the main thread, | |
# avoiding the hang. See explanation below. | |
::Extlib::Pooling.scavenger | |
require 'session' | |
class App < Sinatra::Base | |
use ::Rack::Session::DataMapper, :key => '_ccsession', :cache => true | |
get "/*" do | |
"hi mom: #{params[:splat]}" | |
end | |
end | |
App.run! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Updated to add generate_sid method into Store. | |
require 'rack/session/abstract/id' | |
require 'dm-core' | |
module Rack | |
module Session | |
class DataMapper < ::Rack::Session::Abstract::ID | |
class Store | |
def initialize(app, options) | |
@mutex = Mutex.new | |
@sidbits = options[:sidbits] || ::Rack::Session::Abstract::ID::DEFAULT_OPTIONS[:sidbits] | |
if options.delete(:cache) | |
@@cache = {} | |
elsif not self.class.class_variable_defined? :@@cache | |
@@cache = nil | |
end | |
unless self.class.class_variable_defined? :@@session_class and @@session_class | |
@@session_class = options.delete(:session_class) || Session | |
end | |
end | |
def generate_sid | |
"%0#{@sidbits / 4}x" % rand(2**@sidbits - 1) | |
end | |
def get_session(env, sid) | |
@mutex.lock if env['rack.multithread'] | |
sid ||= generate_sid | |
session = @@cache && @@cache[sid] || @@session_class.first(:session_id => sid) | |
[sid, session.nil? ? {} : session.data] | |
ensure | |
@mutex.unlock if env['rack.multithread'] | |
end | |
def set_session(env, sid, session_data, options) | |
@mutex.lock if env['rack.multithread'] | |
if options[:renew] | |
@@session_class.all(:session_id => sid).destroy! | |
@@cache.delete(sid) if @@cache | |
sid = generate_sid | |
end | |
session = @@cache && @@cache[sid] || @@session_class.first_or_create(:session_id => sid) | |
session.data = session_data | |
session.updated_at = Time.now if session.dirty? | |
@@cache[sid] = session if @@cache | |
if $VERBOSE and session.new? | |
env['rack.errors'].puts("created new session #{sid}") | |
end | |
# docs say this should return true/false, but | |
# commit_session in abstract relies on the id | |
# being returned. | |
session.save ? sid : false | |
ensure | |
@mutex.unlock if env['rack.multithread'] | |
end | |
end | |
# Use Text instead of Object for data because loading an | |
# Object property automatically sets the model to dirty | |
# (DM 0.9.11). | |
class Session | |
include ::DataMapper::Resource | |
def self.name | |
"session" | |
end | |
property :id, Serial | |
property :session_id, String, :unique_index => true | |
property :data, Text, :nullable => false, :lazy => false, :auto_validation => false, | |
:default => ::Base64.encode64(Marshal.dump({})) | |
property :updated_at, DateTime, :nullable => true, :index => true | |
def data=(data) | |
attribute_set(:data, ::Base64.encode64(Marshal.dump(data))) | |
end | |
def data | |
Marshal.load(::Base64.decode64(attribute_get(:data))) | |
end | |
end | |
# use Rack::Session::DataMapper, :opt1 => :foo, :opt2 => :bar | |
def initialize(app, options) | |
super | |
@store = Store.new(app, options) | |
end | |
def get_session(env, sid) | |
@store.get_session(env, sid) | |
end | |
def set_session(env, sid, session_data, options) | |
@store.set_session(env, sid, session_data, options) | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment