class Prometheus::Client::DataStores::DirectFileStore::FileMappedDict

A dict of doubles, backed by an file we access directly a a byte array.

The file starts with a 4 byte int, indicating how much of it is used. Then 4 bytes of padding. There's then a number of entries, consisting of a 4 byte int which is the size of the next field, a utf-8 encoded string key, padding to an 8 byte alignment, and then a 8 byte float which is the value.

Constants

INITIAL_FILE_SIZE

Attributes

capacity[R]
positions[R]
used[R]

Public Class Methods

new(filename, readonly = false) click to toggle source
# File lib/prometheus/client/data_stores/direct_file_store.rb, line 213
def initialize(filename, readonly = false)
  @positions = {}
  @used = 0

  open_file(filename, readonly)
  @used = @f.read(4).unpack('l')[0] if @capacity > 0

  if @used > 0
    # File already has data. Read the existing values
    with_file_lock { populate_positions }
  else
    # File is empty. Init the `used` counter, if we're in write mode
    if !readonly
      @used = 8
      @f.seek(0)
      @f.write([@used].pack('l'))
    end
  end
end

Public Instance Methods

all_values() click to toggle source

Return a list of key-value pairs

# File lib/prometheus/client/data_stores/direct_file_store.rb, line 234
def all_values
  with_file_lock do
    @positions.map do |key, pos|
      @f.seek(pos)
      value = @f.read(8).unpack('d')[0]
      [key, value]
    end
  end
end
close() click to toggle source
# File lib/prometheus/client/data_stores/direct_file_store.rb, line 265
def close
  @f.close
end
read_value(key) click to toggle source
# File lib/prometheus/client/data_stores/direct_file_store.rb, line 244
def read_value(key)
  if !@positions.has_key?(key)
    init_value(key)
  end

  pos = @positions[key]
  @f.seek(pos)
  @f.read(8).unpack('d')[0]
end
with_file_lock() { || ... } click to toggle source
# File lib/prometheus/client/data_stores/direct_file_store.rb, line 269
def with_file_lock
  @f.flock(File::LOCK_EX)
  yield
ensure
  @f.flock(File::LOCK_UN)
end
write_value(key, value) click to toggle source
# File lib/prometheus/client/data_stores/direct_file_store.rb, line 254
def write_value(key, value)
  if !@positions.has_key?(key)
    init_value(key)
  end

  pos = @positions[key]
  @f.seek(pos)
  @f.write([value].pack('d'))
  @f.flush
end

Private Instance Methods

init_value(key) click to toggle source

Initialize a value. Lock must be held by caller.

# File lib/prometheus/client/data_stores/direct_file_store.rb, line 299
def init_value(key)
  # Pad to be 8-byte aligned.
  padded = key + (' ' * (8 - (key.length + 4) % 8))
  value = [padded.length, padded, 0.0].pack("lA#{padded.length}d")
  while @used + value.length > @capacity
    @capacity *= 2
    resize_file(@capacity)
  end
  @f.seek(@used)
  @f.write(value)
  @used += value.length
  @f.seek(0)
  @f.write([@used].pack('l'))
  @f.flush
  @positions[key] = @used - 8
end
open_file(filename, readonly) click to toggle source
# File lib/prometheus/client/data_stores/direct_file_store.rb, line 278
def open_file(filename, readonly)
  mode = if readonly
           "r"
         elsif File.exist?(filename)
           "r+b"
         else
           "w+b"
         end

  @f = File.open(filename, mode)
  if @f.size == 0 && !readonly
    resize_file(INITIAL_FILE_SIZE)
  end
  @capacity = @f.size
end
populate_positions() click to toggle source

Read position of all keys. No locking is performed.

# File lib/prometheus/client/data_stores/direct_file_store.rb, line 317
def populate_positions
  @f.seek(8)
  while @f.pos < @used
    padded_len = @f.read(4).unpack('l')[0]
    key = @f.read(padded_len).unpack("A#{padded_len}")[0].strip
    @positions[key] = @f.pos
    @f.seek(8, :CUR)
  end
end
resize_file(new_capacity) click to toggle source
# File lib/prometheus/client/data_stores/direct_file_store.rb, line 294
def resize_file(new_capacity)
  @f.truncate(new_capacity)
end