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 (Fixnum) (defaults to: 16)

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



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

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.



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

def abort_transaction
  if @transaction_stack.empty?
    raise RuntimeError, '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 cached is flushed before the transaction is started.



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

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:



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

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.
    raise RuntimeError, "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:



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

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.
    raise RuntimeError, "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.



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

def end_transaction
  case @transaction_stack.length
  when 0
    raise RuntimeError, '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.



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

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)


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

def in_transaction?
  !@transaction_stack.empty?
end

#inspectObject

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



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

def inspect
end

#resetObject

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



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

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