require 'prometheus/client/helper/mmaped_file' require 'prometheus/client/helper/plain_file' require 'prometheus/client' module Prometheus module Client class ParsingError < StandardError end # A dict of doubles, backed by an mmapped file. # # 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. class MmapedDict MINIMUM_SIZE = 8 attr_reader :m, :used, :positions def self.read_all_values(f) Helper::PlainFile.new(f).entries.map do |data, encoded_len, value_offset, _| encoded, value = data.unpack(format('@4A%d@%dd', encoded_len, value_offset)) [encoded, value] end end def initialize(m) @mutex = Mutex.new @m = m # @m.mlock # TODO: Ensure memory is locked to RAM @used = @m.used @positions = {} read_all_positions.each do |key, pos| @positions[key] = pos end rescue StandardError => e raise ParsingError, "exception #{e} while processing metrics file #{path}" end def read_value(key) @mutex.synchronize do init_value(key) unless @positions.key?(key) end pos = @positions[key] # We assume that reading from an 8 byte aligned value is atomic. @m[pos..pos + 7].unpack('d')[0] end def write_value(key, value) @mutex.synchronize do init_value(key) unless @positions.key?(key) end pos = @positions[key] # We assume that writing to an 8 byte aligned value is atomic. @m[pos..pos + 7] = [value].pack('d') end def path @m.filepath unless @m.nil? end def close @m.sync @m.close rescue TypeError => e Prometheus::Client.logger.warn("munmap raised error #{e}") end private # Initialize a value. Lock must be held by caller. def init_value(key) @m.add_entry(key, 0.0) # Update how much space we've used. @used = @m.used @positions[key] = @used - 8 end # Yield (key, pos). No locking is performed. def read_all_positions @m.entries.map do |data, encoded_len, _, absolute_pos| encoded, = data.unpack(format('@4A%d', encoded_len)) [encoded, absolute_pos] end end end end end