class LargeObjectStore::RailsWrapper

Attributes

store[R]

Public Class Methods

new(store) click to toggle source
# File lib/large_object_store.rb, line 24
def initialize(store)
  @store = store
end

Public Instance Methods

delete(key) click to toggle source
# File lib/large_object_store.rb, line 98
def delete(key)
  @store.delete(key(key, 0))
end
exist?(key) click to toggle source
# File lib/large_object_store.rb, line 94
def exist?(key)
  @store.exist?(key(key, 0))
end
fetch(key, options={}) { || ... } click to toggle source
# File lib/large_object_store.rb, line 86
def fetch(key, options={})
  value = read(key)
  return value unless value.nil?
  value = yield
  write(key, value, options)
  value
end
read(key) click to toggle source
# File lib/large_object_store.rb, line 59
def read(key)
  # read pages
  pages, uuid = @store.read(key(key, 0))
  return if pages.nil?

  data = if pages.is_a?(Integer)
    # read sliced data
    keys = (1..pages).map { |i| key(key, i) }
    # use values_at to enforce key order because read_multi doesn't guarantee a return order
    slices = @store.read_multi(*keys).values_at(*keys)
    return nil if slices.compact.size != pages

    slices = slices.map do |slice|
      s = slice.dup
      [s.slice!(0, UUID_SIZE), s]
    end

    return nil unless slices.map(&:first).uniq == [uuid]

    slices.map!(&:last).join("")
  else
    pages
  end

  deserialize(data)
end
write(key, value, options = {}) click to toggle source
# File lib/large_object_store.rb, line 28
def write(key, value, options = {})
  options = options.dup
  value = serialize(value, options)

  # calculate slice size; note that key length is a factor because
  # the key is stored on the same slab page as the value
  slice_size = MAX_OBJECT_SIZE - ITEM_HEADER_SIZE - UUID_SIZE - key.bytesize

  # store number of pages
  pages = (value.size / slice_size.to_f).ceil

  if pages == 1
    !!@store.write(key(key, 0), value, options)
  else
    # store meta
    uuid = SecureRandom.hex(UUID_BYTES)
    return false unless @store.write(key(key, 0), [pages, uuid], options) # invalidates the old cache

    # store object
    page = 1
    loop do
      slice = value.slice!(0, slice_size)
      break if slice.size == 0

      return false unless @store.write(key(key, page), slice.prepend(uuid), options.merge(raw: true))
      page += 1
    end
    true
  end
end

Private Instance Methods

compress?(value, options) click to toggle source

Don't pass compression on to Rails, we're doing it ourselves.

# File lib/large_object_store.rb, line 134
def compress?(value, options)
  return unless options.delete(:compress)
  compress_limit = options.delete(:compress_limit) || DEFAULT_COMPRESS_LIMIT
  value.bytesize > compress_limit
end
deserialize(raw_data) click to toggle source

opposite operations and order of serialize

# File lib/large_object_store.rb, line 125
def deserialize(raw_data)
  data = raw_data.dup
  flag = data.slice!(0, 1).to_i(FLAG_RADIX)
  data = Zlib::Inflate.inflate(data) if flag & COMPRESSED == COMPRESSED
  data = Marshal.load(data) if flag & RAW != RAW
  data
end
key(key, i) click to toggle source
# File lib/large_object_store.rb, line 140
def key(key, i)
  "#{key}_#{CACHE_VERSION}_#{i}"
end
serialize(value, options) click to toggle source

convert a object to a string modifies options

# File lib/large_object_store.rb, line 106
def serialize(value, options)
  flag = NORMAL

  if options.delete(:raw)
    flag |= RAW
    value = value.to_s
  else
    value = Marshal.dump(value)
  end

  if compress?(value, options)
    flag |= COMPRESSED
    value = Zlib::Deflate.deflate(value)
  end

  value.prepend(flag.to_s(FLAG_RADIX))
end