class CacheManager

This class manages the cache. It is initialized by the Store and front ends the database behind the Store.

Public Class Methods

new(db) click to toggle source
# File lib/storage/cache.rb, line 112
def initialize(db)
  @cwidth = options['cache_width']
  @cdepth = options['cache_depth']

  @db =  db
  @st = CacheStats.new

  @cache = Array.new(@cwidth) {Array.new}
  @cwidth.times do |i|
    @cdepth.times do |j|
      @cache[i] << CacheEntry.new
      @st.inc(:cache_size)
    end
  end

end

Public Instance Methods

delete(oid) click to toggle source

delete an object

# File lib/storage/cache.rb, line 254
def delete(oid)
  return nil if oid.nil?
  @st.inc(:deletes)
  hv = hash(oid)

  # mark dead in cache

  @cache[hv].each do |ce|
    next if ce.oid != oid
    ce.mark_dead
    break
  end
  # delete it from the database

  @db.delete(oid.to_s)
end
get(oid) click to toggle source

gets an object

oid
  • integer object id to retrieve

return
  • a reference to the object or nil if none exists

# File lib/storage/cache.rb, line 151
def get(oid)
  return nil if oid.nil?
  @st.inc(:reads)
  hv = hash(oid)

  # search the cache

  @cache[hv].each do |ce|
    if ce.oid == oid
      @st.inc(:cache_read_hits)
      # need to try putting hot hit to the head of the list?

      return ce.obj
    end
  end

  # cache miss - search the database

  if @db.has_key? oid.to_s
    ret = Utility.decode(@db[oid.to_s])
  else
    @st.inc(:database_read_fails)
    return nil
  end
  @st.inc(:database_reads)

  # get and remove the last entry off this list

  ch = @cache[hv].pop
  # if its dirty we write it to the database

  if ch.dirty? && !ch.dead?
    @db[ch.oid.to_s] = Utility.encode(ch.obj)
    @st.inc(:database_writes)
    if ch.noswap?  # here we have a problem we can't use this

      # first push it back onto the list

      @cache[hv].unshift ch
      # get ourselves a brand new

      ch = CacheEntry.new
      # problem solved

      # the depth of any list chains will be cache_depth + # noswap entries

    end
  end

  # assign our new object to the cache entry

  ch.obj = ret
  ch.oid = oid
  ch.clean!
  # push it to the head of the list

  @cache[hv].unshift ch

  ret
end
hash(oid) click to toggle source

Our simple hash algoritm. Our database keys are sequentially assigned integers, so… I don’t know what would be better.

# File lib/storage/cache.rb, line 144
def hash(oid)
  oid % @cwidth
end
inspect() click to toggle source

report on the cache map see cmd_dumpcache

# File lib/storage/cache.rb, line 130
def inspect
  str = "cache width:#{@cwidth} cache depth:#{@cdepth}\n"
  @cwidth.times do |i|
    @cache[i].each_with_index do |ce,j|
      next if ce.dead?
      str << "cache map [#{i}][#{j}] => "
      str << "oid #{ce.oid} object_id #{ce.obj.object_id} dirty? #{ce.dirty?}  noswap? #{ce.noswap?}\n"
    end
  end
  str
end
keys() click to toggle source

This is provided to traverse the entire object chain. This thrashes the cache violently. See Store#each. One needs to question the design of routines that use Store#each above.

# File lib/storage/cache.rb, line 359
def keys
  kys = []
  @cwidth.times do |i|
    @cache[i].each {|ce| kys << ce.oid if !ce.oid.nil? }
  end
  kys
end
makenoswap(oid) click to toggle source

Mark an object in the cache as nonswappable

oid

is the object id

return

undefined

# File lib/storage/cache.rb, line 319
def makenoswap(oid)
  return nil if oid.nil?
  @st.inc(:cache_noswap)
  hv = hash(oid)

  @cache[hv].each do |ce|
    next if ce.oid != oid
    ce.noswap!
    return
  end

  # This would indicate we've tried marking it noswap before we did a put.

  # A logic error

  @st.inc(:cache_noswap_misses)
  log.debug "Marking object nonswappable before put - #{oid}"
