Class: Session
- Inherits:
-
Object
- Object
- Session
- Defined in:
- backend/app/model/session.rb
Constant Summary collapse
- SESSION_ID_LENGTH =
32
- UPDATE_FREQUENCY_SECONDS =
If it’s worth doing it’s worth overdoing!
For really small AJAX-driven lookups, like nodes and waypoints, sometimes touching the user’s session (with its associated database commit) was adding 5-10x to the response time. Upsetting!
Since touching sessions is very common, but not really mission critical, offload the work to a background thread that will periodically update them.
5
Instance Attribute Summary collapse
-
#id ⇒ Object
readonly
Returns the value of attribute id.
-
#system_mtime ⇒ Object
readonly
Returns the value of attribute system_mtime.
Class Method Summary collapse
-
.expire(sid) ⇒ Object
-
.expire_old_sessions ⇒ Object
-
.find(sid) ⇒ Object
-
.init ⇒ Object
-
.touch_pending_sessions(now = Time.now) ⇒ Object
-
.touch_session(id) ⇒ Object
Instance Method Summary collapse
-
#[](key) ⇒ Object
-
#[]=(key, val) ⇒ Object
-
#age ⇒ Object
-
#initialize(sid = nil, store = nil, system_mtime = nil) ⇒ Session
constructor
A new instance of Session.
-
#save ⇒ Object
-
#touch ⇒ Object
Constructor Details
#initialize(sid = nil, store = nil, system_mtime = nil) ⇒ Session
Returns a new instance of Session.
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'backend/app/model/session.rb', line 58 def initialize(sid = nil, store = nil, system_mtime = nil) now = Time.now if not sid # Create a new session in the DB DB.open do |db| while true sid = SecureRandom.hex(SESSION_ID_LENGTH) completed = DB.attempt { db[:session].insert(:session_id => Digest::SHA1.hexdigest(sid), :session_data => [Marshal.dump({})].pack("m*"), :system_mtime => now) true }.and_if_constraint_fails { # Retry with a different session ID. false } break if completed end @id = sid @system_mtime = now @store = {} end else @id = sid @store = store @system_mtime = system_mtime end end |
Instance Attribute Details
#id ⇒ Object (readonly)
Returns the value of attribute id
56 57 58 |
# File 'backend/app/model/session.rb', line 56 def id @id end |
#system_mtime ⇒ Object (readonly)
Returns the value of attribute system_mtime
56 57 58 |
# File 'backend/app/model/session.rb', line 56 def system_mtime @system_mtime end |
Class Method Details
.expire(sid) ⇒ Object
109 110 111 112 113 |
# File 'backend/app/model/session.rb', line 109 def self.expire(sid) DB.open do |db| db[:session].filter(:session_id => Digest::SHA1.hexdigest(sid)).delete end end |
.expire_old_sessions ⇒ Object
116 117 118 119 120 121 122 123 124 |
# File 'backend/app/model/session.rb', line 116 def self.expire_old_sessions max_expirable_age = AppConfig[:session_expire_after_seconds] || (7 * 24 * 60 * 60) max_nonexpirable_age = AppConfig[:session_nonexpirable_force_expire_after_seconds] || (7 * 24 * 60 * 60) DB.open do |db| db[:session].where {system_mtime < (Time.now - max_expirable_age)}.filter(:expirable => 1).delete db[:session].where {system_mtime < (Time.now - max_nonexpirable_age)}.filter(:expirable => 0).delete end end |
.find(sid) ⇒ Object
93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'backend/app/model/session.rb', line 93 def self.find(sid) DB.open do |db| row = db[:session] .filter(:session_id => Digest::SHA1.hexdigest(sid)) .select(:session_data, :system_mtime) .first if row Session.new(sid, Marshal.load(row[:session_data].unpack("m*").first), row[:system_mtime]) else nil end end end |
.init ⇒ Object
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'backend/app/model/session.rb', line 19 def self.init @sessions_to_update = Queue.new @session_touch_thread = Thread.new do while true begin self.touch_pending_sessions rescue Log.exception($!) end sleep UPDATE_FREQUENCY_SECONDS end end end |
.touch_pending_sessions(now = Time.now) ⇒ Object
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'backend/app/model/session.rb', line 35 def self.touch_pending_sessions(now = Time.now) sessions = [] while !@sessions_to_update.empty? sessions << @sessions_to_update.pop end unless sessions.empty? DB.open do |db| db[:session] .filter(:session_id => sessions.map {|id| Digest::SHA1.hexdigest(id) }.uniq) .update(:system_mtime => now) end end end |
.touch_session(id) ⇒ Object
51 52 53 |
# File 'backend/app/model/session.rb', line 51 def self.touch_session(id) @sessions_to_update << id end |
Instance Method Details
#[](key) ⇒ Object
132 133 134 |
# File 'backend/app/model/session.rb', line 132 def [](key) return @store[key] end |
#[]=(key, val) ⇒ Object
127 128 129 |
# File 'backend/app/model/session.rb', line 127 def []=(key, val) @store[key] = val end |
#age ⇒ Object
153 154 155 |
# File 'backend/app/model/session.rb', line 153 def age (Time.now - system_mtime).to_i end |
#save ⇒ Object
137 138 139 140 141 142 143 144 145 |
# File 'backend/app/model/session.rb', line 137 def save DB.open do |db| db[:session] .filter(:session_id => Digest::SHA1.hexdigest(@id)) .update(:session_data => [Marshal.dump(@store)].pack("m*"), :expirable => @store[:expirable] ? 1 : 0, :system_mtime => Time.now) end end |
#touch ⇒ Object
148 149 150 |
# File 'backend/app/model/session.rb', line 148 def touch self.class.touch_session(@id) end |