Module: Archipelago::Hashish::CachedHashish

Includes:
Current::Synchronized
Included in:
BerkeleyHashish, DumpHashish
Defined in:
lib/archipelago/hashish.rb

Overview

In essence a persistence backed Hash (with certain BTree behaviours).

Will cache everything including timestamps for last modification in normal Hashes, but keep everything stored in a persistency backer defined by the subclass.

Instance Method Summary collapse

Methods included from Current::Synchronized

#lock_on, #mon_check_owner, #synchronize_on, #unlock_on

Instance Method Details

#[](key) ⇒ Object

Simply get the value for the key.

Will call value.load_hook and send it a block that does the actuall insertion of the value into the live hash if value.respond_to?(:load_hook) if the value didnt exist in the live hash yet.



121
122
123
124
125
126
127
128
129
130
# File 'lib/archipelago/hashish.rb', line 121

def [](key)
  synchronize_on(key) do

    value = @content[key]
    return value if value
    
    return get_from_db(key)
 
  end
end

#[]=(key, value) ⇒ Object

Insert value under key.

Will call value.save_hook(old_value) and send it a block that does the actual saving if value.respond_to?(:save_hook).



146
147
148
149
150
151
152
153
154
155
156
# File 'lib/archipelago/hashish.rb', line 146

def []=(key, value)
  synchronize_on(key, @unlocked_thread != Thread.current) do

    @content[key] = value

    write_to_db(key, Marshal.dump(key), Marshal.dump(value), value)

    return value

  end
end

#changed?(key, value) ⇒ Boolean

Returns true if what key points to is no longer value.

Returns:

  • (Boolean)


41
42
43
# File 'lib/archipelago/hashish.rb', line 41

def changed?(key, value)
  raise "You have to implement me!"
end

#close!Object

Close the persistent backers behind this CachedHashish.



63
64
65
# File 'lib/archipelago/hashish.rb', line 63

def close!
  raise "You have to implement me!"
end

#db_include?Boolean

Returns whether the persistent backer contains key.

Returns:

  • (Boolean)


69
70
71
# File 'lib/archipelago/hashish.rb', line 69

def db_include?
  raise "You have to implement me!"
end

#delete(key) ⇒ Object

Delete key and its value and timestamp.



209
210
211
212
213
214
215
216
217
218
# File 'lib/archipelago/hashish.rb', line 209

def delete(key)
  synchronize_on(key, @unlocked_thread != Thread.current) do

    serialized_key = Marshal.dump(key)

    @content.delete(key)
    @timestamps.delete(key)
    do_delete_from_persistence(serialized_key)
  end
end

#do_delete_from_persistence(serialized_key) ⇒ Object

Actually deletes serialized_key from the persistent backer.



81
82
83
# File 'lib/archipelago/hashish.rb', line 81

def do_delete_from_persistence(serialized_key)
  raise "You have to implement me!"
end

#do_get_from_db(serialized_key) ⇒ Object

Will perform an actual fetching of serialized_key from the persistent backer.



75
76
77
# File 'lib/archipelago/hashish.rb', line 75

def do_get_from_db(serialized_key)
  raise "You have to implement me!"
end

#do_get_timestamp_from_db(serialized_key) ⇒ Object

Get the serialized timestamp for serialized_key from the persistent backer.



47
48
49
# File 'lib/archipelago/hashish.rb', line 47

def do_get_timestamp_from_db(serialized_key)
  raise "You have to implement me!"
end

#do_write_to_db(key, serialized_key, serialized_value, timestamp) ⇒ Object

Actually writes key serialized as serialized_key an serialized_value to the db, and timestamp to the timestamp db.

Used by write_to_db.



57
58
59
# File 'lib/archipelago/hashish.rb', line 57

def do_write_to_db(key, serialized_key, serialized_value, timestamp)
  raise "You have to implement me!"
end

#each(callable) ⇒ Object

Will do callable.call(key, value) for each key-and-value pair in this Hashish.

NB: This is totaly thread-unsafe, only do this for management or rescue!



91
92
93
# File 'lib/archipelago/hashish.rb', line 91

def each(callable)
  raise "You have to implement me!"
end

#firstObject

Get the first key/value pair from our cache tree.



97
98
99
# File 'lib/archipelago/hashish.rb', line 97

def first
  @content.first
end

#forget(key) ⇒ Object

Forget the given key in the cache tree.