end
makeswap(oid) click to toggle source

Mark an object in the cache as swappable

oid

is the object id

return

undefined

# File lib/storage/cache.rb, line 339
def makeswap(oid)
  return nil if oid.nil?
  @st.inc(:cache_swap)
  hv = hash(oid)

  @cache[hv].each do |ce|
    next if ce.oid != oid
    ce.swap!
    return
  end

  # This would indicate we've tried marking it swap before we did a put.

  # A logic error

  @st.inc(:cache_swap_misses)
  log.debug "Marking object swappable before put - #{oid}"
end
mark(oid) click to toggle source

deliberately mark an object in the cache as dirty see properties.

# File lib/storage/cache.rb, line 271
def mark(oid)
  return nil if oid.nil?
  @st.inc(:cache_marks)
  hv = hash(oid)

  @cache[hv].each do |ce|
    next if ce.oid != oid
    ce.dirty!
    return
  end

  # this would indicate we've tried marking it dirty before we did a put.

  @st.inc(:cache_mark_misses)
  log.debug "Marking object dirty before put - #{oid}"
end
put(obj) click to toggle source

puts an object

obj
  • The integer object id to retrieve

return
  • undefined

# File lib/storage/cache.rb, line 203
def put(obj)
  return nil if obj.nil?
  @st.inc(:writes)
  hv = hash(obj.id)

  # search the cache

  @cache[hv].each do |ce|
    next if ce.oid != obj.id
    @st.inc(:cache_write_hits) if ce.dirty?
    if obj.object_id != ce.obj.object_id  # be safe

      # Be extra careful here.  It's possible we could have two objects

      # with the same object id but with different Ruby object_ids.

      # Most likely this is a bug, but we should handle it here..

      ce.obj = obj
      log.warn "Duplicate object id's in cache!"
      log.warn "insert - #{obj.inspect}"
      log.warn "cache - #{obj.inspect}"
    end
    # need to try putting hot hit to the head of the list?

    ce.dirty!
    return
  end

  # get and remove the last entry off this list

  ch = @cache[hv].pop
  # if its dirty we write it to the database

  if ch.dirty? && !ch.dead?
    # errors possible - check in store module

    @db[ch.oid.to_s] = Utility.encode(ch.obj)
    @st.inc(:database_writes)
    if ch.noswap?  # here we have a problem we can't use this

      # first push it back onto the list

      @cache[hv].unshift ch
      # get ourselves a brand new

      ch = CacheEntry.new
      # problem solved

      # the depth of any list chains will be cache_depth + # noswap entries

    end
  end

  # assign our new object to the cache entry

  ch.obj = obj
  ch.oid = obj.id
  ch.dirty!
  # push it to the head of the list

  @cache[hv].unshift ch

  return
end
stats() click to toggle source

produce a report - see cmd_stats

# File lib/storage/cache.rb, line 368
def stats
  str =  "----------* Cache Statistics *----------\n"
  @st.each do |k,v|
    str << sprintf("%25-25s : %d\n",k.to_s.gsub(%r_/,' '),v)
  end
  str << "----------*                  *----------\n"
end
sync() click to toggle source

syncronize the entire cache with the database called by save.

# File lib/storage/cache.rb, line 289
def sync
  @st.inc(:cache_syncs)
  # search the list chains for dirty objects and write them out.

  @cwidth.times do |i|
    @cache[i].each do |ce|
      if ce.dirty?
        @db[ce.oid.to_s] = Utility.encode(ce.obj)
        @st.inc(:database_writes)
        ce.clean!
      end
    end
  end
end
sync_chain(i) click to toggle source

syncronize a specific list chain not yet called but included for possible performance enhancement

# File lib/storage/cache.rb, line 305
def sync_chain(i)
  @st.inc(:chain_syncs)
  @cache[i].each do |ce|
    if ce.dirty?
      @db[ce.oid.to_s] = Utility.encode(ce.obj)
      @st.inc(:database_writes)
      ce.clean!
    end
  end
end