Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Refactor LRUCache

Remove need for Item object.
  • Loading branch information...
commit 2e38ca1efc4b672fb2cf3317510afbb710735d14 1 parent 088abdd
Michael Neumann authored
6 lib/wee/application.rb
View
@@ -47,7 +47,7 @@ def call(env)
request = Wee::Request.new(env)
if request.session_id
- session = @mutex.synchronize { @sessions[request.session_id] }
+ session = @mutex.synchronize { @sessions.fetch(request.session_id) }
if session and session.alive?
session.call(env)
else
@@ -74,8 +74,8 @@ def insert_session(session, retries=3)
retries.times do
@mutex.synchronize {
id = @session_ids.next
- if @sessions[id].nil?
- @sessions[id] = session
+ if not @sessions.has_key?(id)
+ @sessions.store(id, session)
session.id = id
return
end
88 lib/wee/lru_cache.rb
View
@@ -4,21 +4,16 @@ module Wee
# Implementation of a Least Recently Used (LRU) Cache
#
class LRUCache
- class Item
- attr_accessor :value, :time
- def initialize(value=nil, time=nil)
- @value, @time = value, time
- end
-
- def <=>(other)
- @time <=> other.time
- end
+ #
+ # Common interface for all items
+ #
+ module Item
+ attr_accessor :lru_time
end
- def initialize(capacity=20, &replace_callback)
+ def initialize(capacity=20)
@capacity = capacity
- @replace_callback = replace_callback
@store = Hash.new
@time = 0
end
@@ -31,62 +26,63 @@ def delete(key)
@store.delete(key)
end
- def delete_if
- @store.delete_if {|id, item|
- yield id, item.value
- }
+ def delete_if(&block)
+ @store.delete_if(&block)
end
def fetch(key, default_value=nil)
if item = @store[key]
- item.time = (@time += 1)
- item.value
+ touch(item)
+ item
else
default_value
end
end
- def store(key, value)
- if item = @store[key]
- # update item only
- item.time = (@time += 1)
- item.value = value
- else
- # insert new item
- item = Item.new
- item.time = (@time += 1)
- item.value = value
- garbage_collect() if @store.size >= @capacity
- while @store.size >= @capacity
- old_item = @store.delete(min_key()) || raise
- @replace_callback.call(old_item) if @replace_callback
- end
- @store[key] = item
- end
+ def store(key, item)
+ touch(item)
+ compact()
+ @store[key] = item
end
- def garbage_collect
+ protected
+
+ #
+ # Is called whenever an item is looked up or stored to update it's
+ # timestamp to maintain least recently used information.
+ #
+ def touch(item)
+ item.lru_time = (@time += 1)
end
- def each(&block)
- @store.each(&block)
+ #
+ # Is called for each item that is replaced from cache. Overwrite.
+ #
+ def purge(item)
end
- alias [] fetch
- alias []= store
+ #
+ # Is called before replacing old items in order to remove items
+ # known-to-be no longer in use. Overwrite.
+ #
+ def garbage_collect
+ end
- protected
+ #
+ # Replaces old items and makes place for new.
+ #
+ def compact
+ garbage_collect() if @store.size >= @capacity
+ while @store.size >= @capacity
+ purge(@store.delete(min_key()) || raise)
+ end
+ end
#
# Returns the key of the minimum item
#
def min_key
- min_k, min_time = nil, @time
- @store.each {|k, v|
- if v.time < min_time
- min_k, min_time = k, v.time
- end
- }
+ min_k, _ = @store.min_by {|_, item| item.lru_time}
return min_k
end
9 lib/wee/session.rb
View
@@ -7,6 +7,8 @@ module Wee
class Session
+ include LRUCache::Item
+
#
# The default serializer, when no continuations are going to be used.
# Ensures that only one request of the same session is executed at
@@ -47,6 +49,7 @@ def call(env)
class Page
attr_accessor :id, :state, :callbacks
+ include LRUCache::Item
def initialize(id=nil, state=nil, callbacks=nil)
@id, @state, @callbacks = id, state, callbacks
end
@@ -241,7 +244,7 @@ def handle(env)
#
@initial_state ||= take_snapshot()
new_page = Page.new(@page_ids.next, @initial_state, nil)
- @page_cache[new_page.id] = new_page
+ @page_cache.store(new_page.id, new_page)
url = request.build_url(:page_id => new_page.id)
if request.page_id
@@ -330,7 +333,7 @@ def action(request, page)
#
@current_page = page
page.state = take_snapshot()
- @page_cache[page.id] = page
+ @page_cache.store(page.id, page)
return abort.response
else
# pass on - this is a premature response from Component#call
@@ -342,7 +345,7 @@ def action(request, page)
# create new page (state)
#
new_page = Page.new(@page_ids.next, take_snapshot(), nil)
- @page_cache[new_page.id] = new_page
+ @page_cache.store(new_page.id, new_page)
@current_page = new_page
url = request.build_url(:page_id => new_page.id)
51 test/test_lru_cache.rb
View
@@ -0,0 +1,51 @@
+require 'test/unit'
+require 'wee/lru_cache'
+
+class I
+ include Wee::LRUCache::Item
+
+ attr_accessor :id
+
+ def initialize(id)
+ @id = id
+ end
+end
+
+class Test_LRUCache < Test::Unit::TestCase
+ def test_replacement
+ cache = Wee::LRUCache.new(2)
+ def cache.purge(item) @purged = item end
+ def cache.purged() @purged end
+
+ a = I.new("a")
+ b = I.new("b")
+ c = I.new("c")
+
+ assert_nil cache.purged
+
+ cache.store(a.id, a)
+ assert_nil cache.purged
+
+ cache.store(b.id, b)
+ assert_nil cache.purged
+
+ cache.store(c.id, c)
+ assert_same a, cache.purged
+
+ cache.store(a.id, a)
+ assert_same b, cache.purged
+
+ cache.store(b.id, b)
+ assert_same c, cache.purged
+
+ #
+ # Reads also modify LRU
+ #
+ assert_same a, cache.fetch(a.id)
+ assert_same b, cache.fetch(b.id)
+ assert_same a, cache.fetch(a.id)
+
+ cache.store(c.id, c)
+ assert_same b, cache.purged
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.