Class: PEROBS::SpaceTreeNodeCache

Inherits:
Object
  • Object
show all
Defined in:
lib/perobs/SpaceTreeNodeCache.rb

Instance Method Summary collapse

Constructor Details

#initialize(tree, size) ⇒ SpaceTreeNodeCache

Simple cache that can hold up to size SpaceTreeNode entries. Entries are hashed with a simple node_address % size function. This keeps the overhead for managing the cache extremely low yet giving an OK probability to have cache hits. The cache also keeps track if a node is still in memory or needs to be reloaded from the file. All node accesses must always go through this cache to avoid having duplicate in-memory nodes for the same on-disk node.

Parameters:

  • size (Integer)

    maximum number of cache entries



42
43
44
45
46
# File 'lib/perobs/SpaceTreeNodeCache.rb', line 42

def initialize(tree, size)
  @tree = tree
  @size = size
  clear
end

Instance Method Details

#_collect(address) ⇒ Object

Remove a node from the in-memory list. This is an internal method and should never be called from user code. It will be called from a finalizer, so many restrictions apply!

Parameters:

  • node_address (Integer)

    Node address of the node to remove from the list



128
129
130
# File 'lib/perobs/SpaceTreeNodeCache.rb', line 128

def _collect(address)
  @in_memory_nodes.delete(address)
end

#clearObject

Remove all entries from the cache.



139
140
141
142
143
144
# File 'lib/perobs/SpaceTreeNodeCache.rb', line 139

def clear
  # A hash that stores all objects by ID that are currently in memory.
  @in_memory_nodes = {}
  @unmodified_nodess = ::Array.new(@size)
  @modified_nodes = ::Array.new(@size)
end

#delete(address) ⇒ Object

Remove a node from the cache.

Parameters:

  • address (Integer)

    address of node to remove.



108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/perobs/SpaceTreeNodeCache.rb', line 108

def delete(address)
  # The object is likely still in memory, but we really don't want to
  # access it anymore.
  @in_memory_nodes.delete(address)

  index = address % @size
  if (node = @unmodified_nodess[index]) && node.node_address == address
    @unmodified_nodess[index] = nil
  end

  if (node = @modified_nodes[index]) && node.node_address == address
    @modified_nodes[index] = nil
  end
end

#flushObject

Write all modified objects into the backing store.



133
134
135
136
# File 'lib/perobs/SpaceTreeNodeCache.rb', line 133

def flush
  @modified_nodes.each { |node| node.save if node }
  @modified_nodes = ::Array.new(@size)
end

#get(address) ⇒ Object

Retrieve a node reference from the cache.

Parameters:

  • address (Integer)

    address of the node to retrieve.



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/perobs/SpaceTreeNodeCache.rb', line 75

def get(address)
  node = @unmodified_nodess[address % @size]
  # We can have collisions. Check if the cached node really matches the
  # requested address.
  return node if node && node.node_address == address

  node = @modified_nodes[address % @size]
  # We can have collisions. Check if the cached node really matches the
  # requested address.
  return node if node && node.node_address == address

  if (obj_id = @in_memory_nodes[address])
    # We have the node in memory so we can just return it.
    begin
      node = ObjectSpace._id2ref(obj_id)
      unless node.node_address == address
        raise RuntimeError, "In memory list is corrupted"
      end
      insert_unmodified(node)
      return node
    rescue RangeError
      # Due to a race condition the object can still be in the
      # @in_memory_nodes list but has been collected already by the Ruby
      # GC. In that case we need to load it again.
      @in_memory_nodes.delete(address)
    end
  end

  SpaceTreeNode::load(@tree, address)
end

#insert_modified(node) ⇒ Object

Insert a modified node into the cache.

Parameters:

  • node (SpaceTreeNode)

    Modified SpaceTreeNode

  • address (Integer)

    Address of the node in the file



58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/perobs/SpaceTreeNodeCache.rb', line 58

def insert_modified(node)
  @in_memory_nodes[node.node_address] = node.object_id
  index = node.node_address % @size
  if (old_node = @modified_nodes[index])
    # If the object is already in the modified object list, we don't have
    # to do anything.
    return if old_node.node_address == node.node_address
    # If the new object will replace an existing entry in the cash we have
    # to save the object first.
    old_node.save
  end

  @modified_nodes[index] = node
end

#insert_unmodified(node) ⇒ Object

Insert an unmodified node into the cache.

Parameters:



50
51
52
53
# File 'lib/perobs/SpaceTreeNodeCache.rb', line 50

def insert_unmodified(node)
  @in_memory_nodes[node.node_address] = node.object_id
  @unmodified_nodess[node.node_address % @size] = node
end