Class: PEROBS::Cache

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

Overview

The Cache provides two functions for the PEROBS Store. It keeps some amount of objects in memory to substantially reduce read access latencies. It also stores a list of objects that haven’t been synced to the permanent store yet to accelerate object writes.

Instance Method Summary collapse

Constructor Details

#initialize(bits = 16) ⇒ Cache

Create a new Cache object.

Parameters:

  • bits (Integer) (defaults to: 16)

    Number of bits for the cache index. This parameter heavilty affects the performance and memory consumption of the cache.



43
44
45
46
47
48
49
50
# File 'lib/perobs/Cache.rb', line 43

def initialize(bits = 16)
  @bits = bits
  # This mask is used to access the _bits_ least significant bits of the
  # object ID.
  @mask = 2 ** bits - 1
  # Initialize the read and write cache
  reset
end

Instance Method Details

#abort_transactionObject

Tell the cache to abort the currently active transaction. All modified objects will be restored from the storage back-end to their state before the transaction started.



171
172
173
174
175
176
177
178
# File 'lib/perobs/Cache.rb', line 171

def abort_transaction
  if @transaction_stack.empty?
    PEROBS.log.fatal 'No ongoing transaction to abort'
  end
  @transaction_stack.pop.each do |id|
    @transaction_objects[id]._restore(@transaction_stack.length)
  end
end

#begin_transactionObject

Tell the cache to start a new transaction. If no other transaction is active, the write cache is flushed before the transaction is started.



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/perobs/Cache.rb', line 128

def begin_transaction
  if @transaction_stack.empty?
    # The new transaction is the top-level transaction. Flush the write
    # buffer to save the current state of all objects.
    flush
  else
    # Save a copy of all objects that were modified during the enclosing
    # transaction.
    @transaction_stack.last.each do |id|
      @transaction_objects[id]._stash(@transaction_stack.length - 1)
    end
  end
  # Push a transaction buffer onto the transaction stack. This buffer will
  # hold a reference to all objects modified during this transaction.
  @transaction_stack.push(::Array.new)
end

#cache_read(obj) ⇒ Object

Add an PEROBS::Object to the read cache.

Parameters:



54
55
56
57
58
59
60
61
62
# File 'lib/perobs/Cache.rb', line 54

def cache_read(obj)
  # This is just a safety check. It can probably be disabled in the future
  # to increase performance.
  if obj.respond_to?(:is_poxreference?)
    # If this condition triggers, we have a bug in the library.
    PEROBS.log.fatal "POXReference objects should never be cached"
  end
  @reads[index(obj)] = obj
end

#cache_write(obj) ⇒ Object

Add a PEROBS::Object to the write cache.

Parameters:



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
91
92
93
94
# File 'lib/perobs/Cache.rb', line 66

def cache_write(obj)
  # This is just a safety check. It can probably be disabled in the future
  # to increase performance.
  if obj.respond_to?(:is_poxreference?)
    # If this condition triggers, we have a bug in the library.
    PEROBS.log.fatal "POXReference objects should never be cached"
  end

  if @transaction_stack.empty?
    # We are not in transaction mode.
    idx = index(obj)
    if (old_obj = @writes[idx]) && old_obj._id != obj._id
      # There is another old object using this cache slot. Before we can
      # re-use the slot, we need to sync it to the permanent storage.
      old_obj._sync
    end
    @writes[idx] = obj
  else
    # When a transaction is active, we don't have a write cache. The read
    # cache is used to speed up access to recently used objects.
    cache_read(obj)
    # Push the reference of the modified object into the write buffer for
    # this transaction level.
    unless @transaction_stack.last.include?(obj._id)
      @transaction_stack.last << obj._id
      @transaction_objects[obj._id] = obj
    end
  end
end

#end_transactionObject

Tell the cache to end the currently active transaction. All write operations of the current transaction will be synced to the storage back-end.



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/perobs/Cache.rb', line 148

def end_transaction
  case @transaction_stack.length
  when 0
    PEROBS.log.fatal 'No ongoing transaction to end'
  when 1
    # All transactions completed successfully. Write all modified objects
    # into the backend storage.
    @transaction_stack.pop.each { |id| @transaction_objects[id]._sync }
    @transaction_objects = ::Hash.new
  else
    # A nested transaction completed successfully. We add the list of
    # modified objects to the list of the enclosing transaction.
    transactions = @transaction_stack.pop
    # Merge the two lists
    @transaction_stack.push(@transaction_stack.pop + transactions)
    # Ensure that each object is only included once in the list.
    @transaction_stack.last.uniq!
  end
end

#flushObject

Flush all pending writes to the persistant storage back-end.



114
115
116
117
# File 'lib/perobs/Cache.rb', line 114

def flush
  @writes.each { |w| w._sync if w }
  @writes = ::Array.new(2 ** @bits)
end

#in_transaction?true/false

Returns true if the Cache is currently handling a transaction, false otherwise.

Returns:

  • (true/false)


122
123
124
# File 'lib/perobs/Cache.rb', line 122

def in_transaction?
  !@transaction_stack.empty?
end

#inspectObject

Don’t include the cache buffers in output of other objects that reference Cache.



194
195
# File 'lib/perobs/Cache.rb', line 194

def inspect
end

#object_by_id(id) ⇒ Object

Return the PEROBS::Object with the specified ID or nil if not found.

Parameters:

  • id (Integer)

    ID of the cached PEROBS::ObjectBase



98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/perobs/Cache.rb', line 98

def object_by_id(id)
  idx = id & @mask
  # The index is just a hash. We still need to check if the object IDs are
  # actually the same before we can return the object.
  if (obj = @writes[idx]) && obj._id == id
    # The object was in the write cache.
    return obj
  elsif (obj = @reads[idx]) && obj._id == id
    # The object was in the read cache.
    return obj
  end

  nil
end

#resetObject

Clear all cached entries. You must call flush before calling this method. Otherwise unwritten objects will be lost.



182
183
184
185
186
187
188
189
190
# File 'lib/perobs/Cache.rb', line 182

def reset
  # The read and write caches are Arrays. We use the _bits_ least
  # significant bits of the PEROBS::ObjectBase ID to select the index in
  # the read or write cache Arrays.
  @reads = ::Array.new(2 ** @bits)
  @writes = ::Array.new(2 ** @bits)
  @transaction_stack = ::Array.new
  @transaction_objects = ::Hash.new
end