109
110
111
112
# File 'lib/archipelago/hashish.rb', line 109

def forget(key)
  @content.delete(key)
  @timestamps.delete(key)
end

#get_deep_clone(key) ⇒ Object

Returns a deep ( Marshal.load(Marshal.dump(o)) ) clone of the object represented by key.



195
196
197
# File 'lib/archipelago/hashish.rb', line 195

def get_deep_clone(key)
  return Marshal.load(Marshal.dump(@content[key]))
end

#get_from_db(key) ⇒ Object

Read key from db and if it is found put it in the cache Hash.

Will call value.load_hook and send it a block that does the actuall insertion of the value into the live hash if value.respond_to?(:load_hook).



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/archipelago/hashish.rb', line 227

def get_from_db(key)
  serialized_key = Marshal.dump(key)
  serialized_value = do_get_from_db(serialized_key)
  return nil unless serialized_value
  
  value = Marshal.load(serialized_value)
  if value.respond_to?(:load_hook)
    value.load_hook do
      @content[key] = value
    end
  else
    @content[key] = value
  end
  return value
end

#include?(key) ⇒ Boolean

Returns true if this BerkeleyHashish include key.

Returns:

  • (Boolean)


245
246
247
# File 'lib/archipelago/hashish.rb', line 245

def include?(key)
  @content.include?(key) || db_include?(key)
end

#initialize_cached_hashishObject

Initializes the cache hashes for this CachedHashish.



201
202
203
204
205
# File 'lib/archipelago/hashish.rb', line 201

def initialize_cached_hashish
  @content = RBTree.new
  @timestamps = {}
  @unlocked_thread = nil
end

#lastObject

Get the last key/value pair from our cache tree.



103
104
105
# File 'lib/archipelago/hashish.rb', line 103

def last
  @content.last
end

#store_if_changed(key) ⇒ Object

Stores whatever is under key if it is not the same as whats in the persistent db - if the key actually has a representation in the db.

Will call value.save_hook(old_value) and send it a block that does the actual saving if value.respond_to?(:save_hook) and a save is actually performed.



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/archipelago/hashish.rb', line 167

def store_if_changed(key)
  synchronize_on(key) do

    value = @content[key]

    if value.respond_to?(:dirty?)

      if value.dirty?
        serialized_value = Marshal.dump(value)
        serialized_key = Marshal.dump(key)
        write_to_db(key, serialized_key, serialized_value, value)
        value.is_clean! if value.respond_to?(:is_clean!)
      end
    
    else

      serialized_value = Marshal.dump(value)
      serialized_key = Marshal.dump(key)
      write_to_db(key, serialized_key, serialized_value, value) if changed?(serialized_key, serialized_value)
      
    end

  end
end

#timestamp(key) ⇒ Object

Returns the last time the value under key was changed.



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/archipelago/hashish.rb', line 251

def timestamp(key)
  synchronize_on(key) do
    
    timestamp = @timestamps[key]
    return timestamp if timestamp
    
    serialized_key = Marshal.dump(key)
    serialized_timestamp = do_get_timestamp_from_db(serialized_key)
    return nil unless serialized_timestamp
    
    timestamp = Marshal.load(serialized_timestamp)
    @timestamps[key] = timestamp
    return timestamp

  end
end

#without_lock(&block) ⇒ Object



131
132
133
134
135
136
137
138
# File 'lib/archipelago/hashish.rb', line 131

def without_lock(&block)
  @unlocked_thread = Thread.current
  begin
    yield
  ensure
    @unlocked_thread = nil
  end
end

#write_to_db(key, serialized_key, serialized_value, value) ⇒ Object

Write key, serialized as serialized_key and serialized_value to the db.

Will call value.save_hook(old_value) and send it a block that does the actual saving if value.respond_to?(:save_hook).



275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/archipelago/hashish.rb', line 275

def write_to_db(key, serialized_key, serialized_value, value)
  if value.respond_to?(:save_hook)
    old_serialized_value = do_get_from_db(serialized_key)
    old_value = old_serialized_value ? Marshal.load(old_serialized_value) : nil
    value.save_hook(old_value) do
      now = Time.now
      do_write_to_db(key, serialized_key, serialized_value, now)
      @timestamps[key] = now
    end
  else
    now = Time.now
    do_write_to_db(key, serialized_key, serialized_value, now)
    @timestamps[key] = now
  end
end